tag 2.0.8
git-svn-id: http://code.alibabatech.com/svn/dubbo/tags/2.0.8@768 1a56cb94-b969-4eaa-88fa-be21384802f2
diff --git a/COPYRIGHT b/COPYRIGHT
new file mode 100644
index 0000000..7644b41
--- /dev/null
+++ b/COPYRIGHT
@@ -0,0 +1,13 @@
+Copyright 1999-2011 Alibaba Group.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT 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..15c1e54
--- /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-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.
diff --git a/dubbo-cluster/pom.xml b/dubbo-cluster/pom.xml
new file mode 100644
index 0000000..575a374
--- /dev/null
+++ b/dubbo-cluster/pom.xml
@@ -0,0 +1,44 @@
+<!--
+ - Copyright 1999-2011 Alibaba Group.
+ -
+ - Licensed under the Apache License, Version 2.0 (the "License");
+ - you may not use this file except in compliance with the License.
+ - You may obtain a copy of the License at
+ -
+ - http://www.apache.org/licenses/LICENSE-2.0
+ -
+ - Unless required by applicable law or agreed to in writing, software
+ - distributed under the License is distributed on an "AS IS" BASIS,
+ - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ - See the License for the specific language governing permissions and
+ - limitations under the License.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>com.alibaba</groupId>
+ <artifactId>dubbo-parent</artifactId>
+ <version>2.0.8</version>
+ </parent>
+ <artifactId>dubbo-cluster</artifactId>
+ <packaging>jar</packaging>
+ <name>Dubbo Cluster Module</name>
+ <description>The cluster module of dubbo project</description>
+ <properties>
+ <skip_maven_deploy>false</skip_maven_deploy>
+ </properties>
+ <dependencies>
+ <dependency>
+ <groupId>com.alibaba</groupId>
+ <artifactId>dubbo-rpc</artifactId>
+ <version>${project.parent.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.bsf</groupId>
+ <artifactId>bsf-api</artifactId>
+ <scope>provided</scope>
+ <optional>true</optional>
+ </dependency>
+ </dependencies>
+</project>
\ No newline at end of file
diff --git a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/Cluster.java b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/Cluster.java
new file mode 100644
index 0000000..b39ecea
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/Cluster.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.cluster;
+
+import com.alibaba.dubbo.common.Adaptive;
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.RpcException;
+import com.alibaba.dubbo.rpc.cluster.support.FailoverCluster;
+
+/**
+ * Cluster. (SPI, Singleton, ThreadSafe)
+ *
+ * <a href="http://en.wikipedia.org/wiki/Computer_cluster">Cluster</a>
+ * <a href="http://en.wikipedia.org/wiki/Fault-tolerant_system">Fault-Tolerant</a>
+ *
+ * @author william.liangf
+ */
+@Extension(FailoverCluster.NAME)
+public interface Cluster {
+
+ /**
+ * Merge the directory invokers to a virtual invoker.
+ *
+ * @param <T>
+ * @param directory
+ * @return
+ * @throws RpcException
+ */
+ @Adaptive
+ <T> Invoker<T> merge(Directory<T> directory) throws RpcException;
+
+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/Directory.java b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/Directory.java
new file mode 100644
index 0000000..10bacd8
--- /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, Singleton, ThreadSafe)
+ *
+ * <a href="http://en.wikipedia.org/wiki/Directory_service">Directory Service</a>
+ *
+ * @see com.alibaba.dubbo.rpc.cluster.Cluster#merge(Directory)
+ * @author william.liangf
+ */
+public interface Directory<T> extends Node {
+
+ /**
+ * get service type.
+ *
+ * @return service type.
+ */
+ Class<T> getInterface();
+
+ /**
+ * list invokers.
+ *
+ * @return invokers
+ */
+ List<Invoker<T>> list(Invocation invocation) throws RpcException;
+
+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/LoadBalance.java b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/LoadBalance.java
new file mode 100644
index 0000000..603cfe7
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/LoadBalance.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.cluster;
+
+import java.util.List;
+
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.RpcException;
+import com.alibaba.dubbo.rpc.cluster.loadbalance.RandomLoadBalance;
+
+/**
+ * LoadBalance. (SPI, Singleton, ThreadSafe)
+ *
+ * <a href="http://en.wikipedia.org/wiki/Load_balancing_(computing)">Load-Balancing</a>
+ *
+ * @see com.alibaba.dubbo.rpc.cluster.Cluster#merge(Directory)
+ * @author qian.lei
+ * @author william.liangf
+ */
+@Extension(RandomLoadBalance.NAME)
+public interface LoadBalance {
+
+ /**
+ * select one invoker in list.
+ *
+ * @param invokers invokers.
+ * @param invocation invocation.
+ * @return selected invoker.
+ */
+ <T> Invoker<T> select(List<Invoker<T>> invokers, Invocation invocation) throws RpcException;
+
+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/Router.java b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/Router.java
new file mode 100644
index 0000000..1a5392c
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/Router.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;
+
+import java.util.List;
+
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.RpcException;
+
+/**
+ * Router.
+ *
+ * @author chao.liuc
+ */
+public interface Router {
+
+ /**
+ * route.
+ *
+ * @param invokers
+ * @param invocation
+ * @return
+ * @throws RpcException
+ */
+ <T> List<Invoker<T>> route(List<Invoker<T>> invokers, Invocation invocation) throws RpcException;
+
+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/RouterFactory.java b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/RouterFactory.java
new file mode 100644
index 0000000..7ab9d66
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/RouterFactory.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.rpc.cluster;
+
+import com.alibaba.dubbo.common.Adaptive;
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.common.URL;
+
+/**
+ * RouterFactory. (SPI, Singleton, ThreadSafe)
+ *
+ * @author chao.liuc
+ */
+@Extension
+public interface RouterFactory {
+
+ /**
+ * Create router.
+ *
+ * @param url
+ * @return
+ */
+ @Adaptive("protocol")
+ Router getRouter(URL url);
+
+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/directory/StaticDirectory.java b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/directory/StaticDirectory.java
new file mode 100644
index 0000000..fa459f4
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/directory/StaticDirectory.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.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;
+import com.alibaba.dubbo.rpc.cluster.support.AbstractDirectory;
+
+/**
+ * StaticDirectory
+ *
+ * @author william.liangf
+ */
+public class StaticDirectory<T> extends AbstractDirectory<T> {
+
+ private final List<Invoker<T>> invokers;
+
+ public StaticDirectory(List<Invoker<T>> invokers){
+ this(null, invokers, null);
+ }
+
+ public StaticDirectory(List<Invoker<T>> invokers, List<Router> routers){
+ this(null, invokers, routers);
+ }
+
+ public StaticDirectory(URL url, List<Invoker<T>> invokers) {
+ this(url, invokers, null);
+ }
+
+ public StaticDirectory(URL url, List<Invoker<T>> invokers, List<Router> routers) {
+ super(url == null && invokers != null && invokers.size() > 0 ? invokers.get(0).getUrl() : url, routers);
+ if (invokers == null || invokers.size() == 0)
+ throw new IllegalArgumentException("invokers == null");
+ this.invokers = invokers;
+ }
+
+ public Class<T> getInterface() {
+ return invokers.get(0).getInterface();
+ }
+
+ public boolean isAvailable() {
+ if (destroyed) return false;
+ for (Invoker<T> invoker : invokers) {
+ if (invoker.isAvailable()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public void destroy() {
+ if(destroyed) {
+ return;
+ }
+ super.destroy();
+ for (Invoker<T> invoker : invokers) {
+ invoker.destroy();
+ }
+ invokers.clear();
+ }
+
+ @Override
+ protected List<Invoker<T>> doList(Invocation invocation) throws RpcException {
+
+ return invokers;
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/loadbalance/AbstractLoadBalance.java b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/loadbalance/AbstractLoadBalance.java
new file mode 100644
index 0000000..a1d9b13
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/loadbalance/AbstractLoadBalance.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.cluster.loadbalance;
+
+import java.util.List;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.cluster.LoadBalance;
+
+/**
+ * AbstractLoadBalance
+ *
+ * @author william.liangf
+ */
+public abstract class AbstractLoadBalance implements LoadBalance {
+
+ public <T> Invoker<T> select(List<Invoker<T>> invokers, Invocation invocation) {
+ if (invokers == null || invokers.size() == 0)
+ return null;
+ if (invokers.size() == 1)
+ return invokers.get(0);
+ return doSelect(invokers, invocation);
+ }
+
+ protected abstract <T> Invoker<T> doSelect(List<Invoker<T>> invokers, Invocation invocation);
+
+ protected int getWeight(Invoker<?> invoker, Invocation invocation) {
+ return invoker.getUrl().getMethodParameter(invocation.getMethodName(), Constants.WEIGHT_KEY, Constants.DEFAULT_WEIGHT);
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/loadbalance/LeastActiveLoadBalance.java b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/loadbalance/LeastActiveLoadBalance.java
new file mode 100644
index 0000000..6123071
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/loadbalance/LeastActiveLoadBalance.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.cluster.loadbalance;
+
+import java.util.List;
+import java.util.Random;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.RpcStatus;
+import com.alibaba.dubbo.rpc.Invocation;
+
+/**
+ * LeastActiveLoadBalance
+ *
+ * @author william.liangf
+ */
+@Extension(LeastActiveLoadBalance.NAME)
+public class LeastActiveLoadBalance extends AbstractLoadBalance {
+ public static final String NAME = "leastactive";
+
+ private final Random random = new Random();
+
+ protected <T> Invoker<T> doSelect(List<Invoker<T>> invokers, Invocation invocation) {
+ int length = invokers.size(); // 总个数
+ int leastActive = -1; // 最小的活跃数
+ int leastCount = 0; // 相同最小活跃数的个数
+ int[] leastIndexs = new int[length]; // 相同最小活跃数的下标
+ int totalWeight = 0; // 总权重
+ int firstWeight = 0; // 第一个权重,用于于计算是否相同
+ boolean sameWeight = true; // 是否所有权重相同
+ for (int i = 0; i < length; i++) {
+ Invoker<T> invoker = invokers.get(i);
+ int active = RpcStatus.getStatus(invoker.getUrl(), invocation.getMethodName()).getActive(); // 活跃数
+ int weight = invoker.getUrl().getMethodParameter(invocation.getMethodName(), Constants.WEIGHT_KEY, Constants.DEFAULT_WEIGHT); // 权重
+ if (leastActive == -1 || active < leastActive) { // 发现更小的活跃数,重新开始
+ leastActive = active; // 记录最小活跃数
+ leastCount = 1; // 重新统计相同最小活跃数的个数
+ leastIndexs[0] = i; // 重新记录最小活跃数下标
+ totalWeight = weight; // 重新累计总权重
+ firstWeight = weight; // 记录第一个权重
+ sameWeight = true; // 还原权重相同标识
+ } else if (active == leastActive) { // 累计相同最小的活跃数
+ leastIndexs[leastCount ++] = i; // 累计相同最小活跃数下标
+ totalWeight += weight; // 累计总权重
+ // 判断所有权重是否一样
+ if (sameWeight && i > 0
+ && weight != firstWeight) {
+ sameWeight = false;
+ }
+ }
+ }
+ // assert(leastCount > 0)
+ if (leastCount == 1) {
+ // 如果只有一个最小则直接返回
+ return invokers.get(leastIndexs[0]);
+ }
+ if (! sameWeight && totalWeight > 0) {
+ // 如果权重不相同且权重大于0则按总权重数随机
+ int offsetWeight = random.nextInt(totalWeight);
+ // 并确定随机值落在哪个片断上
+ for (int i = 0; i < leastCount; i++) {
+ int leastIndex = leastIndexs[i];
+ offsetWeight -= getWeight(invokers.get(leastIndex), invocation);
+ if (offsetWeight <= 0)
+ return invokers.get(leastIndex);
+ }
+ }
+ // 如果权重相同或权重为0则均等随机
+ return invokers.get(leastIndexs[random.nextInt(leastCount)]);
+ }
+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/loadbalance/LoadBalanceAdptive.java b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/loadbalance/LoadBalanceAdptive.java
new file mode 100644
index 0000000..42f6ab7
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/loadbalance/LoadBalanceAdptive.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.cluster.loadbalance;
+
+import java.util.List;
+
+import com.alibaba.dubbo.common.Adaptive;
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.ExtensionLoader;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.RpcException;
+import com.alibaba.dubbo.rpc.cluster.LoadBalance;
+
+/**
+ * LoadBalanceAdptive
+ *
+ * @author ding.lid
+ * @author william.liangf
+ */
+@Adaptive
+public class LoadBalanceAdptive implements LoadBalance {
+ public <T> Invoker<T> select(List<Invoker<T>> invokers, Invocation invocation) throws RpcException {
+ if (invokers == null || invokers.size() == 0) {
+ return null;
+ }
+ URL url = invokers.get(0).getUrl();
+ String method = invocation.getMethodName();
+ String name;
+ if (method == null || method.length() == 0) {
+ name = url.getParameter(Constants.LOADBALANCE_KEY, Constants.DEFAULT_LOADBALANCE);
+ } else {
+ name = url.getMethodParameter(method, Constants.LOADBALANCE_KEY, Constants.DEFAULT_LOADBALANCE);
+ }
+ LoadBalance loadbalance = ExtensionLoader.getExtensionLoader(LoadBalance.class).getExtension(name);
+ return loadbalance.select(invokers, invocation);
+ }
+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/loadbalance/RandomLoadBalance.java b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/loadbalance/RandomLoadBalance.java
new file mode 100644
index 0000000..a86c5f7
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/loadbalance/RandomLoadBalance.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.cluster.loadbalance;
+
+import java.util.List;
+import java.util.Random;
+
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.Invocation;
+
+/**
+ * random load balance.
+ *
+ * @author qianlei
+ * @author william.liangf
+ */
+@Extension(RandomLoadBalance.NAME)
+public class RandomLoadBalance extends AbstractLoadBalance {
+ public static final String NAME = "random";
+
+ private final Random random = new Random();
+
+ protected <T> Invoker<T> doSelect(List<Invoker<T>> invokers, Invocation invocation) {
+ int length = invokers.size(); // 总个数
+ int totalWeight = 0; // 总权重
+ boolean sameWeight = true; // 权重是否都一样
+ for (int i = 0; i < length; i++) {
+ // 获取权重
+ int weight = getWeight(invokers.get(i), invocation);
+ // 累计总权重
+ totalWeight += weight;
+ // 判断所有权重是否一样
+ if (sameWeight && i > 0
+ && weight != getWeight(invokers.get(i - 1), invocation)) {
+ sameWeight = false;
+ }
+ }
+ if (!sameWeight && totalWeight > 0) {
+ // 如果权重不相同且权重大于0则按总权重数随机
+ int offset = random.nextInt(totalWeight);
+ // 并确定随机值落在哪个片断上
+ for (int i = 0; i < length; i++) {
+ offset -= getWeight(invokers.get(i), invocation);
+ if (offset < 0) {
+ return invokers.get(i);
+ }
+ }
+ }
+ // 如果权重相同或权重为0则均等随机
+ return invokers.get(random.nextInt(length));
+ }
+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/loadbalance/RoundRobinLoadBalance.java b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/loadbalance/RoundRobinLoadBalance.java
new file mode 100644
index 0000000..a8aaec6
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/loadbalance/RoundRobinLoadBalance.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.cluster.loadbalance;
+
+import java.util.List;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.common.utils.AtomicPositiveInteger;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.Invocation;
+
+/**
+ * Round robin load balance.
+ *
+ * @author qian.lei
+ * @author william.liangf
+ */
+@Extension(RoundRobinLoadBalance.NAME)
+public class RoundRobinLoadBalance extends AbstractLoadBalance {
+ public static final String NAME = "roundrobin";
+
+ private final ConcurrentMap<String, AtomicPositiveInteger> sequences = new ConcurrentHashMap<String, AtomicPositiveInteger>();
+
+ protected <T> Invoker<T> doSelect(List<Invoker<T>> invokers, Invocation invocation) {
+ String key = invokers.get(0).getInterface().getName() + "." + invocation.getMethodName();// + System.identityHashCode(invokers);
+ AtomicPositiveInteger sequence = sequences.get(key);
+ if (sequence == null) {
+ sequences.putIfAbsent(key, new AtomicPositiveInteger());
+ sequence = sequences.get(key);
+ }
+ // 取模轮循
+ return invokers.get(sequence.getAndIncrement() % invokers.size());
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/router/FileRouterFactory.java b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/router/FileRouterFactory.java
new file mode 100644
index 0000000..aee5f54
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/router/FileRouterFactory.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.cluster.router;
+
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.utils.IOUtils;
+import com.alibaba.dubbo.rpc.RpcConstants;
+import com.alibaba.dubbo.rpc.cluster.Router;
+import com.alibaba.dubbo.rpc.cluster.RouterFactory;
+
+@Extension(FileRouterFactory.NAME)
+public class FileRouterFactory implements RouterFactory {
+
+ public static final String NAME = "file";
+
+ private RouterFactory routerFactory;
+
+ public void setRouterFactory(RouterFactory routerFactory) {
+ this.routerFactory = routerFactory;
+ }
+
+ public Router getRouter(URL url) {
+ try {
+ // File URL 转换成 其它Route URL,然后Load
+ // file:///d:/path/to/route.js?router=script ==> script:///d:/path/to/route.js?type=js&rule=<file-content>
+ String protocol = url.getParameter(RpcConstants.ROUTER_KEY, ScriptRouterFactory.NAME); // 将原类型转为协议
+ String type = null; // 使用文件后缀做为类型
+ String path = url.getPath();
+ if (path != null) {
+ int i = path.lastIndexOf('.');
+ if (i > 0) {
+ type = path.substring(i + 1);
+ }
+ }
+ String rule = IOUtils.read(new FileReader(new File(url.getAbsolutePath())));
+ URL script = url.setProtocol(protocol).addParameter(RpcConstants.TYPE_KEY, type).addParameterAndEncoded(RpcConstants.RULE_KEY, rule);
+
+ return routerFactory.getRouter(script);
+ } catch (IOException e) {
+ throw new IllegalStateException(e.getMessage(), e);
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/router/ScriptRouter.java b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/router/ScriptRouter.java
new file mode 100644
index 0000000..4f887ba
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/router/ScriptRouter.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.cluster.router;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import javax.script.Bindings;
+import javax.script.Compilable;
+import javax.script.CompiledScript;
+import javax.script.ScriptEngine;
+import javax.script.ScriptEngineManager;
+import javax.script.ScriptException;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.logger.Logger;
+import com.alibaba.dubbo.common.logger.LoggerFactory;
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.RpcConstants;
+import com.alibaba.dubbo.rpc.RpcContext;
+import com.alibaba.dubbo.rpc.RpcException;
+import com.alibaba.dubbo.rpc.cluster.Router;
+
+/**
+ * ScriptRouter
+ *
+ * @author william.liangf
+ */
+public class ScriptRouter implements Router {
+
+ private static final Logger logger = LoggerFactory.getLogger(ScriptRouter.class);
+
+ private static final Map<String, ScriptEngine> engines = new ConcurrentHashMap<String, ScriptEngine>();
+
+ private final ScriptEngine engine;
+
+ private final String rule;
+
+ public ScriptRouter(URL url) {
+ String type = url.getParameter(RpcConstants.TYPE_KEY);
+ String rule = url.getParameterAndDecoded(RpcConstants.RULE_KEY);
+ if (type == null || type.length() == 0){
+ type = RpcConstants.DEFAULT_SCRIPT_TYPE_KEY;
+ }
+ if (rule == null || rule.length() == 0){
+ throw new IllegalStateException(new IllegalStateException("route rule can not be empty. rule:" + rule));
+ }
+ ScriptEngine engine = engines.get(type);
+ if (engine == null){
+ engine = new ScriptEngineManager().getEngineByName(type);
+ if (engine == null) {
+ throw new IllegalStateException(new IllegalStateException("Unsupported route rule type: " + type + ", rule: " + rule));
+ }
+ engines.put(type, engine);
+ }
+ this.engine = engine;
+ this.rule = rule;
+ }
+
+ @SuppressWarnings("unchecked")
+ public <T> List<Invoker<T>> route(List<Invoker<T>> invokers, Invocation invocation) throws RpcException {
+ try {
+ List<Invoker<T>> invokersCopy = new ArrayList<Invoker<T>>(invokers);
+ Compilable compilable = (Compilable) engine;
+ Bindings bindings = engine.createBindings();
+ bindings.put("invokers", invokersCopy);
+ bindings.put("invocation", invocation);
+ bindings.put("context", RpcContext.getContext());
+ CompiledScript function = compilable.compile(rule);
+ Object obj = function.eval(bindings);
+ if (obj instanceof Invoker[]) {
+ invokersCopy = Arrays.asList((Invoker<T>[]) obj);
+ } else if (obj instanceof Object[]) {
+ invokersCopy = new ArrayList<Invoker<T>>();
+ for (Object inv : (Object[]) obj) {
+ invokersCopy.add((Invoker<T>)inv);
+ }
+ } else {
+ invokersCopy = (List<Invoker<T>>) obj;
+ }
+ return invokersCopy;
+ } catch (ScriptException e) {
+ //fail then ignore rule .invokers.
+ logger.error("route error , rule has been ignored .rule :"+ rule + ",invocation:" + invocation + ",url :"+(RpcContext.getContext().getInvoker() == null ? "" : RpcContext.getContext().getInvoker().getUrl()), e);
+ return invokers;
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/router/ScriptRouterFactory.java b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/router/ScriptRouterFactory.java
new file mode 100644
index 0000000..43edc39
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/router/ScriptRouterFactory.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.cluster.router;
+
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.rpc.cluster.Router;
+import com.alibaba.dubbo.rpc.cluster.RouterFactory;
+
+/**
+ * ScriptRouterFactory
+ *
+ * Script Router Factory用到的URL形如:
+ * <ol>
+ * <li> script://registyAddress?type=js&rule=xxxx
+ * <li> script:///path/to/routerfile.js?type=js&rule=xxxx
+ * <li> script://D:\path\to\routerfile.js?type=js&rule=xxxx
+ * <li> script://C:/path/to/routerfile.js?type=js&rule=xxxx
+ * </ol>
+ * URL的Host一段包含的是Script Router内容的来源,Registry、File etc
+ *
+ * @author william.liangf
+ */
+@Extension(ScriptRouterFactory.NAME)
+public class ScriptRouterFactory implements RouterFactory {
+
+ public static final String NAME = "script";
+
+ public Router getRouter(URL url) {
+ return new ScriptRouter(url);
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/AbstractClusterInvoker.java b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/AbstractClusterInvoker.java
new file mode 100644
index 0000000..9e1303b
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/AbstractClusterInvoker.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.rpc.cluster.support;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.ExtensionLoader;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.Version;
+import com.alibaba.dubbo.common.logger.Logger;
+import com.alibaba.dubbo.common.logger.LoggerFactory;
+import com.alibaba.dubbo.common.utils.NetUtils;
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.Result;
+import com.alibaba.dubbo.rpc.RpcConstants;
+import com.alibaba.dubbo.rpc.RpcException;
+import com.alibaba.dubbo.rpc.cluster.Directory;
+import com.alibaba.dubbo.rpc.cluster.LoadBalance;
+
+/**
+ * AbstractRpcRouter
+ *
+ * @author william.liangf
+ * @author chao.liuc
+ */
+public abstract class AbstractClusterInvoker<T> implements Invoker<T> {
+
+ private static final Logger logger = LoggerFactory
+ .getLogger(AbstractClusterInvoker.class);
+ protected final Directory<T> directory;
+
+ protected final boolean availablecheck;
+
+ private volatile boolean destroyed = false;
+
+ private volatile Invoker<T> stickyInvoker = null;
+
+
+ public AbstractClusterInvoker(Directory<T> directory) {
+ this(directory, directory.getUrl());
+ }
+ public AbstractClusterInvoker(Directory<T> directory, URL url) {
+ if (directory == null)
+ throw new IllegalArgumentException("service directory == null");
+
+ this.directory = directory ;
+ this.availablecheck = url.getParameter(RpcConstants.CLUSTER_AVAILABLE_CHECK_KEY, RpcConstants.DEFAULT_CLUSTER_AVAILABLE_CHECK) ;
+ }
+
+ public Class<T> getInterface() {
+ return directory.getInterface();
+ }
+
+ public URL getUrl() {
+ return directory.getUrl();
+ }
+
+ public boolean isAvailable() {
+ Invoker<T> invoker = stickyInvoker;
+ if (invoker != null) {
+ return invoker.isAvailable();
+ }
+ return directory.isAvailable();
+ }
+
+ public void destroy() {
+ directory.destroy();
+ destroyed = true;
+ }
+
+ /**
+ * 使用loadbalance选择invoker.</br>
+ * a)先lb选择,如果在selected列表中 或者 不可用且做检验时,进入下一步(重选),否则直接返回</br>
+ * b)重选验证规则:selected > available .保证重选出的结果尽量不在select中,并且是可用的
+ *
+ * @param availablecheck 如果设置true,在选择的时候先选invoker.available == true
+ * @param selected 已选过的invoker.注意:输入保证不重复
+ *
+ */
+ protected Invoker<T> select(LoadBalance loadbalance, Invocation invocation, List<Invoker<T>> invokers, List<Invoker<T>> selected) throws RpcException {
+ if (invokers == null || invokers.size() == 0)
+ return null;
+ String methodName = invocation == null ? "" : invocation.getMethodName();
+
+ boolean sticky = invokers.get(0).getUrl().getMethodParameter(methodName,RpcConstants.CLUSTER_STICKY_KEY, RpcConstants.DEFAULT_CLUSTER_STICKY) ;
+ {
+ //ignore overloaded method
+ if ( stickyInvoker != null && !invokers.contains(stickyInvoker) ){
+ stickyInvoker = null;
+ }
+ //ignore cucurrent problem
+ if (sticky && stickyInvoker != null && (selected == null || !selected.contains(stickyInvoker))){
+ if (availablecheck && stickyInvoker.isAvailable()){
+ return stickyInvoker;
+ }
+ }
+ }
+ Invoker<T> invoker = doselect(loadbalance, invocation, invokers, selected);
+
+ if (sticky){
+ stickyInvoker = invoker;
+ }
+ return invoker;
+ }
+
+ private Invoker<T> doselect(LoadBalance loadbalance, Invocation invocation, List<Invoker<T>> invokers, List<Invoker<T>> selected) throws RpcException {
+ if (invokers == null || invokers.size() == 0)
+ return null;
+ if (invokers.size() == 1)
+ return invokers.get(0);
+ // 如果只有两个invoker,退化成轮循
+ if (invokers.size() == 2 && selected != null && selected.size() > 0) {
+ return selected.get(0) == invokers.get(0) ? invokers.get(1) : invokers.get(0);
+ }
+ Invoker<T> invoker = loadbalance.select(invokers, invocation);
+
+ //如果 selected中包含(优先判断) 或者 不可用&&availablecheck=true 则重试.
+ if( (selected != null && selected.contains(invoker))
+ ||(!invoker.isAvailable() && getUrl()!=null && availablecheck)){
+ try{
+ Invoker<T> rinvoker = reselect(loadbalance, invocation, invokers, selected, availablecheck);
+ if(rinvoker != null){
+ invoker = rinvoker;
+ }else{
+ //看下第一次选的位置,如果不是最后,选+1位置.
+ int index = invokers.indexOf(invoker);
+ try{
+ //最后在避免碰撞
+ invoker = index <invokers.size()-1?invokers.get(index+1) :invoker;
+ }catch (Exception e) {
+ logger.warn(e.getMessage()+" may because invokers list dynamic change, ignore.",e);
+ }
+ }
+ }catch (Throwable t){
+ logger.error("clustor relselect fail reason is :"+t.getMessage() +" if can not slove ,you can set cluster.availablecheck=false in url",t);
+ }
+ }
+ return invoker;
+ }
+
+ /**
+ * 重选,先从非selected的列表中选择,没有在从selected列表中选择.
+ * @param loadbalance
+ * @param invocation
+ * @param invokers
+ * @param selected
+ * @return
+ * @throws RpcException
+ */
+ private Invoker<T> reselect(LoadBalance loadbalance,Invocation invocation,
+ List<Invoker<T>> invokers, List<Invoker<T>> selected ,boolean availablecheck)
+ throws RpcException {
+
+ //预先分配一个,这个列表是一定会用到的.
+ List<Invoker<T>> reselectInvokers = new ArrayList<Invoker<T>>(invokers.size()>1?(invokers.size()-1):invokers.size());
+
+ //先从非select中选
+ if( availablecheck ){ //选isAvailable 的非select
+ for(Invoker<T> invoker : invokers){
+ if(invoker.isAvailable()){
+ if(selected ==null || !selected.contains(invoker)){
+ reselectInvokers.add(invoker);
+ }
+ }
+ }
+ if(reselectInvokers.size()>0){
+ return loadbalance.select(reselectInvokers, invocation);
+ }
+ }else{ //选全部非select
+ for(Invoker<T> invoker : invokers){
+ if(selected ==null || !selected.contains(invoker)){
+ reselectInvokers.add(invoker);
+ }
+ }
+ if(reselectInvokers.size()>0){
+ return loadbalance.select(reselectInvokers, invocation);
+ }
+ }
+ //最后从select中选可用的.
+ {
+ if(selected != null){
+ for(Invoker<T> invoker : selected){
+ if((invoker.isAvailable()) //优先选available
+ && !reselectInvokers.contains(invoker)){
+ reselectInvokers.add(invoker);
+ }
+ }
+ }
+ if(reselectInvokers.size()>0){
+ return loadbalance.select(reselectInvokers, invocation);
+ }
+ }
+ return null;
+ }
+
+ public Result invoke(final Invocation invocation) throws RpcException {
+
+ if(destroyed){
+ throw new RpcException("Rpc invoker for " + getInterface() + " on consumer " + NetUtils.getLocalHost()
+ + " use dubbo version " + Version.getVersion()
+ + " is not destroyed! Can not invoke any more.");
+ }
+
+ LoadBalance loadbalance;
+
+ List<Invoker<T>> invokers = directory.list(invocation);
+ if (invokers != null && invokers.size() > 0) {
+ loadbalance = ExtensionLoader.getExtensionLoader(LoadBalance.class).getExtension(invokers.get(0).getUrl()
+ .getMethodParameter(invocation.getMethodName(),Constants.LOADBALANCE_KEY, Constants.DEFAULT_LOADBALANCE));
+ } else {
+ loadbalance = ExtensionLoader.getExtensionLoader(LoadBalance.class).getExtension(Constants.DEFAULT_LOADBALANCE);
+ }
+ return doInvoke(invocation, invokers, loadbalance);
+ }
+
+ @Override
+ public String toString() {
+ return getInterface() + " -> " + getUrl().toString();
+ }
+
+ protected abstract Result doInvoke(Invocation invocation, List<Invoker<T>> invokers,
+ LoadBalance loadbalance) throws RpcException;
+
+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/AbstractDirectory.java b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/AbstractDirectory.java
new file mode 100644
index 0000000..6b635a6
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/AbstractDirectory.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.cluster.support;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.ExtensionLoader;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.RpcException;
+import com.alibaba.dubbo.rpc.cluster.Directory;
+import com.alibaba.dubbo.rpc.cluster.Router;
+import com.alibaba.dubbo.rpc.cluster.RouterFactory;
+
+/**
+ * 增加router的Directory
+ * @author chao.liuc
+ */
+public abstract class AbstractDirectory<T> implements Directory<T> {
+
+ private final URL url ;
+ protected volatile boolean destroyed = false;
+
+ private List<Router> routers = new ArrayList<Router>();
+
+ public AbstractDirectory(URL url) {
+ this(url, null);
+ }
+
+ public AbstractDirectory(URL url, List<Router> routers) {
+ if (url == null)
+ throw new IllegalArgumentException("url == null");
+ if (routers == null){
+ routers = new ArrayList<Router>();
+ }
+
+ this.url = url;
+ String routerkey = url.getParameter(Constants.ROUTER_KEY);
+ if (routerkey != null && routerkey.length()>0 ){
+ RouterFactory routerFactory = ExtensionLoader.getExtensionLoader(RouterFactory.class).getExtension(routerkey);
+ routers.add(routerFactory.getRouter(url));
+ }
+ if (routers != null) {
+ setRouters(routers);
+ }
+ }
+
+ public List<Invoker<T>> list(Invocation invocation) throws RpcException {
+ if (destroyed){
+ throw new RpcException("Directory already destroyed .url: "+ getUrl());
+ }
+ List<Invoker<T>> invokers = doList(invocation);
+ for (Router router: routers){
+ invokers = router.route(invokers, invocation);
+ }
+ return invokers;
+ }
+
+ public URL getUrl() {
+ return url;
+ }
+
+ public List<Router> getRouters(){
+ return routers;
+ }
+
+ protected abstract List<Invoker<T>> doList(Invocation invocation) throws RpcException ;
+
+ protected void setRouters(List<Router> r){
+ routers = r;
+ }
+
+ public void destroy(){
+ destroyed = true;
+ }
+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/AvailableCluster.java b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/AvailableCluster.java
new file mode 100644
index 0000000..0ef2630
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/AvailableCluster.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.cluster.support;
+
+import java.util.List;
+
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.RpcException;
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Result;
+import com.alibaba.dubbo.rpc.cluster.Cluster;
+import com.alibaba.dubbo.rpc.cluster.Directory;
+import com.alibaba.dubbo.rpc.cluster.LoadBalance;
+
+/**
+ * AvailableCluster
+ *
+ * @author william.liangf
+ */
+@Extension(AvailableCluster.NAME)
+public class AvailableCluster implements Cluster {
+
+ public static final String NAME = "available";
+
+ public <T> Invoker<T> merge(Directory<T> directory) throws RpcException {
+
+ return new AbstractClusterInvoker<T>(directory) {
+ public Result doInvoke(Invocation invocation, List<Invoker<T>> invokers, LoadBalance loadbalance) throws RpcException {
+ for (Invoker<T> invoker : invokers) {
+ if (invoker.isAvailable()) {
+ return invoker.invoke(invocation);
+ }
+ }
+ throw new RpcException("No provider available in " + invokers);
+ }
+ };
+
+ }
+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/AvailableClusterInvoker.java b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/AvailableClusterInvoker.java
new file mode 100644
index 0000000..203fe81
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/AvailableClusterInvoker.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.cluster.support;
+
+import java.util.List;
+
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.Result;
+import com.alibaba.dubbo.rpc.RpcException;
+import com.alibaba.dubbo.rpc.cluster.Directory;
+import com.alibaba.dubbo.rpc.cluster.LoadBalance;
+
+/**
+ * AvailableCluster
+ *
+ * @author william.liangf
+ */
+public class AvailableClusterInvoker<T> extends AbstractClusterInvoker<T> {
+ public AvailableClusterInvoker(Directory<T> directory) {
+ super(directory);
+ }
+
+ public Result doInvoke(Invocation invocation, List<Invoker<T>> invokers, LoadBalance loadbalance) throws RpcException {
+ for (Invoker<T> invoker : invokers) {
+ if (invoker.isAvailable()) {
+ return invoker.invoke(invocation);
+ }
+ }
+ throw new RpcException("No provider available in " + invokers);
+ }
+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/ClusterUtils.java b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/ClusterUtils.java
new file mode 100644
index 0000000..032ed6f
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/ClusterUtils.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.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);
+ }
+ 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..e16c068
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/FailbackCluster.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.rpc.cluster.support;
+
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.RpcException;
+import com.alibaba.dubbo.rpc.cluster.Cluster;
+import com.alibaba.dubbo.rpc.cluster.Directory;
+
+/**
+ * 失败自动恢复,后台记录失败请求,定时重发,通常用于消息通知操作。
+ *
+ * <a href="http://en.wikipedia.org/wiki/Failback">Failback</a>
+ *
+ * @author william.liangf
+ */
+@Extension(FailbackCluster.NAME)
+public class FailbackCluster implements Cluster {
+ public final static String NAME = "failback";
+
+ public <T> Invoker<T> merge(Directory<T> directory) throws RpcException {
+ return new FailbackClusterInvoker<T>(directory);
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/FailbackClusterInvoker.java b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/FailbackClusterInvoker.java
new file mode 100644
index 0000000..ae3254c
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/FailbackClusterInvoker.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.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.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.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> {
+
+ public FailbackClusterInvoker(Directory<T> directory){
+ super(directory);
+ }
+
+ 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<?>>();
+
+ private void addFailed(Invocation invocation, AbstractClusterInvoker<?> router) {
+ if (retryFuture == null) {
+ synchronized (this) {
+ if (retryFuture == null) {
+ retryFuture = scheduledExecutorService.scheduleWithFixedDelay(new Runnable() {
+
+ public void run() {
+ // 收集统计信息
+ try {
+ retryFailed();
+ } catch (Throwable t) { // 防御性容错
+ logger.error("Unexpected error occur at collect statistic", t);
+ }
+ }
+ }, RETRY_FAILED_PERIOD, RETRY_FAILED_PERIOD, TimeUnit.MILLISECONDS);
+ }
+ }
+ }
+ failed.put(invocation, router);
+ }
+
+ void retryFailed() {
+ if (failed.size() == 0) {
+ return;
+ }
+ for (Map.Entry<Invocation, AbstractClusterInvoker<?>> entry : new HashMap<Invocation, AbstractClusterInvoker<?>>(
+ failed).entrySet()) {
+ Invocation invocation = entry.getKey();
+ Invoker<?> invoker = entry.getValue();
+ try {
+ invoker.invoke(invocation);
+ failed.remove(invocation);
+ } catch (Throwable e) {
+ logger.error("Failed retry to invoke " + invocation + ", waiting again.", e);
+ }
+ }
+ }
+
+ protected Result doInvoke(Invocation invocation, List<Invoker<T>> invokers, LoadBalance loadbalance) throws RpcException {
+ try {
+ if (invokers == null || invokers.size() == 0)
+ throw new RpcException("No provider available for service " + getInterface().getName() + " on consumer " + NetUtils.getLocalHost() + " use dubbo version " + Version.getVersion() + ", Please check whether the service do exist or version is right firstly, and check the provider has started.");
+ Invoker<T> invoker = select(loadbalance, invocation, invokers, null);
+ return invoker.invoke(invocation);
+ } catch (Throwable e) {
+ logger.error("Failback to invoke " + invocation + ", wait for retry in background. Ignored exception: "
+ + e.getMessage() + ", ", e);
+ addFailed(invocation, this);
+ return new RpcResult(); // ignore
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/FailfastCluster.java b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/FailfastCluster.java
new file mode 100644
index 0000000..3945f2b
--- /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.common.Extension;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.RpcException;
+import com.alibaba.dubbo.rpc.cluster.Cluster;
+import com.alibaba.dubbo.rpc.cluster.Directory;
+
+/**
+ * 快速失败,只发起一次调用,失败立即报错,通常用于非幂等性的写操作。</br>
+ * <a href="http://en.wikipedia.org/wiki/Fail-fast">Fail-fast</a>
+ *
+ * @author william.liangf
+ */
+@Extension(FailfastCluster.NAME)
+public class FailfastCluster implements Cluster {
+ public final static String NAME = "failfast";
+
+ public <T> Invoker<T> merge(Directory<T> directory) throws RpcException {
+ return new FailfastClusterInvoker<T>(directory);
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/FailfastClusterInvoker.java b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/FailfastClusterInvoker.java
new file mode 100644
index 0000000..26d0b06
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/FailfastClusterInvoker.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.cluster.support;
+
+import java.util.List;
+
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.common.Version;
+import com.alibaba.dubbo.common.utils.NetUtils;
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.Result;
+import com.alibaba.dubbo.rpc.RpcException;
+import com.alibaba.dubbo.rpc.cluster.Directory;
+import com.alibaba.dubbo.rpc.cluster.LoadBalance;
+
+/**
+ * 快速失败,只发起一次调用,失败立即报错,通常用于非幂等性的写操作。</br>
+ * <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 {
+ if (invokers == null || invokers.size() == 0)
+ throw new RpcException("No provider available for service " + getInterface().getName() + " on consumer " + NetUtils.getLocalHost() + " use dubbo version " + Version.getVersion() + ", Please check whether the service do exist or version is right firstly, and check the provider has started.");
+ Invoker<T> invoker = select(loadbalance, invocation, invokers, null);
+ try {
+ return invoker.invoke(invocation);
+ } catch (Throwable e) {
+ throw new RpcException("Failfast invoke providers " + invoker.getUrl() + " " + loadbalance.getClass().getAnnotation(Extension.class).value() + " select from all providers " + invokers + " for service " + getInterface().getName() + " method " + invocation.getMethodName() + " on consumer " + NetUtils.getLocalHost() + " use dubbo version " + Version.getVersion() + ", but no luck to perform the invocation. Last error is: " + e.getMessage(), e);
+ }
+ }
+}
\ 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..d004412
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/FailoverCluster.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.rpc.cluster.support;
+
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.RpcException;
+import com.alibaba.dubbo.rpc.cluster.Cluster;
+import com.alibaba.dubbo.rpc.cluster.Directory;
+
+/**
+ * 失败转移,当出现失败,重试其它服务器,通常用于读操作,但重试会带来更长延迟。 <a
+ * href="http://en.wikipedia.org/wiki/Failover">Failover</a>
+ *
+ * @author william.liangf
+ */
+@Extension(FailoverCluster.NAME)
+public class FailoverCluster implements Cluster {
+
+ public final static String NAME = "failover";
+
+ public <T> Invoker<T> merge(Directory<T> directory) throws RpcException {
+
+ return new FailoverClusterInvoker<T>(directory);
+ }
+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/FailoverClusterInvoker.java b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/FailoverClusterInvoker.java
new file mode 100644
index 0000000..e08e51a
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/FailoverClusterInvoker.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.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.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/Failover">Failover</a>
+ * @author william.liangf
+ *
+ */
+public class FailoverClusterInvoker<T> extends AbstractClusterInvoker<T>{
+ public FailoverClusterInvoker(Directory<T> directory) {
+ super(directory);
+ }
+
+ public Result doInvoke(Invocation invocation, List<Invoker<T>> invokers, LoadBalance loadbalance) throws RpcException {
+ if (invokers == null || invokers.size() == 0)
+ throw new RpcException("No provider available for service " + getInterface().getName() + " on consumer " + NetUtils.getLocalHost() + " use dubbo version " + Version.getVersion() + ", Please check whether the service do exist or version is right firstly, and check the provider has started.");
+
+ int len = getUrl().getMethodParameter(invocation.getMethodName(), Constants.RETRIES_KEY, Constants.DEFAULT_RETRIES) + 1;
+ if (len <= 0)
+ len = 1;
+
+ // retry loop.
+ RpcException le = null; // last exception.
+ List<Invoker<T>> invoked = new ArrayList<Invoker<T>>(invokers.size()); // invoked invokers.
+ Set<URL> providers = new HashSet<URL>(len);
+ for (int i = 0; i < len; i++) {
+ //boolean pp = false; // is provider problem.
+ Invoker<T> invoker = select(loadbalance, invocation, invokers, invoked);
+ invoked.add(invoker);
+ providers.add(invoker.getUrl());
+ try {
+ return invoker.invoke(invocation);
+ } catch (RpcException e) {
+ if (e.isBiz()) throw e;
+
+ le = e;
+ //pp = true;
+ } catch (Throwable e) // biz exception.
+ {
+ throw new RpcException(e.getMessage(), e);
+ } finally {
+ //if (pp) // if provider problem, fail over.
+ // inv.setWeight(0);
+ }
+ }
+ List<URL> urls = new ArrayList<URL>(invokers.size());
+ for(Invoker<T> invoker : invokers){
+ if(invoker != null )
+ urls.add(invoker.getUrl());
+ }
+ throw new RpcException(le.getCode(),"Tried " + len + " times to invoke providers " + providers + " " + loadbalance.getClass().getAnnotation(Extension.class).value() + " select from all providers " + invokers + " for service " + getInterface().getName() + " method " + invocation.getMethodName() + " on consumer " + NetUtils.getLocalHost() + " use dubbo version " + Version.getVersion() + ", but no luck to perform the invocation. Last error is: " + (le != null ? le.getMessage() : ""), 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..2bf974b
--- /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.common.Extension;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.RpcException;
+import com.alibaba.dubbo.rpc.cluster.Cluster;
+import com.alibaba.dubbo.rpc.cluster.Directory;
+
+/**
+ * 失败安全,出现异常时,直接忽略,通常用于写入审计日志等操作。 <a
+ * href="http://en.wikipedia.org/wiki/Fail-safe">Fail-safe</a>
+ *
+ * @author william.liangf
+ */
+@Extension(FailsafeCluster.NAME)
+public class FailsafeCluster implements Cluster {
+
+ public final static String NAME = "failsafe";
+
+ public <T> Invoker<T> merge(Directory<T> directory) throws RpcException {
+ return new FailsafeClusterInvoker<T>(directory);
+ }
+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/FailsafeClusterInvoker.java b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/FailsafeClusterInvoker.java
new file mode 100644
index 0000000..bd4b7e7
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/FailsafeClusterInvoker.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.cluster.support;
+
+import java.util.List;
+
+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.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 {
+ if (invokers == null || invokers.size() == 0)
+ throw new RpcException("No provider available for service " + getInterface().getName() + " on consumer " + NetUtils.getLocalHost() + " use dubbo version " + Version.getVersion() + ", Please check whether the service do exist or version is right firstly, and check the provider has started.");
+ 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..f57cda0
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/ForkingCluster.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.rpc.cluster.support;
+
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.RpcException;
+import com.alibaba.dubbo.rpc.cluster.Cluster;
+import com.alibaba.dubbo.rpc.cluster.Directory;
+
+/**
+ * 并行调用,只要一个成功即返回,通常用于实时性要求较高的操作,但需要浪费更多服务资源。
+ *
+ * @author william.liangf
+ */
+@Extension(ForkingCluster.NAME)
+public class ForkingCluster implements Cluster {
+
+ public final static String NAME = "forking";
+
+ public <T> Invoker<T> merge(Directory<T> directory) throws RpcException {
+ return new ForkingClusterInvoker<T>(directory);
+ }
+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/ForkingClusterInvoker.java b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/ForkingClusterInvoker.java
new file mode 100644
index 0000000..25e7178
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/ForkingClusterInvoker.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.cluster.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.Version;
+import com.alibaba.dubbo.common.utils.NamedThreadFactory;
+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.RpcResult;
+import com.alibaba.dubbo.rpc.cluster.Directory;
+import com.alibaba.dubbo.rpc.cluster.LoadBalance;
+
+/**
+ * 并行调用,只要一个成功即返回,通常用于实时性要求较高的操作,但需要浪费更多服务资源。
+ *
+ * @author william.liangf
+ */
+public class ForkingClusterInvoker<T> extends AbstractClusterInvoker<T>{
+
+ public ForkingClusterInvoker(Directory<T> directory) {
+ super(directory);
+ }
+
+ private final ExecutorService executor = Executors.newCachedThreadPool(new NamedThreadFactory("forking-cluster-timer", true));
+
+ public Result doInvoke(final Invocation invocation, List<Invoker<T>> invokers, LoadBalance loadbalance) throws RpcException {
+ if (invokers == null || invokers.size() == 0)
+ throw new RpcException("No provider available for service " + getInterface().getName() + " on consumer " + NetUtils.getLocalHost() + " use dubbo version " + Version.getVersion() + ", Please check whether the service do exist or version is right firstly, and check the provider has started.");
+ 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);
+ }
+ }
+ }
+ final AtomicInteger count = new AtomicInteger();
+ final BlockingQueue<Result> ref = new LinkedBlockingQueue<Result>();
+ 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(new RpcResult(e));
+ }
+ }
+ }
+ });
+ }
+ try {
+ return ref.poll(timeout, TimeUnit.MILLISECONDS);
+ } catch (InterruptedException e) {
+ throw new RpcException("Failed to forking invoke provider " + selected + ", cause: " + e.getMessage(), e);
+ }
+ }
+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/main/resources/META-INF/services/com.alibaba.dubbo.rpc.cluster.Cluster b/dubbo-cluster/src/main/resources/META-INF/services/com.alibaba.dubbo.rpc.cluster.Cluster
new file mode 100644
index 0000000..f952821
--- /dev/null
+++ b/dubbo-cluster/src/main/resources/META-INF/services/com.alibaba.dubbo.rpc.cluster.Cluster
@@ -0,0 +1,6 @@
+com.alibaba.dubbo.rpc.cluster.support.FailoverCluster
+com.alibaba.dubbo.rpc.cluster.support.FailfastCluster
+com.alibaba.dubbo.rpc.cluster.support.FailsafeCluster
+com.alibaba.dubbo.rpc.cluster.support.FailbackCluster
+com.alibaba.dubbo.rpc.cluster.support.ForkingCluster
+com.alibaba.dubbo.rpc.cluster.support.AvailableCluster
\ No newline at end of file
diff --git a/dubbo-cluster/src/main/resources/META-INF/services/com.alibaba.dubbo.rpc.cluster.LoadBalance b/dubbo-cluster/src/main/resources/META-INF/services/com.alibaba.dubbo.rpc.cluster.LoadBalance
new file mode 100644
index 0000000..6bf44c0
--- /dev/null
+++ b/dubbo-cluster/src/main/resources/META-INF/services/com.alibaba.dubbo.rpc.cluster.LoadBalance
@@ -0,0 +1,4 @@
+com.alibaba.dubbo.rpc.cluster.loadbalance.LoadBalanceAdptive
+com.alibaba.dubbo.rpc.cluster.loadbalance.RandomLoadBalance
+com.alibaba.dubbo.rpc.cluster.loadbalance.RoundRobinLoadBalance
+com.alibaba.dubbo.rpc.cluster.loadbalance.LeastActiveLoadBalance
\ No newline at end of file
diff --git a/dubbo-cluster/src/main/resources/META-INF/services/com.alibaba.dubbo.rpc.cluster.RouterFactory b/dubbo-cluster/src/main/resources/META-INF/services/com.alibaba.dubbo.rpc.cluster.RouterFactory
new file mode 100644
index 0000000..a5021fb
--- /dev/null
+++ b/dubbo-cluster/src/main/resources/META-INF/services/com.alibaba.dubbo.rpc.cluster.RouterFactory
@@ -0,0 +1,2 @@
+com.alibaba.dubbo.rpc.cluster.router.FileRouterFactory
+com.alibaba.dubbo.rpc.cluster.router.ScriptRouterFactory
\ No newline at end of file
diff --git a/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/StickyTest.java b/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/StickyTest.java
new file mode 100644
index 0000000..f179f21
--- /dev/null
+++ b/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/StickyTest.java
@@ -0,0 +1,164 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.cluster;
+
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.easymock.EasyMock;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.Result;
+import com.alibaba.dubbo.rpc.RpcConstants;
+import com.alibaba.dubbo.rpc.RpcException;
+import com.alibaba.dubbo.rpc.RpcResult;
+import com.alibaba.dubbo.rpc.cluster.Directory;
+import com.alibaba.dubbo.rpc.cluster.LoadBalance;
+import com.alibaba.dubbo.rpc.cluster.support.AbstractClusterInvoker;
+@SuppressWarnings("unchecked")
+public class StickyTest {
+
+ List<Invoker<StickyTest>> invokers = new ArrayList<Invoker<StickyTest>>();
+
+
+ Invoker<StickyTest> invoker1 = EasyMock.createMock(Invoker.class);
+ Invoker<StickyTest> invoker2 = EasyMock.createMock(Invoker.class);
+ Invocation invocation;
+ Directory<StickyTest> dic ;
+ Result result = new RpcResult();
+ StickyClusterInvoker<StickyTest> clusterinvoker = null;
+
+ @BeforeClass
+ public static void setUpBeforeClass() throws Exception {
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ dic = EasyMock.createMock(Directory.class);
+ invocation = EasyMock.createMock(Invocation.class);
+
+ EasyMock.expect(dic.getUrl()).andReturn(url).anyTimes();
+ EasyMock.expect(dic.list(invocation)).andReturn(invokers).anyTimes();
+ EasyMock.expect(dic.getInterface()).andReturn(StickyTest.class).anyTimes();
+ EasyMock.replay(dic);
+ invokers.add(invoker1);
+ invokers.add(invoker2);
+
+ clusterinvoker = new StickyClusterInvoker<StickyTest>(dic);
+ }
+ URL url = URL.valueOf("test://test:11/test?"
+ +"&loadbalance=roundrobin"
+// +"&"+RpcConstants.CLUSTER_AVAILABLE_CHECK_KEY+"=true"
+ +"&"+RpcConstants.CLUSTER_STICKY_KEY+"=true"
+ );
+
+ int runs = 1;
+ @Test
+ public void testStickyNoCheck() {
+ int count = testSticky(null,false);
+ System.out.println(count);
+ Assert.assertTrue(count>0 && count <=runs);
+ }
+
+ @Test
+ public void testStickyForceCheck() {
+ int count = testSticky(null,true);
+ Assert.assertTrue(count == 0 || count == runs);
+ }
+ @Test
+ public void testMethodStickyNoCheck() {
+ int count = testSticky("method1",false);
+ System.out.println(count);
+ Assert.assertTrue(count>0 && count <=runs);
+ }
+
+ @Test
+ public void testMethodStickyForceCheck() {
+ int count = testSticky("method1",true);
+ Assert.assertTrue(count == 0 || count == runs);
+ }
+
+ @Test
+ public void testMethodsSticky() {
+ for(int i = 0 ;i<100 ; i++){//多次调用看两个方法是否都选在同一个invoker
+ int count1 = testSticky("method1",true);
+ int count2 = testSticky("method2",true);
+ Assert.assertTrue(count1 == count2);
+ }
+ }
+
+ public int testSticky(String methodName, boolean check) {
+ if (methodName == null){
+ url = url.addParameter(RpcConstants.CLUSTER_STICKY_KEY, String.valueOf(check));
+ }else {
+ url = url.addParameter(methodName+"."+RpcConstants.CLUSTER_STICKY_KEY, String.valueOf(check));
+ }
+ EasyMock.reset(invoker1);
+ EasyMock.expect(invoker1.invoke(invocation)).andReturn(result).anyTimes();
+ EasyMock.expect(invoker1.isAvailable()).andReturn(true).anyTimes();
+ EasyMock.expect(invoker1.getUrl()).andReturn(url).anyTimes();
+ EasyMock.expect(invoker1.getInterface()).andReturn(StickyTest.class).anyTimes();
+ EasyMock.replay(invoker1);
+
+ EasyMock.reset(invoker2);
+ EasyMock.expect(invoker2.invoke(invocation)).andReturn(result).anyTimes();
+ EasyMock.expect(invoker2.isAvailable()).andReturn(true).anyTimes();
+ EasyMock.expect(invoker2.getUrl()).andReturn(url).anyTimes();
+ EasyMock.expect(invoker2.getInterface()).andReturn(StickyTest.class).anyTimes();
+ EasyMock.replay(invoker2);
+
+ EasyMock.reset(invocation);
+ EasyMock.expect(invocation.getMethodName()).andReturn(methodName).anyTimes();
+ EasyMock.replay(invocation);
+
+ int count = 0;
+ for (int i = 0; i < runs; i++) {
+ Assert.assertEquals(null, clusterinvoker.invoke(invocation));
+ if(invoker1 == clusterinvoker.getSelectedInvoker()){
+ count ++;
+ }
+ }
+ return count;
+ }
+
+
+ static class StickyClusterInvoker<T> extends AbstractClusterInvoker<T>{
+ private Invoker<T> selectedInvoker ;
+ public StickyClusterInvoker(Directory<T> directory) {
+ super(directory);
+ }
+ public StickyClusterInvoker(Directory<T> directory,URL url) {
+ super(directory, url);
+ }
+ @Override
+ protected Result doInvoke(Invocation invocation, List<Invoker<T>> invokers,
+ LoadBalance loadbalance) throws RpcException {
+ Invoker<T> invoker = select(loadbalance, invocation, invokers, null);
+ selectedInvoker = invoker ;
+ return null;
+ }
+ public Invoker<T> getSelectedInvoker() {
+ return selectedInvoker;
+ }
+ }
+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/filter/DemoService.java b/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/filter/DemoService.java
new file mode 100644
index 0000000..373e7d1
--- /dev/null
+++ b/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/filter/DemoService.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.cluster.filter;
+
+/**
+ * <code>TestService</code>
+ */
+
+public interface DemoService
+{
+ String sayHello(String name);
+
+ int plus(int a,int b);
+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/filter/DemoServiceLocal.java b/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/filter/DemoServiceLocal.java
new file mode 100644
index 0000000..8f29028
--- /dev/null
+++ b/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/filter/DemoServiceLocal.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.cluster.filter;
+
+/**
+ * <code>TestService</code>
+ */
+
+public class DemoServiceLocal implements DemoService {
+
+ public DemoServiceLocal(DemoService demoService){
+ }
+
+ public String sayHello(String name) {
+ return name;
+ }
+
+ public int plus(int a, int b) {
+ return a + b;
+ }
+
+ public void ondisconnect(){
+
+ }
+
+ public void onconnect(){
+
+ }
+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/filter/DemoServiceMock.java b/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/filter/DemoServiceMock.java
new file mode 100644
index 0000000..b1b7b4a
--- /dev/null
+++ b/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/filter/DemoServiceMock.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.cluster.filter;
+
+/**
+ * MockService.java
+ * @author tony.chenl
+ */
+public class DemoServiceMock implements DemoService{
+ public String sayHello(String name) {
+ return name;
+ }
+
+ public int plus(int a, int b) {
+ return a+b;
+ }
+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/filter/DemoServiceStub.java b/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/filter/DemoServiceStub.java
new file mode 100644
index 0000000..c9bcb6c
--- /dev/null
+++ b/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/filter/DemoServiceStub.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.cluster.filter;
+
+/**
+ * <code>TestService</code>
+ */
+
+public class DemoServiceStub implements DemoService {
+
+ public DemoServiceStub(DemoService demoService){
+ }
+
+ public String sayHello(String name) {
+ return name;
+ }
+
+ public int plus(int a, int b) {
+ return a + b;
+ }
+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/filter/MockService.java b/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/filter/MockService.java
new file mode 100644
index 0000000..a8a2d3a
--- /dev/null
+++ b/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/filter/MockService.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.cluster.filter;
+
+/**
+ * MockService.java
+ * @author tony.chenl
+ */
+public class MockService implements DemoService{
+ public String sayHello(String name) {
+ return name;
+ }
+
+ public int plus(int a, int b) {
+ return a+b;
+ }
+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/loadbalance/LoadBalanceTest.java b/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/loadbalance/LoadBalanceTest.java
new file mode 100644
index 0000000..ade8587
--- /dev/null
+++ b/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/loadbalance/LoadBalanceTest.java
@@ -0,0 +1,150 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.cluster.loadbalance;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicLong;
+
+import junit.framework.Assert;
+
+import org.easymock.EasyMock;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import com.alibaba.dubbo.common.ExtensionLoader;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.cluster.LoadBalance;
+
+/**
+ * RoundRobinLoadBalanceTest
+ * @author liuchao
+ *
+ */
+@SuppressWarnings({ "unchecked", "rawtypes" })
+public class LoadBalanceTest {
+ Invocation invocation ;
+ List<Invoker<LoadBalanceTest>> invokers = new ArrayList<Invoker<LoadBalanceTest>>();
+ Invoker<LoadBalanceTest> invoker1 ;
+ Invoker<LoadBalanceTest> invoker2 ;
+ Invoker<LoadBalanceTest> invoker3 ;
+ Invoker<LoadBalanceTest> invoker4 ;
+ Invoker<LoadBalanceTest> invoker5 ;
+ /**
+ * @throws java.lang.Exception
+ */
+ @BeforeClass
+ public static void setUpBeforeClass() throws Exception {
+ }
+
+ /**
+ * @throws java.lang.Exception
+ */
+ @Before
+ public void setUp() throws Exception {
+
+
+ invocation = EasyMock.createMock(Invocation.class);
+ EasyMock.expect(invocation.getMethodName()).andReturn("method1").anyTimes();
+
+ invoker1 = EasyMock.createMock(Invoker.class);
+ invoker2 = EasyMock.createMock(Invoker.class);
+ invoker3 = EasyMock.createMock(Invoker.class);
+ invoker4 = EasyMock.createMock(Invoker.class);
+ invoker5 = EasyMock.createMock(Invoker.class);
+
+ URL url = URL.valueOf("test://127.0.0.1/DemoService");
+
+ EasyMock.expect(invoker1.isAvailable()).andReturn(true).anyTimes();
+ EasyMock.expect(invoker1.getInterface()).andReturn(LoadBalanceTest.class).anyTimes();
+ EasyMock.expect(invoker1.getUrl()).andReturn(url).anyTimes();
+
+ EasyMock.expect(invoker2.isAvailable()).andReturn(true).anyTimes();
+ EasyMock.expect(invoker2.getInterface()).andReturn(LoadBalanceTest.class).anyTimes();
+ EasyMock.expect(invoker2.getUrl()).andReturn(url).anyTimes();
+
+ EasyMock.expect(invoker3.isAvailable()).andReturn(true).anyTimes();
+ EasyMock.expect(invoker3.getInterface()).andReturn(LoadBalanceTest.class).anyTimes();
+ EasyMock.expect(invoker3.getUrl()).andReturn(url).anyTimes();
+
+ EasyMock.expect(invoker4.isAvailable()).andReturn(true).anyTimes();
+ EasyMock.expect(invoker4.getInterface()).andReturn(LoadBalanceTest.class).anyTimes();
+ EasyMock.expect(invoker4.getUrl()).andReturn(url).anyTimes();
+
+ EasyMock.expect(invoker5.isAvailable()).andReturn(true).anyTimes();
+ EasyMock.expect(invoker5.getInterface()).andReturn(LoadBalanceTest.class).anyTimes();
+ EasyMock.expect(invoker5.getUrl()).andReturn(url).anyTimes();
+
+ EasyMock.replay(invocation,invoker1,invoker2,invoker3,invoker4,invoker5);
+
+ invokers.add(invoker1);
+ invokers.add(invoker2);
+ invokers.add(invoker3);
+ invokers.add(invoker4);
+ invokers.add(invoker5);
+ }
+
+ @Test
+ public void testRoundRobinLoadBalance_select() {
+ int runs = 10000;
+ Map<Invoker,AtomicLong> counter = getInvokeCounter(runs,RoundRobinLoadBalance.NAME);
+ for (Invoker minvoker :counter.keySet() ){
+ Long count = counter.get(minvoker).get();
+ Assert.assertTrue("abs diff shoud < 1", Math.abs(count-runs/(0f+invokers.size())) <1f);
+ }
+ }
+ @Test
+ public void testRandomLoadBalance_select() {
+ int runs = 1000;
+ Map<Invoker,AtomicLong> counter = getInvokeCounter(runs,RandomLoadBalance.NAME);
+ for (Invoker minvoker :counter.keySet() ){
+ Long count = counter.get(minvoker).get();
+// System.out.println(count);
+ Assert.assertTrue("abs diff shoud < avg", Math.abs(count-runs/(0f+invokers.size())) <runs/(0f+invokers.size()));
+ }
+ }
+ @Test
+ public void testLeastActiveLoadBalance_select() {
+ int runs = 10000;
+ Map<Invoker,AtomicLong> counter = getInvokeCounter(runs,LeastActiveLoadBalance.NAME);
+ for (Invoker minvoker :counter.keySet() ){
+ Long count = counter.get(minvoker).get();
+// System.out.println(count);
+ Assert.assertTrue("abs diff shoud < avg", Math.abs(count-runs/(0f+invokers.size())) <runs/(0f+invokers.size()));
+ }
+ }
+
+
+ public Map<Invoker,AtomicLong> getInvokeCounter(int runs,String loadbalanceName) {
+ Map<Invoker,AtomicLong> counter = new ConcurrentHashMap<Invoker,AtomicLong>();
+ LoadBalance lb = ExtensionLoader.getExtensionLoader(LoadBalance.class).getExtension(loadbalanceName);
+ for(Invoker invoker :invokers){
+ counter.put(invoker, new AtomicLong(0));
+ }
+ for(int i=0;i<runs;i++){
+ Invoker sinvoker = lb.select(invokers, invocation);
+ counter.get(sinvoker).incrementAndGet();
+ }
+ return counter;
+ }
+
+
+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/router/MockInvoker.java b/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/router/MockInvoker.java
new file mode 100644
index 0000000..9e2c8f1
--- /dev/null
+++ b/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/router/MockInvoker.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.cluster.router;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.Result;
+import com.alibaba.dubbo.rpc.RpcException;
+
+public class MockInvoker<T> implements Invoker<T> {
+ private boolean available = false;
+ private URL url ;
+
+ public MockInvoker() {
+ }
+ public MockInvoker(URL url) {
+ super();
+ this.url = url;
+ }
+ public MockInvoker(boolean available) {
+ this.available = available;
+ }
+
+ public Class<T> getInterface() {
+ return null;
+ }
+
+ public URL getUrl() {
+ return url;
+ }
+
+ public boolean isAvailable() {
+ return available;
+ }
+
+ public Result invoke(Invocation invocation) throws RpcException {
+ return null;
+ }
+
+ public void destroy() {
+ }
+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/router/ScriptRouterEngineTest.java b/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/router/ScriptRouterEngineTest.java
new file mode 100644
index 0000000..9b79bb2
--- /dev/null
+++ b/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/router/ScriptRouterEngineTest.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.cluster.router;
+
+
+import java.util.ArrayList;
+import java.util.List;
+
+import junit.framework.Assert;
+
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.RpcConstants;
+import com.alibaba.dubbo.rpc.RpcInvocation;
+import com.alibaba.dubbo.rpc.cluster.Router;
+
+public class ScriptRouterEngineTest {
+
+ @BeforeClass
+ public static void setUpBeforeClass() throws Exception {
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ }
+
+ private URL SCRIPT_URL = URL.valueOf("script://javascript?type=javascript");
+
+ private URL getRouteUrl(String rule) {
+ return SCRIPT_URL.addParameterAndEncoded(RpcConstants.RULE_KEY, rule);
+ }
+
+ @Test
+ public void testRoute_ReturnAll(){
+ Router router = new ScriptRouterFactory().getRouter(getRouteUrl("function route(op1,op2){return op1} route(invokers)"));
+ List<Invoker<String>> invokers = new ArrayList<Invoker<String>>();
+ invokers.add(new MockInvoker<String>());
+ invokers.add(new MockInvoker<String>());
+ invokers.add(new MockInvoker<String>());
+ List<Invoker<String>> fileredInvokers = router.route(invokers, new RpcInvocation());
+ Assert.assertEquals(invokers, fileredInvokers);
+ }
+
+ @Test
+ public void testRoute_PickInvokers(){
+ String rule = "var result = new java.util.ArrayList(invokers.size());" +
+ "for (i=0;i<invokers.size(); i++){ " +
+ "if (invokers.get(i).isAvailable()) {" +
+ "result.add(invokers.get(i)) ;" +
+ "}" +
+ "} ; " +
+ "return result;";
+ String script = "function route(invokers,invocation,context){" + rule + "} route(invokers,invocation,context)";
+ Router router = new ScriptRouterFactory().getRouter(getRouteUrl(script));
+
+ List<Invoker<String>> invokers = new ArrayList<Invoker<String>>();
+ Invoker<String> invoker1 = new MockInvoker<String>(false) ;
+ Invoker<String> invoker2 = new MockInvoker<String>(true) ;
+ Invoker<String> invoker3 = new MockInvoker<String>(true) ;
+ invokers.add(invoker1);
+ invokers.add(invoker2);
+ invokers.add(invoker3);
+ List<Invoker<String>> fileredInvokers = router.route(invokers, new RpcInvocation());
+ Assert.assertEquals(2, fileredInvokers.size());
+ Assert.assertEquals(invoker2, fileredInvokers.get(0));
+ Assert.assertEquals(invoker3, fileredInvokers.get(1));
+ }
+ //TODO 异常场景测试。
+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/router/file/FileRouterEngineTest.java b/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/router/file/FileRouterEngineTest.java
new file mode 100644
index 0000000..8ff505c
--- /dev/null
+++ b/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/router/file/FileRouterEngineTest.java
@@ -0,0 +1,194 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.cluster.router.file;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import javax.script.ScriptEngineManager;
+
+import junit.framework.Assert;
+
+import org.easymock.EasyMock;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import com.alibaba.dubbo.common.ExtensionLoader;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.Result;
+import com.alibaba.dubbo.rpc.RpcException;
+import com.alibaba.dubbo.rpc.RpcResult;
+import com.alibaba.dubbo.rpc.cluster.Directory;
+import com.alibaba.dubbo.rpc.cluster.LoadBalance;
+import com.alibaba.dubbo.rpc.cluster.RouterFactory;
+import com.alibaba.dubbo.rpc.cluster.directory.StaticDirectory;
+import com.alibaba.dubbo.rpc.cluster.support.AbstractClusterInvoker;
+
+/**
+ * @author chao.liuc
+ */
+@SuppressWarnings("unchecked")
+public class FileRouterEngineTest {
+ List<Invoker<FileRouterEngineTest>> invokers = new ArrayList<Invoker<FileRouterEngineTest>>();
+
+ Invoker<FileRouterEngineTest> invoker1 = EasyMock.createMock(Invoker.class);
+ Invoker<FileRouterEngineTest> invoker2 = EasyMock.createMock(Invoker.class);
+ Invocation invocation;
+ Directory<FileRouterEngineTest> dic;
+ Result result = new RpcResult();
+ private RouterFactory routerFactory = ExtensionLoader.getExtensionLoader(RouterFactory.class).getAdaptiveExtension();
+
+ @BeforeClass
+ public static void setUpBeforeClass() throws Exception {
+ }
+
+ private static boolean isScriptUnsupported = new ScriptEngineManager().getEngineByName("javascript") == null;
+
+ @Before
+ public void setUp() throws Exception {
+ invokers.add(invoker1);
+ invokers.add(invoker2);
+ }
+
+ @Test
+ public void testRouteNotAvailable() {
+ if (isScriptUnsupported) return;
+ URL url = initUrl("notAvailablerule.javascript");
+ initInvocation("method1");
+ initDic(url);
+ initInvokers(url, true, false);
+
+ MockClusterInvoker<FileRouterEngineTest> sinvoker = new MockClusterInvoker<FileRouterEngineTest>(
+ dic, url);
+ for (int i = 0; i < 100; i++) {
+ sinvoker.invoke(invocation);
+ Invoker<FileRouterEngineTest> invoker = sinvoker.getSelectedInvoker();
+ Assert.assertEquals(invoker2, invoker);
+ }
+ }
+
+ @Test
+ public void testRouteAvailable() {
+ if (isScriptUnsupported) return;
+ URL url = initUrl("availablerule.javascript");
+ initInvocation("method1");
+ initDic(url);
+ initInvokers(url);
+
+ MockClusterInvoker<FileRouterEngineTest> sinvoker = new MockClusterInvoker<FileRouterEngineTest>(
+ dic, url);
+ for (int i = 0; i < 100; i++) {
+ sinvoker.invoke(invocation);
+ Invoker<FileRouterEngineTest> invoker = sinvoker.getSelectedInvoker();
+ Assert.assertEquals(invoker1, invoker);
+ }
+ }
+
+ @Test
+ public void testRouteByMethodName() {
+ if (isScriptUnsupported) return;
+ URL url = initUrl("methodrule.javascript");
+ {
+ initInvocation("method1");
+ initDic(url);
+ initInvokers(url, true, true);
+
+ MockClusterInvoker<FileRouterEngineTest> sinvoker = new MockClusterInvoker<FileRouterEngineTest>(
+ dic, url);
+ for (int i = 0; i < 100; i++) {
+ sinvoker.invoke(invocation);
+ Invoker<FileRouterEngineTest> invoker = sinvoker.getSelectedInvoker();
+ Assert.assertEquals(invoker1, invoker);
+ }
+ }
+ {
+ initInvocation("method2");
+ initDic(url);
+ initInvokers(url, true, true);
+ MockClusterInvoker<FileRouterEngineTest> sinvoker = new MockClusterInvoker<FileRouterEngineTest>(
+ dic, url);
+ for (int i = 0; i < 100; i++) {
+ sinvoker.invoke(invocation);
+ Invoker<FileRouterEngineTest> invoker = sinvoker.getSelectedInvoker();
+ Assert.assertEquals(invoker2, invoker);
+ }
+ }
+ }
+
+ private URL initUrl(String filename) {
+ filename = getClass().getClassLoader().getResource(getClass().getPackage().getName().replace('.', '/') + "/" + filename).toString();
+ URL url = URL.valueOf(filename);
+ return url;
+ }
+
+ private void initInvocation(String methodName) {
+ invocation = EasyMock.createMock(Invocation.class);
+ EasyMock.expect(invocation.getMethodName()).andReturn(methodName).anyTimes();
+ EasyMock.replay(invocation);
+ }
+
+ private void initInvokers(URL url) {
+ initInvokers(url, true, false);
+ }
+
+ private void initInvokers(URL url, boolean invoker1Status, boolean invoker2Status) {
+ EasyMock.reset(invoker1);
+ EasyMock.expect(invoker1.invoke(invocation)).andReturn(result).anyTimes();
+ EasyMock.expect(invoker1.isAvailable()).andReturn(invoker1Status).anyTimes();
+ EasyMock.expect(invoker1.getUrl()).andReturn(url).anyTimes();
+ EasyMock.expect(invoker1.getInterface()).andReturn(FileRouterEngineTest.class).anyTimes();
+ EasyMock.replay(invoker1);
+
+ EasyMock.reset(invoker2);
+ EasyMock.expect(invoker2.invoke(invocation)).andReturn(result).anyTimes();
+ EasyMock.expect(invoker2.isAvailable()).andReturn(invoker2Status).anyTimes();
+ EasyMock.expect(invoker2.getUrl()).andReturn(url).anyTimes();
+ EasyMock.expect(invoker2.getInterface()).andReturn(FileRouterEngineTest.class).anyTimes();
+ EasyMock.replay(invoker2);
+ }
+
+ private void initDic(URL url) {
+ dic = new StaticDirectory<FileRouterEngineTest>(url, invokers, Arrays.asList(routerFactory.getRouter(url)));
+ }
+
+ static class MockClusterInvoker<T> extends AbstractClusterInvoker<T> {
+ private Invoker<T> selectedInvoker;
+
+ public MockClusterInvoker(Directory<T> directory) {
+ super(directory);
+ }
+
+ public MockClusterInvoker(Directory<T> directory, URL url) {
+ super(directory, url);
+ }
+
+ @Override
+ protected Result doInvoke(Invocation invocation, List<Invoker<T>> invokers,
+ LoadBalance loadbalance) throws RpcException {
+ Invoker<T> invoker = select(loadbalance, invocation, invokers, null);
+ selectedInvoker = invoker;
+ return null;
+ }
+
+ public Invoker<T> getSelectedInvoker() {
+ return selectedInvoker;
+ }
+ }
+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/support/AbstractClusterInvokerTest.java b/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/support/AbstractClusterInvokerTest.java
new file mode 100644
index 0000000..48fb483
--- /dev/null
+++ b/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/support/AbstractClusterInvokerTest.java
@@ -0,0 +1,405 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.cluster.support;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicLong;
+
+import junit.framework.Assert;
+
+import org.easymock.EasyMock;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import com.alibaba.dubbo.common.ExtensionLoader;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.Result;
+import com.alibaba.dubbo.rpc.RpcConstants;
+import com.alibaba.dubbo.rpc.RpcException;
+import com.alibaba.dubbo.rpc.cluster.Directory;
+import com.alibaba.dubbo.rpc.cluster.LoadBalance;
+import com.alibaba.dubbo.rpc.cluster.loadbalance.LeastActiveLoadBalance;
+import com.alibaba.dubbo.rpc.cluster.loadbalance.RandomLoadBalance;
+import com.alibaba.dubbo.rpc.cluster.loadbalance.RoundRobinLoadBalance;
+
+/**
+ * AbstractClusterInvokerTest
+ * @author chao.liuc
+ *
+ */
+@SuppressWarnings("rawtypes")
+public class AbstractClusterInvokerTest {
+ List<Invoker<AbstractClusterInvokerTest>> invokers = new ArrayList<Invoker<AbstractClusterInvokerTest>>();
+ List<Invoker<AbstractClusterInvokerTest>> selectedInvokers = new ArrayList<Invoker<AbstractClusterInvokerTest>>();
+ AbstractClusterInvoker<AbstractClusterInvokerTest> cluster;
+ AbstractClusterInvoker<AbstractClusterInvokerTest> cluster_nocheck;
+ Directory<AbstractClusterInvokerTest> dic ;
+ Invocation invocation;
+ URL url = URL.valueOf("registry://localhost:9090");
+
+ Invoker<AbstractClusterInvokerTest> invoker1 ;
+ Invoker<AbstractClusterInvokerTest> invoker2 ;
+ Invoker<AbstractClusterInvokerTest> invoker3 ;
+ Invoker<AbstractClusterInvokerTest> invoker4 ;
+ Invoker<AbstractClusterInvokerTest> invoker5 ;
+
+
+ @BeforeClass
+ public static void setUpBeforeClass() throws Exception {
+ }
+ @SuppressWarnings({ "unchecked" })
+ @Before
+ public void setUp() throws Exception {
+ dic = EasyMock.createMock(Directory.class);
+
+ invoker1 = EasyMock.createMock(Invoker.class);
+ invoker2 = EasyMock.createMock(Invoker.class);
+ invoker3 = EasyMock.createMock(Invoker.class);
+ invoker4 = EasyMock.createMock(Invoker.class);
+ invoker5 = EasyMock.createMock(Invoker.class);
+
+ URL turl = URL.valueOf("test://test:11/test");
+
+ EasyMock.expect(invoker1.isAvailable()).andReturn(false).anyTimes();
+ EasyMock.expect(invoker1.getInterface()).andReturn(AbstractClusterInvokerTest.class).anyTimes();
+ EasyMock.expect(invoker1.getUrl()).andReturn(turl.addParameter("name", "invoker1")).anyTimes();
+
+ EasyMock.expect(invoker2.isAvailable()).andReturn(true).anyTimes();
+ EasyMock.expect(invoker2.getInterface()).andReturn(AbstractClusterInvokerTest.class).anyTimes();
+ EasyMock.expect(invoker2.getUrl()).andReturn(turl.addParameter("name", "invoker2")).anyTimes();
+
+ EasyMock.expect(invoker3.isAvailable()).andReturn(false).anyTimes();
+ EasyMock.expect(invoker3.getInterface()).andReturn(AbstractClusterInvokerTest.class).anyTimes();
+ EasyMock.expect(invoker3.getUrl()).andReturn(turl.addParameter("name", "invoker3")).anyTimes();
+
+ EasyMock.expect(invoker4.isAvailable()).andReturn(true).anyTimes();
+ EasyMock.expect(invoker4.getInterface()).andReturn(AbstractClusterInvokerTest.class).anyTimes();
+ EasyMock.expect(invoker4.getUrl()).andReturn(turl.addParameter("name", "invoker4")).anyTimes();
+
+ EasyMock.expect(invoker5.isAvailable()).andReturn(false).anyTimes();
+ EasyMock.expect(invoker5.getInterface()).andReturn(AbstractClusterInvokerTest.class).anyTimes();
+ EasyMock.expect(invoker5.getUrl()).andReturn(turl.addParameter("name", "invoker5")).anyTimes();
+
+ EasyMock.expect(dic.getUrl()).andReturn(url).anyTimes();
+
+ invocation = EasyMock.createMock(Invocation.class);
+ EasyMock.expect(invocation.getMethodName()).andReturn("method1").anyTimes();
+ EasyMock.replay(dic,invoker1,invoker2,invoker3,invoker4,invoker5,invocation);
+
+ cluster = new AbstractClusterInvoker(dic) {
+ @Override
+ protected Result doInvoke(Invocation invocation, List invokers, LoadBalance loadbalance)
+ throws RpcException {
+ return null;
+ }
+ };
+
+ cluster_nocheck = new AbstractClusterInvoker(dic,url.addParameterIfAbsent(RpcConstants.CLUSTER_AVAILABLE_CHECK_KEY, Boolean.FALSE.toString())) {
+ @Override
+ protected Result doInvoke(Invocation invocation, List invokers, LoadBalance loadbalance)
+ throws RpcException {
+ return null;
+ }
+ };
+ }
+
+ @Test
+ public void testSelect_Invokersize0() throws Exception {
+ {
+ Invoker invoker = cluster.select(null,null,null,null);
+ Assert.assertEquals(null, invoker);
+ }
+ {
+ invokers.clear();
+ selectedInvokers.clear();
+ Invoker invoker = cluster.select(null,null,invokers,null);
+ Assert.assertEquals(null, invoker);
+ }
+ }
+
+ @Test
+ public void testSelect_Invokersize1() throws Exception {
+ invokers.clear();
+ invokers.add(invoker1);
+ Invoker invoker = cluster.select(null,null,invokers,null);
+ Assert.assertEquals(invoker1, invoker);
+ }
+
+ @Test
+ public void testSelect_Invokersize2AndselectNotNull() throws Exception {
+ invokers.clear();
+ invokers.add(invoker1);
+ invokers.add(invoker2);
+ {
+ selectedInvokers.clear();
+ selectedInvokers.add(invoker1);
+ Invoker invoker = cluster.select(null,null,invokers,selectedInvokers);
+ Assert.assertEquals(invoker2, invoker);
+ }
+ {
+ selectedInvokers.clear();
+ selectedInvokers.add(invoker2);
+ Invoker invoker = cluster.select(null,null,invokers,selectedInvokers);
+ Assert.assertEquals(invoker1, invoker);
+ }
+ }
+
+ @Test
+ public void testSelect_multiInvokers() throws Exception {
+ testSelect_multiInvokers( RoundRobinLoadBalance.NAME);
+ testSelect_multiInvokers( LeastActiveLoadBalance.NAME);
+ testSelect_multiInvokers( RandomLoadBalance.NAME);
+ }
+
+ @Test
+ public void testCloseAvailablecheck(){
+ LoadBalance lb = EasyMock.createMock(LoadBalance.class);
+ EasyMock.expect(lb.select(invokers, invocation)).andReturn(invoker1);
+ EasyMock.replay(lb);
+ initlistsize5();
+
+ Invoker sinvoker = cluster_nocheck.select(lb, invocation, invokers, selectedInvokers);
+ Assert.assertEquals(false,sinvoker.isAvailable());
+ Assert.assertEquals(invoker1,sinvoker);
+
+ }
+
+ @Test
+ public void testDonotSelectAgainAndNoCheckAvailable(){
+
+ LoadBalance lb = ExtensionLoader.getExtensionLoader(LoadBalance.class).getExtension(RoundRobinLoadBalance.NAME);
+ initlistsize5();
+ {
+ //边界测试.
+ selectedInvokers.clear();
+ selectedInvokers.add(invoker2);
+ selectedInvokers.add(invoker3);
+ selectedInvokers.add(invoker4);
+ selectedInvokers.add(invoker5);
+ Invoker sinvoker = cluster_nocheck.select(lb, invocation, invokers, selectedInvokers);
+ Assert.assertSame(invoker1, sinvoker);
+ }
+ {
+ //边界测试.
+ selectedInvokers.clear();
+ selectedInvokers.add(invoker1);
+ selectedInvokers.add(invoker3);
+ selectedInvokers.add(invoker4);
+ selectedInvokers.add(invoker5);
+ Invoker sinvoker = cluster_nocheck.select(lb, invocation, invokers, selectedInvokers);
+ Assert.assertSame(invoker2, sinvoker);
+ }
+ {
+ //边界测试.
+ selectedInvokers.clear();
+ selectedInvokers.add(invoker1);
+ selectedInvokers.add(invoker2);
+ selectedInvokers.add(invoker4);
+ selectedInvokers.add(invoker5);
+ Invoker sinvoker = cluster_nocheck.select(lb, invocation, invokers, selectedInvokers );
+ Assert.assertSame(invoker3, sinvoker);
+ }
+ {
+ //边界测试.
+ selectedInvokers.clear();
+ selectedInvokers.add(invoker1);
+ selectedInvokers.add(invoker2);
+ selectedInvokers.add(invoker3);
+ selectedInvokers.add(invoker4);
+ Invoker sinvoker = cluster_nocheck.select(lb, invocation, invokers, selectedInvokers );
+ Assert.assertSame(invoker5, sinvoker);
+ }
+ {
+ //边界测试.
+ selectedInvokers.clear();
+ selectedInvokers.add(invoker1);
+ selectedInvokers.add(invoker2);
+ selectedInvokers.add(invoker3);
+ selectedInvokers.add(invoker4);
+ selectedInvokers.add(invoker5);
+ Invoker sinvoker = cluster_nocheck.select(lb, invocation, invokers, selectedInvokers);
+ Assert.assertTrue(invokers.contains(sinvoker));
+ }
+
+ }
+
+ @Test
+ public void testSelectAgainAndCheckAvailable(){
+
+ LoadBalance lb = ExtensionLoader.getExtensionLoader(LoadBalance.class).getExtension(RoundRobinLoadBalance.NAME);
+ initlistsize5();
+ {
+ //边界测试.
+ selectedInvokers.clear();
+ selectedInvokers.add(invoker1);
+ selectedInvokers.add(invoker2);
+ selectedInvokers.add(invoker3);
+ selectedInvokers.add(invoker5);
+ Invoker sinvoker = cluster.select(lb, invocation, invokers, selectedInvokers);
+ Assert.assertTrue(sinvoker == invoker4 );
+ }
+ {
+ //边界测试.
+ selectedInvokers.clear();
+ selectedInvokers.add(invoker2);
+ selectedInvokers.add(invoker3);
+ selectedInvokers.add(invoker4);
+ selectedInvokers.add(invoker5);
+ Invoker sinvoker = cluster.select(lb, invocation, invokers, selectedInvokers);
+ Assert.assertTrue(sinvoker == invoker2 || sinvoker == invoker4);
+ }
+ {
+ //边界测试.
+ for(int i=0;i<100;i++){
+ selectedInvokers.clear();
+ Invoker sinvoker = cluster.select(lb, invocation, invokers, selectedInvokers);
+ Assert.assertTrue(sinvoker == invoker2 || sinvoker == invoker4);
+ }
+ }
+ {
+ //边界测试.
+ for(int i=0;i<100;i++){
+ selectedInvokers.clear();
+ selectedInvokers.add(invoker1);
+ selectedInvokers.add(invoker3);
+ selectedInvokers.add(invoker5);
+ Invoker sinvoker = cluster.select(lb, invocation, invokers, selectedInvokers);
+ Assert.assertTrue(sinvoker == invoker2 || sinvoker == invoker4);
+ }
+ }
+ {
+ //边界测试.
+ for(int i=0;i<100;i++){
+ selectedInvokers.clear();
+ selectedInvokers.add(invoker1);
+ selectedInvokers.add(invoker3);
+ selectedInvokers.add(invoker2);
+ selectedInvokers.add(invoker4);
+ selectedInvokers.add(invoker5);
+ Invoker sinvoker = cluster.select(lb, invocation, invokers, selectedInvokers);
+ Assert.assertTrue(sinvoker == invoker2 || sinvoker == invoker4);
+ }
+ }
+ }
+
+
+ public void testSelect_multiInvokers(String lbname) throws Exception {
+
+ int min=1000,max=5000;
+ Double d = (Math.random()*(max-min+1)+min);
+ int runs = d.intValue();
+ Assert.assertTrue(runs>min);
+ LoadBalance lb = ExtensionLoader.getExtensionLoader(LoadBalance.class).getExtension(lbname);
+ initlistsize5();
+ for(int i=0;i<runs;i++){
+ Invoker sinvoker = cluster.select(lb, invocation, invokers, selectedInvokers);
+ Assert.assertEquals(true,sinvoker.isAvailable());
+ }
+ for(int i=0;i<runs;i++){
+ selectedInvokers.clear();
+ selectedInvokers.add(invoker1);
+ Invoker sinvoker = cluster.select(lb, invocation, invokers, selectedInvokers);
+ Assert.assertEquals(true,sinvoker.isAvailable());
+ }
+ for(int i=0;i<runs;i++){
+ selectedInvokers.clear();
+ selectedInvokers.add(invoker2);
+ Invoker sinvoker = cluster.select(lb, invocation, invokers, selectedInvokers);
+ Assert.assertEquals(true,sinvoker.isAvailable());
+ }
+ for(int i=0;i<runs;i++){
+ selectedInvokers.clear();
+ selectedInvokers.add(invoker2);
+ selectedInvokers.add(invoker4);
+ Invoker sinvoker = cluster.select(lb, invocation, invokers, selectedInvokers);
+ Assert.assertEquals(true,sinvoker.isAvailable());
+ }
+ for(int i=0;i<runs;i++){
+ selectedInvokers.clear();
+ selectedInvokers.add(invoker1);
+ selectedInvokers.add(invoker3);
+ selectedInvokers.add(invoker5);
+ Invoker sinvoker = cluster.select(lb, invocation, invokers, selectedInvokers);
+ Assert.assertEquals(true,sinvoker.isAvailable());
+ }
+ for(int i=0;i<runs;i++){
+ selectedInvokers.clear();
+ selectedInvokers.add(invoker1);
+ selectedInvokers.add(invoker2);
+ selectedInvokers.add(invoker3);
+ Invoker sinvoker = cluster.select(lb, invocation, invokers, selectedInvokers);
+ Assert.assertEquals(true,sinvoker.isAvailable());
+ }
+ }
+
+ /**
+ * 测试均衡.
+ */
+ @Test
+ public void testSelectBalance(){
+
+ LoadBalance lb = ExtensionLoader.getExtensionLoader(LoadBalance.class).getExtension(RoundRobinLoadBalance.NAME);
+ initlistsize5();
+
+ Map<Invoker,AtomicLong> counter = new ConcurrentHashMap<Invoker,AtomicLong>();
+ for(Invoker invoker :invokers){
+ counter.put(invoker, new AtomicLong(0));
+ }
+ int runs = 1000;
+ for(int i=0;i<runs;i++){
+ selectedInvokers.clear();
+ Invoker sinvoker = cluster.select(lb, invocation, invokers, selectedInvokers);
+ counter.get(sinvoker).incrementAndGet();
+ }
+
+ for (Invoker minvoker :counter.keySet() ){
+ Long count = counter.get(minvoker).get();
+// System.out.println(count);
+ if(minvoker.isAvailable())
+ Assert.assertTrue("count should > avg", count>runs/invokers.size());
+ }
+
+ Assert.assertEquals(runs, counter.get(invoker2).get()+counter.get(invoker4).get());;
+
+ }
+
+ private void initlistsize5(){
+ invokers.clear();
+ selectedInvokers.clear();//需要清除,之前的测试中会主动将正确的invoker2放入其中.
+ invokers.add(invoker1);
+ invokers.add(invoker2);
+ invokers.add(invoker3);
+ invokers.add(invoker4);
+ invokers.add(invoker5);
+ }
+ @Test
+ public void testInvoke() {
+// fail("Not yet implemented");
+ }
+
+ @Test
+ public void testDoInvoke() {
+// fail("Not yet implemented");
+ }
+
+
+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/support/FailSafeClusterInvokerTest.java b/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/support/FailSafeClusterInvokerTest.java
new file mode 100644
index 0000000..c5f9f0f
--- /dev/null
+++ b/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/support/FailSafeClusterInvokerTest.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.cluster.support;
+
+import static org.junit.Assert.assertEquals;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import junit.framework.Assert;
+
+import org.easymock.EasyMock;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.utils.LogUtil;
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.Result;
+import com.alibaba.dubbo.rpc.RpcContext;
+import com.alibaba.dubbo.rpc.RpcResult;
+import com.alibaba.dubbo.rpc.cluster.Directory;
+import com.alibaba.dubbo.rpc.cluster.filter.DemoService;
+
+/**
+ * FailfastClusterInvokerTest
+ * @author tony.chenl
+ *
+ */
+@SuppressWarnings("unchecked")
+public class FailSafeClusterInvokerTest {
+ List<Invoker<DemoService>> invokers = new ArrayList<Invoker<DemoService>>();
+ URL url = URL.valueOf("test://test:11/test");
+ Invoker<DemoService> invoker = EasyMock.createMock(Invoker.class);
+ Invocation invocation;
+ Directory<DemoService> dic ;
+ Result result = new RpcResult();
+ /**
+ * @throws java.lang.Exception
+ */
+
+ @Before
+ public void setUp() throws Exception {
+
+ dic = EasyMock.createMock(Directory.class);
+ invocation = EasyMock.createMock(Invocation.class);
+
+ EasyMock.expect(dic.getUrl()).andReturn(url).anyTimes();
+ EasyMock.expect(dic.list(invocation)).andReturn(invokers).anyTimes();
+ EasyMock.expect(dic.getInterface()).andReturn(DemoService.class).anyTimes();
+
+ EasyMock.expect(invocation.getMethodName()).andReturn("method1").anyTimes();
+ EasyMock.replay(dic,invocation);
+
+ invokers.add(invoker);
+ }
+
+ @After
+ public void tearDown(){
+ EasyMock.verify(invoker,dic,invocation);
+
+ }
+ private void resetInvokerToException(){
+ EasyMock.reset(invoker);
+ EasyMock.expect(invoker.invoke(invocation)).andThrow(new RuntimeException()).anyTimes();
+ EasyMock.expect(invoker.getUrl()).andReturn(url).anyTimes();
+ EasyMock.expect(invoker.getInterface()).andReturn(DemoService.class).anyTimes();
+ EasyMock.replay(invoker);
+ }
+ private void resetInvokerToNoException(){
+ EasyMock.reset(invoker);
+ EasyMock.expect(invoker.invoke(invocation)).andReturn(result).anyTimes();
+ EasyMock.expect(invoker.getUrl()).andReturn(url).anyTimes();
+ EasyMock.expect(invoker.getInterface()).andReturn(DemoService.class).anyTimes();
+ EasyMock.replay(invoker);
+ }
+
+ //TODO assert error log
+ @Test
+ public void testInvokeExceptoin() {
+ resetInvokerToException();
+ FailsafeClusterInvoker<DemoService> invoker = new FailsafeClusterInvoker<DemoService>(dic);
+ invoker.invoke(invocation);
+ Assert.assertNull(RpcContext.getContext().getInvoker());
+ }
+
+ @Test()
+ public void testInvokeNoExceptoin() {
+
+ resetInvokerToNoException();
+
+ FailsafeClusterInvoker<DemoService> invoker = new FailsafeClusterInvoker<DemoService>(dic);
+ Result ret = invoker.invoke(invocation);
+ Assert.assertSame(result, ret);
+ }
+
+ @Test()
+ public void testNoInvoke() {
+ dic = EasyMock.createMock(Directory.class);
+ invocation = EasyMock.createMock(Invocation.class);
+
+ EasyMock.expect(dic.getUrl()).andReturn(url).anyTimes();
+ EasyMock.expect(dic.list(invocation)).andReturn(null).anyTimes();
+ EasyMock.expect(dic.getInterface()).andReturn(DemoService.class).anyTimes();
+
+ EasyMock.expect(invocation.getMethodName()).andReturn("method1").anyTimes();
+ EasyMock.replay(dic,invocation);
+
+ resetInvokerToNoException();
+
+ FailsafeClusterInvoker<DemoService> invoker = new FailsafeClusterInvoker<DemoService>(dic);
+ LogUtil.start();
+ invoker.invoke(invocation);
+ assertEquals(1,LogUtil.findMessage("No provider"));
+ LogUtil.stop();
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/support/FailbackClusterInvokerTest.java b/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/support/FailbackClusterInvokerTest.java
new file mode 100644
index 0000000..5d558c8
--- /dev/null
+++ b/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/support/FailbackClusterInvokerTest.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.cluster.support;
+
+import static org.junit.Assert.assertEquals;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import junit.framework.Assert;
+
+import org.easymock.EasyMock;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.utils.LogUtil;
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.Result;
+import com.alibaba.dubbo.rpc.RpcContext;
+import com.alibaba.dubbo.rpc.RpcResult;
+import com.alibaba.dubbo.rpc.cluster.Directory;
+
+/**
+ * FailbackClusterInvokerTest
+ *
+ * @author tony.chenl
+ */
+@SuppressWarnings("unchecked")
+public class FailbackClusterInvokerTest {
+
+ List<Invoker<FailbackClusterInvokerTest>> invokers = new ArrayList<Invoker<FailbackClusterInvokerTest>>();
+ URL url = URL.valueOf("test://test:11/test");
+ Invoker<FailbackClusterInvokerTest> invoker = EasyMock.createMock(Invoker.class);
+ Invocation invocation;
+ Directory<FailbackClusterInvokerTest> dic;
+ Result result = new RpcResult();
+
+ /**
+ * @throws java.lang.Exception
+ */
+
+ @Before
+ public void setUp() throws Exception {
+
+ dic = EasyMock.createMock(Directory.class);
+ invocation = EasyMock.createMock(Invocation.class);
+
+ EasyMock.expect(dic.getUrl()).andReturn(url).anyTimes();
+ EasyMock.expect(dic.list(invocation)).andReturn(invokers).anyTimes();
+ EasyMock.expect(dic.getInterface()).andReturn(FailbackClusterInvokerTest.class).anyTimes();
+
+ EasyMock.expect(invocation.getMethodName()).andReturn("method1").anyTimes();
+ EasyMock.replay(dic, invocation);
+
+ invokers.add(invoker);
+ }
+
+ @After
+ public void tearDown() {
+ EasyMock.verify(invoker, dic, invocation);
+
+ }
+
+ private void resetInvokerToException() {
+ EasyMock.reset(invoker);
+ EasyMock.expect(invoker.invoke(invocation)).andThrow(new RuntimeException()).anyTimes();
+ EasyMock.expect(invoker.getUrl()).andReturn(url).anyTimes();
+ EasyMock.expect(invoker.getInterface()).andReturn(FailbackClusterInvokerTest.class).anyTimes();
+ EasyMock.replay(invoker);
+ }
+
+ private void resetInvokerToNoException() {
+ EasyMock.reset(invoker);
+ EasyMock.expect(invoker.invoke(invocation)).andReturn(result).anyTimes();
+ EasyMock.expect(invoker.getUrl()).andReturn(url).anyTimes();
+ EasyMock.expect(invoker.getInterface()).andReturn(FailbackClusterInvokerTest.class).anyTimes();
+ EasyMock.replay(invoker);
+ }
+
+ @Test
+ public void testInvokeExceptoin() {
+ resetInvokerToException();
+ FailbackClusterInvoker<FailbackClusterInvokerTest> invoker = new FailbackClusterInvoker<FailbackClusterInvokerTest>(
+ dic);
+ invoker.invoke(invocation);
+ Assert.assertNull(RpcContext.getContext().getInvoker());
+ }
+
+ @Test()
+ public void testInvokeNoExceptoin() {
+
+ resetInvokerToNoException();
+
+ FailbackClusterInvoker<FailbackClusterInvokerTest> invoker = new FailbackClusterInvoker<FailbackClusterInvokerTest>(
+ dic);
+ Result ret = invoker.invoke(invocation);
+ Assert.assertSame(result, ret);
+ }
+
+ @Test()
+ public void testNoInvoke() {
+ dic = EasyMock.createMock(Directory.class);
+ invocation = EasyMock.createMock(Invocation.class);
+
+ EasyMock.expect(dic.getUrl()).andReturn(url).anyTimes();
+ EasyMock.expect(dic.list(invocation)).andReturn(null).anyTimes();
+ EasyMock.expect(dic.getInterface()).andReturn(FailbackClusterInvokerTest.class).anyTimes();
+
+ EasyMock.expect(invocation.getMethodName()).andReturn("method1").anyTimes();
+ EasyMock.replay(dic, invocation);
+
+ invokers.add(invoker);
+
+ resetInvokerToNoException();
+
+ FailbackClusterInvoker<FailbackClusterInvokerTest> invoker = new FailbackClusterInvoker<FailbackClusterInvokerTest>(
+ dic);
+ LogUtil.start();
+ invoker.invoke(invocation);
+ assertEquals(1, LogUtil.findMessage("Failback to invoke"));
+ LogUtil.stop();
+ }
+
+ @Test()
+ public void testRetryFailed() {
+
+ resetInvokerToException();
+
+ FailbackClusterInvoker<FailbackClusterInvokerTest> invoker = new FailbackClusterInvoker<FailbackClusterInvokerTest>(
+ dic);
+ invoker.invoke(invocation);
+ Assert.assertNull(RpcContext.getContext().getInvoker());
+ invoker.retryFailed();// when retry the invoker which get from failed map already is not the mocked invoker,so
+ // it can be invoke successfully
+ }
+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/support/FailfastClusterInvokerTest.java b/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/support/FailfastClusterInvokerTest.java
new file mode 100644
index 0000000..a06b6df
--- /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.fail;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import junit.framework.Assert;
+
+import org.easymock.EasyMock;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.Result;
+import com.alibaba.dubbo.rpc.RpcContext;
+import com.alibaba.dubbo.rpc.RpcException;
+import com.alibaba.dubbo.rpc.RpcResult;
+import com.alibaba.dubbo.rpc.cluster.Directory;
+
+/**
+ * FailfastClusterInvokerTest
+ * @author liuchao
+ *
+ */
+@SuppressWarnings("unchecked")
+public class FailfastClusterInvokerTest {
+ List<Invoker<FailfastClusterInvokerTest>> invokers = new ArrayList<Invoker<FailfastClusterInvokerTest>>();
+ URL url = URL.valueOf("test://test:11/test");
+ Invoker<FailfastClusterInvokerTest> invoker1 = EasyMock.createMock(Invoker.class);
+ Invocation invocation;
+ Directory<FailfastClusterInvokerTest> dic ;
+ Result result = new RpcResult();
+ /**
+ * @throws java.lang.Exception
+ */
+
+ @Before
+ public void setUp() throws Exception {
+
+ dic = EasyMock.createMock(Directory.class);
+ invocation = EasyMock.createMock(Invocation.class);
+
+ EasyMock.expect(dic.getUrl()).andReturn(url).anyTimes();
+ EasyMock.expect(dic.list(invocation)).andReturn(invokers).anyTimes();
+ EasyMock.expect(dic.getInterface()).andReturn(FailfastClusterInvokerTest.class).anyTimes();
+
+ EasyMock.expect(invocation.getMethodName()).andReturn("method1").anyTimes();
+ EasyMock.replay(dic,invocation);
+
+ invokers.add(invoker1);
+ }
+
+ @After
+ public void tearDown(){
+ EasyMock.verify(invoker1,dic,invocation);
+
+ }
+ private void resetInvoker1ToException(){
+ EasyMock.reset(invoker1);
+ EasyMock.expect(invoker1.invoke(invocation)).andThrow(new RuntimeException()).anyTimes();
+ EasyMock.expect(invoker1.getUrl()).andReturn(url).anyTimes();
+ EasyMock.expect(invoker1.getInterface()).andReturn(FailfastClusterInvokerTest.class).anyTimes();
+ EasyMock.replay(invoker1);
+ }
+ private void resetInvoker1ToNoException(){
+ EasyMock.reset(invoker1);
+ EasyMock.expect(invoker1.invoke(invocation)).andReturn(result).anyTimes();
+ EasyMock.expect(invoker1.getUrl()).andReturn(url).anyTimes();
+ EasyMock.expect(invoker1.getInterface()).andReturn(FailfastClusterInvokerTest.class).anyTimes();
+ EasyMock.replay(invoker1);
+ }
+
+ @Test(expected = RpcException.class)
+ public void testInvokeExceptoin() {
+ resetInvoker1ToException();
+ FailfastClusterInvoker<FailfastClusterInvokerTest> invoker = new FailfastClusterInvoker<FailfastClusterInvokerTest>(dic);
+ invoker.invoke(invocation);
+ Assert.assertSame(invoker1, RpcContext.getContext().getInvoker());
+ }
+
+ @Test()
+ public void testInvokeNoExceptoin() {
+
+ resetInvoker1ToNoException();
+
+ FailfastClusterInvoker<FailfastClusterInvokerTest> invoker = new FailfastClusterInvoker<FailfastClusterInvokerTest>(dic);
+ Result ret = invoker.invoke(invocation);
+ Assert.assertSame(result, ret);
+ }
+
+ @Test()
+ public void testNoInvoke() {
+ dic = EasyMock.createMock(Directory.class);
+ invocation = EasyMock.createMock(Invocation.class);
+
+ EasyMock.expect(dic.getUrl()).andReturn(url).anyTimes();
+ EasyMock.expect(dic.list(invocation)).andReturn(null).anyTimes();
+ EasyMock.expect(dic.getInterface()).andReturn(FailfastClusterInvokerTest.class).anyTimes();
+
+ EasyMock.expect(invocation.getMethodName()).andReturn("method1").anyTimes();
+ EasyMock.replay(dic,invocation);
+
+ invokers.add(invoker1);
+
+ resetInvoker1ToNoException();
+
+ FailfastClusterInvoker<FailfastClusterInvokerTest> invoker = new FailfastClusterInvoker<FailfastClusterInvokerTest>(dic);
+ try {
+ invoker.invoke(invocation);
+ fail();
+ } catch (RpcException expected) {
+ }
+ }
+
+
+}
\ 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..706e554
--- /dev/null
+++ b/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/support/FailoverClusterInvokerTest.java
@@ -0,0 +1,175 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.cluster.support;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.easymock.EasyMock;
+import org.junit.Before;
+import org.junit.Test;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.Result;
+import com.alibaba.dubbo.rpc.RpcException;
+import com.alibaba.dubbo.rpc.RpcResult;
+import com.alibaba.dubbo.rpc.cluster.Directory;
+
+/**
+ * FailoverClusterInvokerTest
+ * @author liuchao
+ *
+ */
+@SuppressWarnings("unchecked")
+public class FailoverClusterInvokerTest {
+ List<Invoker<FailoverClusterInvokerTest>> invokers = new ArrayList<Invoker<FailoverClusterInvokerTest>>();
+ int retries = 5;
+ URL url = URL.valueOf("test://test:11/test?retries="+retries);
+ Invoker<FailoverClusterInvokerTest> invoker1 = EasyMock.createMock(Invoker.class);
+ Invoker<FailoverClusterInvokerTest> invoker2 = EasyMock.createMock(Invoker.class);
+ Invocation invocation;
+ Directory<FailoverClusterInvokerTest> dic ;
+ Result result = new RpcResult();
+ /**
+ * @throws java.lang.Exception
+ */
+
+ @Before
+ public void setUp() throws Exception {
+
+ dic = EasyMock.createMock(Directory.class);
+ invocation = EasyMock.createMock(Invocation.class);
+
+ EasyMock.expect(dic.getUrl()).andReturn(url).anyTimes();
+ EasyMock.expect(dic.list(invocation)).andReturn(invokers).anyTimes();
+ EasyMock.expect(dic.getInterface()).andReturn(FailoverClusterInvokerTest.class).anyTimes();
+
+ EasyMock.expect(invocation.getMethodName()).andReturn("method1").anyTimes();
+ EasyMock.replay(dic,invocation);
+
+ invokers.add(invoker1);
+ invokers.add(invoker2);
+ }
+
+
+ @Test
+ public void testInvokeWithRuntimeException() {
+ EasyMock.reset(invoker1);
+ EasyMock.expect(invoker1.invoke(invocation)).andThrow(new RuntimeException()).anyTimes();
+ EasyMock.expect(invoker1.isAvailable()).andReturn(true).anyTimes();
+ EasyMock.expect(invoker1.getUrl()).andReturn(url).anyTimes();
+ EasyMock.expect(invoker1.getInterface()).andReturn(FailoverClusterInvokerTest.class).anyTimes();
+ EasyMock.replay(invoker1);
+
+ EasyMock.reset(invoker2);
+ EasyMock.expect(invoker2.invoke(invocation)).andThrow(new RuntimeException()).anyTimes();
+ EasyMock.expect(invoker2.isAvailable()).andReturn(true).anyTimes();
+ EasyMock.expect(invoker2.getUrl()).andReturn(url).anyTimes();
+ EasyMock.expect(invoker2.getInterface()).andReturn(FailoverClusterInvokerTest.class).anyTimes();
+ EasyMock.replay(invoker2);
+
+ FailoverClusterInvoker<FailoverClusterInvokerTest> invoker = new FailoverClusterInvoker<FailoverClusterInvokerTest>(dic);
+ try {
+ invoker.invoke(invocation);
+ fail();
+ } catch (RpcException expected) {
+ assertEquals(0,expected.getCode());
+ }
+ }
+
+ @Test()
+ public void testInvokeWithRPCException() {
+
+ EasyMock.reset(invoker1);
+ EasyMock.expect(invoker1.invoke(invocation)).andThrow(new RpcException()).anyTimes();
+ EasyMock.expect(invoker1.isAvailable()).andReturn(true).anyTimes();
+ EasyMock.expect(invoker1.getUrl()).andReturn(url).anyTimes();
+ EasyMock.expect(invoker1.getInterface()).andReturn(FailoverClusterInvokerTest.class).anyTimes();
+ EasyMock.replay(invoker1);
+
+ EasyMock.reset(invoker2);
+ EasyMock.expect(invoker2.invoke(invocation)).andReturn(result).anyTimes();
+ EasyMock.expect(invoker2.isAvailable()).andReturn(true).anyTimes();
+ EasyMock.expect(invoker2.getUrl()).andReturn(url).anyTimes();
+ EasyMock.expect(invoker2.getInterface()).andReturn(FailoverClusterInvokerTest.class).anyTimes();
+ EasyMock.replay(invoker2);
+
+ FailoverClusterInvoker<FailoverClusterInvokerTest> invoker = new FailoverClusterInvoker<FailoverClusterInvokerTest>(dic);
+ for(int i=0;i<100;i++){
+ Result ret = invoker.invoke(invocation);
+ assertSame(result, ret);
+ }
+ }
+
+ @Test()
+ public void testInvoke_retryTimes() {
+
+ EasyMock.reset(invoker1);
+ EasyMock.expect(invoker1.invoke(invocation)).andThrow(new RpcException(RpcException.TIMEOUT_EXCEPTION)).anyTimes();
+ EasyMock.expect(invoker1.isAvailable()).andReturn(false).anyTimes();
+ EasyMock.expect(invoker1.getUrl()).andReturn(url).anyTimes();
+ EasyMock.expect(invoker1.getInterface()).andReturn(FailoverClusterInvokerTest.class).anyTimes();
+ EasyMock.replay(invoker1);
+
+ EasyMock.reset(invoker2);
+ EasyMock.expect(invoker2.invoke(invocation)).andThrow(new RpcException()).anyTimes();
+ EasyMock.expect(invoker2.isAvailable()).andReturn(false).anyTimes();
+ EasyMock.expect(invoker2.getUrl()).andReturn(url).anyTimes();
+ EasyMock.expect(invoker2.getInterface()).andReturn(FailoverClusterInvokerTest.class).anyTimes();
+ EasyMock.replay(invoker2);
+
+ FailoverClusterInvoker<FailoverClusterInvokerTest> invoker = new FailoverClusterInvoker<FailoverClusterInvokerTest>(dic);
+ try{
+ Result ret = invoker.invoke(invocation);
+ assertSame(result, ret);
+ fail();
+ }catch (RpcException expected) {
+ assertTrue(expected.isTimeout());
+ assertTrue(expected.getMessage().indexOf((retries+1)+" times")>0);
+ }
+ }
+
+ @Test()
+ public void testNoInvoke() {
+ dic = EasyMock.createMock(Directory.class);
+ invocation = EasyMock.createMock(Invocation.class);
+
+ EasyMock.expect(dic.getUrl()).andReturn(url).anyTimes();
+ EasyMock.expect(dic.list(invocation)).andReturn(null).anyTimes();
+ EasyMock.expect(dic.getInterface()).andReturn(FailoverClusterInvokerTest.class).anyTimes();
+
+ EasyMock.expect(invocation.getMethodName()).andReturn("method1").anyTimes();
+ EasyMock.replay(dic,invocation);
+
+ invokers.add(invoker1);
+
+
+ FailoverClusterInvoker<FailoverClusterInvokerTest> invoker = new FailoverClusterInvoker<FailoverClusterInvokerTest>(dic);
+ try {
+ invoker.invoke(invocation);
+ fail();
+ } catch (RpcException expected) {
+ expected.printStackTrace();
+ }
+ }
+}
\ 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..9b1996f
--- /dev/null
+++ b/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/support/ForkingClusterInvokerTest.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.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.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.Result;
+import com.alibaba.dubbo.rpc.RpcException;
+import com.alibaba.dubbo.rpc.RpcResult;
+import com.alibaba.dubbo.rpc.cluster.Directory;
+
+/**
+ * ForkingClusterInvokerTest
+ *
+ * @author tony.chenl
+ */
+@SuppressWarnings("unchecked")
+public class ForkingClusterInvokerTest {
+
+ List<Invoker<ForkingClusterInvokerTest>> invokers = new ArrayList<Invoker<ForkingClusterInvokerTest>>();
+ URL url = URL.valueOf("test://test:11/test?forks=2");
+ Invoker<ForkingClusterInvokerTest> invoker1 = EasyMock.createMock(Invoker.class);
+ Invoker<ForkingClusterInvokerTest> invoker2 = EasyMock.createMock(Invoker.class);
+ Invoker<ForkingClusterInvokerTest> invoker3 = EasyMock.createMock(Invoker.class);
+ Invocation invocation;
+ Directory<ForkingClusterInvokerTest> dic;
+ Result result = new RpcResult();
+
+ /**
+ * @throws java.lang.Exception
+ */
+
+ @Before
+ public void setUp() throws Exception {
+
+ dic = EasyMock.createMock(Directory.class);
+ invocation = EasyMock.createMock(Invocation.class);
+
+ EasyMock.expect(dic.getUrl()).andReturn(url).anyTimes();
+ EasyMock.expect(dic.list(invocation)).andReturn(invokers).anyTimes();
+ EasyMock.expect(dic.getInterface()).andReturn(ForkingClusterInvokerTest.class).anyTimes();
+
+ EasyMock.expect(invocation.getMethodName()).andReturn("method1").anyTimes();
+ EasyMock.replay(dic, invocation);
+
+ invokers.add(invoker1);
+ invokers.add(invoker2);
+ invokers.add(invoker3);
+
+ }
+
+ @After
+ public void tearDown() {
+ EasyMock.verify(invoker1, dic, invocation);
+
+ }
+
+ private void resetInvokerToException() {
+ EasyMock.reset(invoker1);
+ EasyMock.expect(invoker1.invoke(invocation)).andThrow(new RuntimeException()).anyTimes();
+ EasyMock.expect(invoker1.getUrl()).andReturn(url).anyTimes();
+ EasyMock.expect(invoker1.isAvailable()).andReturn(true).anyTimes();
+ EasyMock.expect(invoker1.getInterface()).andReturn(ForkingClusterInvokerTest.class).anyTimes();
+ EasyMock.replay(invoker1);
+ EasyMock.reset(invoker2);
+ EasyMock.expect(invoker2.invoke(invocation)).andThrow(new RuntimeException()).anyTimes();
+ EasyMock.expect(invoker2.getUrl()).andReturn(url).anyTimes();
+ EasyMock.expect(invoker2.isAvailable()).andReturn(true).anyTimes();
+ EasyMock.expect(invoker2.getInterface()).andReturn(ForkingClusterInvokerTest.class).anyTimes();
+ EasyMock.replay(invoker2);
+ EasyMock.reset(invoker3);
+ EasyMock.expect(invoker3.invoke(invocation)).andThrow(new RuntimeException()).anyTimes();
+ EasyMock.expect(invoker3.getUrl()).andReturn(url).anyTimes();
+ EasyMock.expect(invoker3.isAvailable()).andReturn(true).anyTimes();
+ EasyMock.expect(invoker3.getInterface()).andReturn(ForkingClusterInvokerTest.class).anyTimes();
+ EasyMock.replay(invoker3);
+ }
+
+ private void resetInvokerToNoException() {
+ EasyMock.reset(invoker1);
+ EasyMock.expect(invoker1.invoke(invocation)).andReturn(result).anyTimes();
+ EasyMock.expect(invoker1.getUrl()).andReturn(url).anyTimes();
+ EasyMock.expect(invoker1.isAvailable()).andReturn(true).anyTimes();
+ EasyMock.expect(invoker1.getInterface()).andReturn(ForkingClusterInvokerTest.class).anyTimes();
+ EasyMock.replay(invoker1);
+ EasyMock.reset(invoker2);
+ EasyMock.expect(invoker2.invoke(invocation)).andReturn(result).anyTimes();
+ EasyMock.expect(invoker2.getUrl()).andReturn(url).anyTimes();
+ EasyMock.expect(invoker2.isAvailable()).andReturn(true).anyTimes();
+ EasyMock.expect(invoker2.getInterface()).andReturn(ForkingClusterInvokerTest.class).anyTimes();
+ EasyMock.replay(invoker2);
+ EasyMock.reset(invoker3);
+ EasyMock.expect(invoker3.invoke(invocation)).andReturn(result).anyTimes();
+ EasyMock.expect(invoker3.getUrl()).andReturn(url).anyTimes();
+ EasyMock.expect(invoker3.isAvailable()).andReturn(true).anyTimes();
+ EasyMock.expect(invoker3.getInterface()).andReturn(ForkingClusterInvokerTest.class).anyTimes();
+ EasyMock.replay(invoker3);
+ }
+
+ @Test
+ public void testInvokeExceptoin() {
+ resetInvokerToException();
+ ForkingClusterInvoker<ForkingClusterInvokerTest> invoker = new ForkingClusterInvoker<ForkingClusterInvokerTest>(
+ dic);
+ Assert.assertNull(invoker.invoke(invocation).getResult());
+ Assert.assertNotNull(invoker.invoke(invocation).getException());
+ }
+
+ @Test()
+ public void testInvokeNoExceptoin() {
+
+ resetInvokerToNoException();
+
+ ForkingClusterInvoker<ForkingClusterInvokerTest> invoker = new ForkingClusterInvoker<ForkingClusterInvokerTest>(
+ dic);
+ Result ret = invoker.invoke(invocation);
+ Assert.assertSame(result, ret);
+ }
+
+ @Test()
+ public void testNoInvoke() {
+ dic = EasyMock.createMock(Directory.class);
+ invocation = EasyMock.createMock(Invocation.class);
+
+ EasyMock.expect(dic.getUrl()).andReturn(url).anyTimes();
+ EasyMock.expect(dic.list(invocation)).andReturn(null).anyTimes();
+ EasyMock.expect(dic.getInterface()).andReturn(ForkingClusterInvokerTest.class).anyTimes();
+
+ EasyMock.expect(invocation.getMethodName()).andReturn("method1").anyTimes();
+ EasyMock.replay(dic, invocation);
+
+ resetInvokerToNoException();
+
+ ForkingClusterInvoker<ForkingClusterInvokerTest> invoker = new ForkingClusterInvoker<ForkingClusterInvokerTest>(
+ dic);
+ try {
+ invoker.invoke(invocation);
+ } catch (RpcException expected) {
+ assertTrue(expected.getMessage().contains("No provider"));
+ }
+ }
+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/test/resources/com/alibaba/dubbo/rpc/cluster/router/file/availablerule.javascript b/dubbo-cluster/src/test/resources/com/alibaba/dubbo/rpc/cluster/router/file/availablerule.javascript
new file mode 100644
index 0000000..c76de32
--- /dev/null
+++ b/dubbo-cluster/src/test/resources/com/alibaba/dubbo/rpc/cluster/router/file/availablerule.javascript
@@ -0,0 +1,11 @@
+function route(invokers,invocation,context){
+ var result = new java.util.ArrayList(invokers.size());
+
+ for (i=0;i<invokers.size(); i++){
+ if (invokers.get(i).isAvailable()) {
+ result.add(invokers.get(i)) ;
+ }
+ } ;
+ return result;
+};
+route(invokers,invocation,context);
\ No newline at end of file
diff --git a/dubbo-cluster/src/test/resources/com/alibaba/dubbo/rpc/cluster/router/file/methodrule.javascript b/dubbo-cluster/src/test/resources/com/alibaba/dubbo/rpc/cluster/router/file/methodrule.javascript
new file mode 100644
index 0000000..77ff456
--- /dev/null
+++ b/dubbo-cluster/src/test/resources/com/alibaba/dubbo/rpc/cluster/router/file/methodrule.javascript
@@ -0,0 +1,10 @@
+function route(invokers,invocation,context){
+ var result = new java.util.ArrayList();
+ if (invokers.size()>1 && invocation.getMethodName() .equals("method1")) {
+ result.add(invokers.get(0)) ;
+ } else {
+ result.add(invokers.get(1)) ;
+ }
+ return result;
+};
+route(invokers,invocation,context);
\ No newline at end of file
diff --git a/dubbo-cluster/src/test/resources/com/alibaba/dubbo/rpc/cluster/router/file/notAvailablerule.javascript b/dubbo-cluster/src/test/resources/com/alibaba/dubbo/rpc/cluster/router/file/notAvailablerule.javascript
new file mode 100644
index 0000000..1fcdcae
--- /dev/null
+++ b/dubbo-cluster/src/test/resources/com/alibaba/dubbo/rpc/cluster/router/file/notAvailablerule.javascript
@@ -0,0 +1,11 @@
+function route(invokers,invocation,context){
+ var result = new java.util.ArrayList(invokers.size());
+
+ for (i=0;i<invokers.size(); i++){
+ if (!invokers.get(i).isAvailable()) {
+ result.add(invokers.get(i)) ;
+ }
+ } ;
+ return result;
+};
+route(invokers,invocation,context);
\ No newline at end of file
diff --git a/dubbo-cluster/src/test/resources/log4j.xml b/dubbo-cluster/src/test/resources/log4j.xml
new file mode 100644
index 0000000..e2c0263
--- /dev/null
+++ b/dubbo-cluster/src/test/resources/log4j.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ - Copyright 1999-2011 Alibaba Group.
+ -
+ - Licensed under the Apache License, Version 2.0 (the "License");
+ - you may not use this file except in compliance with the License.
+ - You may obtain a copy of the License at
+ -
+ - http://www.apache.org/licenses/LICENSE-2.0
+ -
+ - Unless required by applicable law or agreed to in writing, software
+ - distributed under the License is distributed on an "AS IS" BASIS,
+ - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ - See the License for the specific language governing permissions and
+ - limitations under the License.
+-->
+<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
+<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/" debug="false">
+ <appender name="DUBBO" class="com.alibaba.dubbo.common.utils.DubboAppender">
+ <layout class="org.apache.log4j.PatternLayout">
+ <param name="ConversionPattern" value="[%d{dd/MM/yy hh:mm:ss:sss z}] %t %5p %c{2}: %m%n" />
+ </layout>
+ </appender>
+ <root>
+ <level value="INFO" />
+ <appender-ref ref="DUBBO" />
+ </root>
+</log4j:configuration>
\ No newline at end of file
diff --git a/dubbo-common/pom.xml b/dubbo-common/pom.xml
new file mode 100644
index 0000000..a77b87f
--- /dev/null
+++ b/dubbo-common/pom.xml
@@ -0,0 +1,50 @@
+<!--
+ - Copyright 1999-2011 Alibaba Group.
+ -
+ - Licensed under the Apache License, Version 2.0 (the "License");
+ - you may not use this file except in compliance with the License.
+ - You may obtain a copy of the License at
+ -
+ - http://www.apache.org/licenses/LICENSE-2.0
+ -
+ - Unless required by applicable law or agreed to in writing, software
+ - distributed under the License is distributed on an "AS IS" BASIS,
+ - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ - See the License for the specific language governing permissions and
+ - limitations under the License.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>com.alibaba</groupId>
+ <artifactId>dubbo-parent</artifactId>
+ <version>2.0.8</version>
+ </parent>
+ <artifactId>dubbo-common</artifactId>
+ <packaging>jar</packaging>
+ <name>Dubbo Common Module</name>
+ <description>The common module of dubbo project</description>
+ <properties>
+ <skip_maven_deploy>false</skip_maven_deploy>
+ </properties>
+ <dependencies>
+ <dependency>
+ <groupId>log4j</groupId>
+ <artifactId>log4j</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.javassist</groupId>
+ <artifactId>javassist</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>com.alibaba</groupId>
+ <artifactId>hessian-lite</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>com.alibaba</groupId>
+ <artifactId>fastjson</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ </dependencies>
+</project>
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/Adaptive.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/Adaptive.java
new file mode 100644
index 0000000..b806424
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/Adaptive.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * 在{@link ExtensionLoader}生成Extension的Adaptive Instance时,为{@link ExtensionLoader}提供信息。
+ *
+ * @author ding.lid
+ *
+ * @see ExtensionLoader
+ * @see URL
+ * @see Extension
+ */
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.TYPE, ElementType.METHOD})
+public @interface Adaptive {
+
+ /**
+ * 从{@link URL}的Key名,对应的Value作为要Adapt成的Extension名。
+ * <p>
+ * 如果{@link URL}这些Key都没有Value,使用 用 缺省的扩展(在接口的{@link Extension}中设定的值)。<br>
+ * 比如,<code>String[] {"key1", "key2"}</code>,表示
+ * <ol>
+ * <li>先在URL上找key1的Value作为要Adapt成的Extension名;
+ * <li>key1没有Value,则使用key2的Value作为要Adapt成的Extension名。
+ * <li>key2没有Value,使用缺省的扩展。
+ * <li>如果没有设定缺省扩展,则方法调用会抛出{@link IllegalStateException}。
+ * </ol>
+ * <p>
+ * 如果不设置则缺省使用Extension接口类名的点分隔小写字串。<br>
+ * 即对于Extension接口{@code com.alibaba.dubbo.xxx.YyyInvokerWrapper}的缺省值为<code>String[] {"yyy.invoker.wrapper"}</code>
+ *
+ * @see Extension#value()
+ */
+ String[] value() default {};
+
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/Constants.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/Constants.java
new file mode 100644
index 0000000..5496b3b
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/Constants.java
@@ -0,0 +1,285 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.regex.Pattern;
+
+/**
+ * Constants
+ *
+ * @author william.liangf
+ */
+public class Constants {
+
+ public static final List<String> DEFAULT_TELNET_COMMANDS = Collections.unmodifiableList(Arrays.asList(new String[] {
+ "ls", "ps", "cd", "pwd", "invoke", "count", "trace", "status", "help", "clear", "exit","log" }));
+
+ public static final List<String> DEFAULT_CHECK_STATUSES = Collections.unmodifiableList(Arrays.asList(new String[] {
+ "server", "registry", "threadpool", "datasource", "spring", "memory", "load" }));
+
+ public static final String SENT_KEY = "sent";
+
+ public static final boolean DEFAULT_SENT = false;
+
+ public static final String REGISTRY_PROTOCOL = "registry";
+
+ public static final String $INVOKE = "$invoke";
+
+ public static final String $ECHO = "$echo";
+
+ public static final int DEFAULT_IO_THREADS = Runtime.getRuntime().availableProcessors() + 1;
+
+ public static final String DEFAULT_PROXY = "javassist";
+
+ public static final int DEFAULT_PAYLOAD = 8 * 1024 * 1024; // 8M
+
+ public static final String DEFAULT_CLUSTER = "failover";
+
+ public static final String DEFAULT_DIRECTORY = "dubbo";
+
+ public static final String DEFAULT_LOADBALANCE = "random";
+
+ public static final String DEFAULT_PROTOCOL = "dubbo";
+
+ public static final String DEFAULT_EXCHANGER = "header";
+
+ public static final String DEFAULT_TRANSPORTER = "netty";
+
+ public static final String DEFAULT_REMOTING_SERVER = "netty";
+
+ public static final String DEFAULT_REMOTING_CLIENT = "netty";
+
+ public static final String DEFAULT_REMOTING_CODEC = "dubbo";
+
+ public static final String DEFAULT_REMOTING_SERIALIZATION = "hessian2";
+
+ public static final String DEFAULT_HTTP_SERVER = "servlet";
+
+ public static final String DEFAULT_HTTP_CLIENT = "jdk";
+
+ public static final String DEFAULT_HTTP_SERIALIZATION = "json";
+
+ public static final String DEFAULT_CHARSET = "UTF-8";
+
+ public static final int DEFAULT_WEIGHT = 5;
+
+ public static final int DEFAULT_FORKS = 2;
+
+ public static final String DEFAULT_THREAD_NAME = "Dubbo";
+
+ public static final int DEFAULT_THREADS = 100;
+
+ public static final int DEFAULT_QUEUES = 0;
+
+ public static final int DEFAULT_THREAD_ALIVE = 60 * 1000;
+
+ public static final int DEFAULT_CONNECTIONS = 0;
+
+ public static final int DEFAULT_ACCEPTS = 0;
+
+ public static final int DEFAULT_IDLE_TIMEOUT = 600 * 1000;
+
+ public static final int DEFAULT_HEARTBEAT = 0;
+
+ public static final int DEFAULT_TIMEOUT = 5000;
+
+ public static final int DEFAULT_RETRIES = 2;
+
+ // default buffer size is 8k.
+ public static final int DEFAULT_BUFFER_SIZE = 8 * 1024;
+
+ public static final int MAX_BUFFER_SIZE = 16 * 1024;
+
+ public static final int MIN_BUFFER_SIZE = 1 * 1024;
+
+ public static final String REMOVE_VALUE_PREFIX = "-";
+
+ public static final String HIDE_KEY_PREFIX = ".";
+
+ public static final String DEFAULT_KEY_PREFIX = "default.";
+
+ public static final String DEFAULT_KEY = "default";
+
+ public static final String LOADBALANCE_KEY = "loadbalance";
+
+ public static final String ROUTER_KEY = "router";
+
+ public static final String CLUSTER_KEY = "cluster";
+
+ public static final String REGISTRY_KEY = "registry";
+
+ public static final String MONITOR_KEY = "monitor";
+
+ public static final String DEFAULT_REGISTRY = "dubbo";
+
+ public static final String BACKUP_KEY = "backup";
+
+ public static final String DIRECTORY_KEY = "directory";
+
+ public static final String DEPRECATED_KEY = "deprecated";
+
+ public static final String ANYHOST_KEY = "anyhost";
+
+ public static final String APPLICATION_KEY = "application";
+
+ public static final String LOCAL_KEY = "local";
+
+ public static final String STUB_KEY = "stub";
+
+ public static final String MOCK_KEY = "mock";
+
+ public static final String PROTOCOL_KEY = "protocol";
+
+ public static final String PROXY_KEY = "proxy";
+
+ public static final String WEIGHT_KEY = "weight";
+
+ public static final String FORKS_KEY = "forks";
+
+ public static final String DEFAULT_THREADPOOL = "fixed";
+
+ public static final String DEFAULT_CLIENT_THREADPOOL = "cached";
+
+ public static final String THREADPOOL_KEY = "threadpool";
+
+ public static final String THREAD_NAME_KEY = "threadname";
+
+ public static final String IO_THREADS_KEY = "iothreads";
+
+ public static final String THREADS_KEY = "threads";
+
+ public static final String QUEUES_KEY = "queues";
+
+ public static final String THREAD_ALIVE_KEY = "threadalive";
+
+ public static final String EXECUTES_KEY = "executes";
+
+ public static final String BUFFER_KEY = "buffer";
+
+ public static final String PAYLOAD_KEY = "payload";
+
+ public static final String REFERENCE_FILTER_KEY = "reference.filter";
+
+ public static final String INVOKER_LISTENER_KEY = "invoker.listener";
+
+ public static final String SERVICE_FILTER_KEY = "service.filter";
+
+ public static final String EXPORTER_LISTENER_KEY = "exporter.listener";
+
+ public static final String ACCESS_LOG_KEY = "accesslog";
+
+ public static final String ACTIVES_KEY = "actives";
+
+ public static final String CONNECTIONS_KEY = "connections";
+
+ public static final String ACCEPTS_KEY = "accepts";
+
+ public static final String IDLE_TIMEOUT_KEY = "idle.timeout";
+
+ public static final String HEARTBEAT_KEY = "heartbeat";
+
+ public static final String HEARTBEAT_TIMEOUT_KEY = "heartbeat.timeout";
+
+ public static final String CONNECT_TIMEOUT_KEY = "connect.timeout";
+
+ public static final String TIMEOUT_KEY = "timeout";
+
+ public static final String RETRIES_KEY = "retries";
+
+ public static final String CODEC_KEY = "codec";
+ public static final String DOWNSTREAM_CODEC_KEY = "codec.downstream";
+
+ public static final String SERIALIZATION_KEY = "serialization";
+
+ public static final String EXCHANGER_KEY = "exchanger";
+
+ public static final String TRANSPORTER_KEY = "transporter";
+
+ public static final String SERVER_KEY = "server";
+
+ public static final String CLIENT_KEY = "client";
+
+ public static final String ASYNC_KEY = "async";
+
+ public static final String TOKEN_KEY = "token";
+
+ public static final String METHODS_KEY = "methods";
+
+ public static final String CHARSET_KEY = "charset";
+
+ public static final String RECONNECT_KEY = "reconnect";
+
+ public static final String SEND_RECONNECT_KEY = "send.reconnect";
+
+ public static final int DEFAULT_RECONNECT_PERIOD = 2000;
+
+ public static final String SHUTDOWN_TIMEOUT_KEY = "shutdown.timeout";
+
+ public static final int DEFAULT_SHUTDOWN_TIMEOUT = 10000;
+
+ public static final String CHECK_KEY = "check";
+
+ 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 VERSION_KEY = "version";
+
+ public static final String REVISION_KEY = "revision";
+
+ public static final String DUBBO_VERSION_KEY = "dubbo";
+
+ public static final String HESSIAN_VERSION_KEY = "hessian.version";
+
+ public static final String CHANNEL_HANDLER_KEY = "channel.handler";
+
+ public static final String DEFAULT_CHANNEL_HANDLER = "default";
+
+ public static final String ANY_VALUE = "*";
+
+ public static final String COMMA_SEPARATOR = ",";
+
+ public static final Pattern COMMA_SPLIT_PATTERN = Pattern.compile("\\s*[,]+\\s*");
+
+ public static final String REGISTRY_SEPARATOR = "|";
+
+ public static final Pattern REGISTRY_SPLIT_PATTERN = Pattern.compile("\\s*[|]+\\s*");
+
+ public static final String SEMICOLON_SEPARATOR = ";";
+
+ public static final Pattern SEMICOLON_SPLIT_PATTERN = Pattern.compile("\\s*[;]+\\s*");
+
+ public static final String CONNECT_QUENE_CAPACITY = "connect.quene.capacity";
+
+ public static final String CONNECT_QUENE_WARNING_SIZE = "connect.quene.warning.size";
+
+ public static final int DEFAULT_CONNECT_QUENE_WARNING_SIZE = 1000;
+
+ private Constants(){
+ }
+
+}
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/Extension.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/Extension.java
new file mode 100644
index 0000000..95ef1e7
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/Extension.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * 扩展点实现的元信息。
+ *
+ * @author william.liangf
+ * @author ding.lid
+ */
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.TYPE})
+public @interface Extension {
+
+ /**
+ * 扩展点名称。<br>
+ *
+ * 如果注解在扩展的接口上,则缺省的扩展点。<p>
+ */
+ String value() default "";
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/ExtensionLoader.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/ExtensionLoader.java
new file mode 100644
index 0000000..a87aa90
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/ExtensionLoader.java
@@ -0,0 +1,551 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common;
+
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeSet;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.regex.Pattern;
+
+import com.alibaba.dubbo.common.bytecode.ClassGenerator;
+import com.alibaba.dubbo.common.logger.Logger;
+import com.alibaba.dubbo.common.logger.LoggerFactory;
+import com.alibaba.dubbo.common.utils.ConcurrentHashSet;
+import com.alibaba.dubbo.common.utils.Reference;
+
+/**
+ * Dubbo使用的扩展点获取。<p>
+ * <ul>
+ * <li>自动注入关联扩展点。</li>
+ * <li>自动Wrap上扩展点的Wrap类。</li>
+ * <li>缺省获得的的扩展点是一个Adaptive Instance。
+ * </ul>
+ *
+ * @see <a href="http://java.sun.com/j2se/1.5.0/docs/guide/jar/jar.html#Service%20Provider">JDK5.0的自动发现机制实现</a>
+ *
+ * @author william.liangf
+ * @author ding.lid
+ *
+ * @see Extension
+ * @see Adaptive
+ * @see Autoproxy
+ */
+public class ExtensionLoader<T> {
+
+ private static final Logger logger = LoggerFactory.getLogger(ExtensionLoader.class);
+
+ private static final String SERVICES_DIRECTORY = "META-INF/services/";
+
+ private static final Pattern NAME_SEPARATOR = Pattern.compile("\\s*[,]+\\s*");
+
+ private static final ConcurrentMap<Class<?>, ExtensionLoader<?>> EXTENSION_LOADERS = new ConcurrentHashMap<Class<?>, ExtensionLoader<?>>();
+
+ private final Class<?> type;
+
+ private final Reference<Map<String, Class<?>>> cachedClasses = new Reference<Map<String,Class<?>>>();
+
+ private final ConcurrentMap<String, Reference<Object>> cachedInstances = new ConcurrentHashMap<String, Reference<Object>>();
+
+ private volatile Class<?> cachedAdaptiveClass = null;
+
+ private final Reference<Object> cachedAdaptiveInstance = new Reference<Object>();
+ private volatile Throwable createAdaptiveInstanceError;
+
+ private Set<Class<?>> cachedWrapperClasses;
+
+ private String cachedDefaultName;
+
+ @SuppressWarnings("unchecked")
+ public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) {
+ if (type == null)
+ throw new IllegalArgumentException("Extension type == null");
+
+ ExtensionLoader<T> loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
+ if (loader == null) {
+ EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader<T>(type));
+ loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
+ }
+ return loader;
+ }
+
+ private ExtensionLoader(Class<?> type) {
+ this.type = type;
+ }
+
+ @SuppressWarnings("unchecked")
+ public T getExtension(String name) {
+ if (name == null || name.length() == 0)
+ throw new IllegalArgumentException("Extension name == null");
+ Reference<Object> reference = cachedInstances.get(name);
+ if (reference == null) {
+ cachedInstances.putIfAbsent(name, new Reference<Object>());
+ reference = cachedInstances.get(name);
+ }
+ Object instance = reference.get();
+ if (instance == null) {
+ synchronized (reference) {
+ instance = reference.get();
+ if (instance == null) {
+ instance = createExtension(name);
+ reference.set(instance);
+ }
+ }
+ }
+ return (T) instance;
+ }
+
+ /**
+ * 返回缺省的扩展,如果没有设置则返回<code>null</code>。
+ */
+ public T getDefaultExtension() {
+ getExtensionClasses();
+ if(null == cachedDefaultName || cachedDefaultName.length() == 0) {
+ return null;
+ }
+ return getExtension(cachedDefaultName);
+ }
+
+ public boolean hasExtension(String name) {
+ if (name == null || name.length() == 0)
+ throw new IllegalArgumentException("Extension name == null");
+ try {
+ return getExtensionClass(name) != null;
+ } catch (Throwable t) {
+ return false;
+ }
+ }
+
+ public Set<String> getSupportedExtensions() {
+ Map<String, Class<?>> clazzes = getExtensionClasses();
+ return Collections.unmodifiableSet(new TreeSet<String>(clazzes.keySet()));
+ }
+
+ /**
+ * 返回缺省的扩展点名,如果没有设置缺省则返回<code>null</code>。
+ */
+ public String getDefaultExtensionName() {
+ getExtensionClasses();
+ return cachedDefaultName;
+ }
+
+
+ @SuppressWarnings("unchecked")
+ public T getAdaptiveExtension() {
+ Object instance = cachedAdaptiveInstance.get();
+ if (instance == null) {
+ if(createAdaptiveInstanceError == null) {
+ synchronized (cachedAdaptiveInstance) {
+ instance = cachedAdaptiveInstance.get();
+ if (instance == null) {
+ try {
+ instance = createAdaptiveExtension();
+ cachedAdaptiveInstance.set(instance);
+ } catch (Throwable t) {
+ createAdaptiveInstanceError = t;
+ rethrowAsRuntime(t, "fail to create adaptive instance: ");
+ }
+ }
+ }
+ }
+ else {
+ rethrowAsRuntime(createAdaptiveInstanceError, "fail to create adaptive instance: ");
+ }
+ }
+
+ return (T) instance;
+ }
+
+ private static void rethrowAsRuntime(Throwable t, String message) {
+ if(t instanceof RuntimeException)
+ throw (RuntimeException)t;
+ else
+ throw new IllegalStateException(message + t.toString(), t);
+ }
+
+ @SuppressWarnings("unchecked")
+ private T createExtension(String name) {
+ Class<?> clazz = getExtensionClasses().get(name);
+ if (clazz == null) {
+ throw new IllegalStateException("No such extension " + type.getName() + " by name " + name);
+ }
+ try {
+ T instance = injectExtension((T) clazz.newInstance());
+ Set<Class<?>> wrapperClasses = cachedWrapperClasses;
+ if (wrapperClasses != null && wrapperClasses.size() > 0) {
+ for (Class<?> wrapperClass : wrapperClasses) {
+ instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
+ }
+ }
+ return instance;
+ } catch (Throwable t) {
+ throw new IllegalStateException("Extension instance(name: " + name + ", class: " +
+ type + ") could not be instantiated: " + t.getMessage(), t);
+ }
+ }
+
+ private T injectExtension(T instance) {
+ try {
+ for (Method method : instance.getClass().getMethods()) {
+ if (method.getName().startsWith("set")
+ && method.getParameterTypes().length == 1
+ && Modifier.isPublic(method.getModifiers())) {
+ Class<?> pt = method.getParameterTypes()[0];
+ if (pt.isInterface()) {
+ try {
+ Object adaptive = getExtensionLoader(pt).getAdaptiveExtension();
+ method.invoke(instance, adaptive);
+ } catch (Exception e) {
+ logger.error("fail to inject via method " + method.getName()
+ + " of interface " + type.getName() + ": " + e.getMessage(), e);
+ }
+ }
+ }
+ }
+ } catch (Exception e) {
+ logger.error(e.getMessage(), e);
+ }
+ return instance;
+ }
+
+ private Class<?> getExtensionClass(String name) {
+ if (type == null)
+ throw new IllegalArgumentException("Extension type == null");
+ if (name == null)
+ throw new IllegalArgumentException("Extension name == null");
+ Class<?> clazz = getExtensionClasses().get(name);
+ if (clazz == null)
+ throw new IllegalStateException("No such extension \"" + name + "\" for " + type.getName() + "!");
+ return clazz;
+ }
+
+ private Map<String, Class<?>> getExtensionClasses() {
+ Map<String, Class<?>> classes = cachedClasses.get();
+ if (classes == null) {
+ synchronized (cachedClasses) {
+ classes = cachedClasses.get();
+ if (classes == null) {
+ classes = loadExtensionClasses();
+ cachedClasses.set(classes);
+ }
+ }
+ }
+ return classes;
+ }
+
+ private Map<String, Class<?>> loadExtensionClasses() {
+ final Extension defaultAnnotation = type.getAnnotation(Extension.class);
+ if(defaultAnnotation != null) {
+ String[] names = NAME_SEPARATOR.split(defaultAnnotation.value());
+ if(names.length > 1) {
+ throw new IllegalStateException("more than 1 default extension name on extension " + type.getName()
+ + ": " + Arrays.toString(names));
+ }
+ if(names.length == 1) cachedDefaultName = names[0];
+ }
+
+ ClassLoader classLoader = findClassLoader();
+ Map<String, Class<?>> extensionClasses = new HashMap<String, Class<?>>();
+ String fileName = null;
+ try {
+ fileName = SERVICES_DIRECTORY + type.getName();
+ Enumeration<java.net.URL> urls;
+ if (classLoader != null) {
+ urls = classLoader.getResources(fileName);
+ } else {
+ urls = ClassLoader.getSystemResources(fileName);
+ }
+ if (urls != null) {
+ while (urls.hasMoreElements()) {
+ java.net.URL url = urls.nextElement();
+ try {
+ BufferedReader reader = new BufferedReader(new InputStreamReader(url.openStream(), "utf-8"));
+ try {
+ String line = null;
+ while ((line = reader.readLine()) != null) {
+ final int ci = line.indexOf('#');
+ if (ci >= 0) line = line.substring(0, ci);
+ line = line.trim();
+ if (line.length() > 0) {
+ try {
+ Class<?> clazz = Class.forName(line, true, classLoader);
+ if (! type.isAssignableFrom(clazz)) {
+ throw new IllegalStateException("Error when load extension class(interface: " +
+ type + ", class line: " + clazz.getName() + "), class "
+ + clazz.getName() + "is not subtype of interface.");
+ }
+ if (clazz.isAnnotationPresent(Adaptive.class)) {
+ if(cachedAdaptiveClass == null) {
+ cachedAdaptiveClass = clazz;
+ } else if (! cachedAdaptiveClass.equals(clazz)) {
+ throw new IllegalStateException("More than 1 adaptive class found: "
+ + cachedAdaptiveClass.getClass().getName()
+ + ", " + clazz.getClass().getName());
+ }
+ } else {
+ try {
+ clazz.getConstructor(type);
+ Set<Class<?>> wrappers = cachedWrapperClasses;
+ if (wrappers == null) {
+ cachedWrapperClasses = new ConcurrentHashSet<Class<?>>();
+ wrappers = cachedWrapperClasses;
+ }
+ wrappers.add(clazz);
+ } catch (NoSuchMethodException e) {
+ clazz.getConstructor();
+ Extension extension = clazz.getAnnotation(Extension.class);
+ if (extension == null) {
+ throw new IllegalStateException("No such @Extension annotation in class " + clazz.getName());
+ }
+ String name = extension.value();
+ if (name == null || name.length() == 0) {
+ throw new IllegalStateException("Illegal @Extension annotation in class " + clazz.getName());
+ }
+ String[] names = NAME_SEPARATOR.split(name);
+ for (String n : names) {
+ Class<?> c = extensionClasses.get(n);
+ if (c == null) {
+ extensionClasses.put(n, clazz);
+ } else if (c != clazz) {
+ throw new IllegalStateException("Duplicate extension " + type.getName() + " name " + n + " on " + c.getName() + " and " + clazz.getName());
+ }
+ }
+ }
+ }
+ } catch (Throwable t) {
+ logger.error("Exception when load extension class(interface: " +
+ type + ", class line: " + line + ") in " + url, t);
+ }
+ }
+ } // end of while read lines
+ } finally {
+ reader.close();
+ }
+ } catch (Throwable t) {
+ logger.error("Exception when load extension class(interface: " +
+ type + ", class file: " + url + ") in " + url, t);
+ }
+ } // end of while urls
+ }
+ } catch (Throwable t) {
+ logger.error("Exception when load extension class(interface: " +
+ type + ", description file: " + fileName + ").", t);
+ }
+ return extensionClasses;
+ }
+
+ @SuppressWarnings("unchecked")
+ private T createAdaptiveExtension() {
+ try {
+ return injectExtension((T) getAdaptiveExtensionClass().newInstance());
+ } catch (Exception e) {
+ throw new IllegalStateException("Can not create adaptive extenstion " + type + ", cause: " + e.getMessage(), e);
+ }
+ }
+
+ private Class<?> getAdaptiveExtensionClass() {
+ getExtensionClasses();
+ if (cachedAdaptiveClass != null) {
+ return cachedAdaptiveClass;
+ }
+ return cachedAdaptiveClass = createAdaptiveExtensionClass();
+ }
+
+ private Class<?> createAdaptiveExtensionClass() {
+ ClassLoader classLoader = findClassLoader();
+
+ Method[] methods = type.getMethods();
+ boolean hasAdaptiveAnnotation = false;
+ for(Method m : methods) {
+ if(m.isAnnotationPresent(Adaptive.class)) {
+ hasAdaptiveAnnotation = true;
+ break;
+ }
+ }
+ // 完全没有Adaptive方法,则不需要生成Adaptive类
+ if(! hasAdaptiveAnnotation)
+ throw new IllegalStateException("No adaptive method on extension " + type.getName() + ", refuse to create the adaptive class!");
+
+ ClassGenerator cg = ClassGenerator.newInstance(classLoader);
+ cg.setClassName(type.getName() + "$Adpative");
+ cg.addInterface(type);
+ cg.addDefaultConstructor();
+
+ for (Method method : methods) {
+ Class<?> rt = method.getReturnType();
+ Class<?>[] pts = method.getParameterTypes();
+
+ Adaptive adaptiveAnnotation = method.getAnnotation(Adaptive.class);
+ StringBuilder code = new StringBuilder(512);
+ if (adaptiveAnnotation == null) {
+ code.append("throw new UnsupportedOperationException(\"method ")
+ .append(method.toString()).append(" of interface ")
+ .append(type.getName()).append(" is not adaptive method!\");");
+ } else {
+ int urlTypeIndex = -1;
+ for (int i = 0; i < pts.length; ++i) {
+ if (pts[i].equals(URL.class)) {
+ urlTypeIndex = i;
+ break;
+ }
+ }
+ // 有类型为URL的参数
+ if (urlTypeIndex != -1) {
+ // Null Point check
+ String s = String.format("if (arg%d == null) { throw new IllegalArgumentException(\"url == null\"); }",
+ urlTypeIndex);
+ code.append(s);
+
+ s = String.format("%s url = arg%d;", URL.class.getName(), urlTypeIndex);
+ code.append(s);
+ }
+ // 参数没有URL类型
+ else {
+ String attribMethod = null;
+
+ // 找到参数的URL属性
+ LBL_PTS:
+ for (int i = 0; i < pts.length; ++i) {
+ Method[] ms = pts[i].getMethods();
+ for (Method m : ms) {
+ String name = m.getName();
+ if ((name.startsWith("get") || name.length() > 3)
+ && Modifier.isPublic(m.getModifiers())
+ && !Modifier.isStatic(m.getModifiers())
+ && m.getParameterTypes().length == 0
+ && m.getReturnType() == URL.class) {
+ urlTypeIndex = i;
+ attribMethod = name;
+ break LBL_PTS;
+ }
+ }
+ }
+ if(attribMethod == null) {
+ throw new IllegalStateException("fail to create adative class for interface " + type.getName()
+ + ": not found url parameter or url attribute in parameters of method " + method.getName());
+ }
+
+ // Null point check
+ String s = String.format("if (arg%d == null) { throw new IllegalArgumentException(\"%s argument == null\"); }",
+ urlTypeIndex, pts[urlTypeIndex].getName());
+ code.append(s);
+ s = String.format("if (arg%d.%s() == null) { throw new IllegalArgumentException(\"%s argument %s() == null\"); }",
+ urlTypeIndex, attribMethod, pts[urlTypeIndex].getName(), attribMethod);
+ code.append(s);
+
+ s = String.format("%s url = arg%d.%s();",URL.class.getName(), urlTypeIndex, attribMethod);
+ code.append(s);
+ }
+
+ String[] value = adaptiveAnnotation.value();
+ // 没有设置Key,则使用“扩展点接口名的点分隔 作为Key
+ if(value.length == 0) {
+ char[] charArray = type.getSimpleName().toCharArray();
+ StringBuilder sb = new StringBuilder(128);
+ for (int i = 0; i < charArray.length; i++) {
+ if(Character.isUpperCase(charArray[i])) {
+ if(i != 0) {
+ sb.append(".");
+ }
+ sb.append(Character.toLowerCase(charArray[i]));
+ }
+ else {
+ sb.append(charArray[i]);
+ }
+ }
+ value = new String[] {sb.toString()};
+ }
+
+ String defaultExtName = cachedDefaultName;
+ String getNameCode = null;
+ for (int i = value.length - 1; i >= 0; --i) {
+ if(i == value.length - 1) {
+ if(null != defaultExtName) {
+ if(!"protocol".equals(value[i]))
+ getNameCode = String.format("url.getParameter(\"%s\", \"%s\")", value[i], defaultExtName);
+ else
+ getNameCode = String.format("( url.getProtocol() == null ? \"%s\" : url.getProtocol() )", defaultExtName);
+ }
+ else {
+ if(!"protocol".equals(value[i]))
+ getNameCode = String.format("url.getParameter(\"%s\")", value[i]);
+ else
+ getNameCode = "url.getProtocol()";
+ }
+ }
+ else {
+ if(!"protocol".equals(value[i]))
+ getNameCode = String.format("url.getParameter(\"%s\", %s)", value[i], getNameCode);
+ else
+ getNameCode = String.format("( url.getProtocol() == null ? (%s) : url.getProtocol() )", getNameCode);
+ }
+ }
+ code.append("String extName = ").append(getNameCode).append(";");
+ // check extName == null?
+ String s = String.format("if(extName == null) {" +
+ "throw new IllegalStateException(\"Fail to get extension(%s) name from url(\" + url.toString() + \") use keys(%s)\"); }",
+ type.getName(), Arrays.toString(value));
+ code.append(s);
+
+ s = String.format("%s extension = (%<s)%s.getExtensionLoader(%s.class).getExtension(extName);",
+ type.getName(), ExtensionLoader.class.getName(), type.getName());
+ code.append(s);
+
+ // return statement
+ if (!rt.equals(void.class)) {
+ code.append("return ");
+ }
+
+ s = String.format("extension.%s(", method.getName());
+ code.append(s);
+ for (int i = 0; i < pts.length; i++) {
+ if (i != 0)
+ code.append(", ");
+ code.append("arg").append(i);
+ }
+ code.append(");");
+ }
+
+ cg.addMethod(method.getName(), method.getModifiers(), rt, pts,
+ method.getExceptionTypes(), code.toString());
+ }
+ return cg.toClass();
+ }
+
+ private static ClassLoader findClassLoader() {
+ ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
+ if (classLoader != null) {
+ return classLoader;
+ }
+ classLoader = ExtensionLoader.class.getClassLoader();
+ return classLoader;
+ }
+
+ @Override
+ public String toString() {
+ return this.getClass().getName() + "[" + type.getName() + "]";
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/Node.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/Node.java
new file mode 100644
index 0000000..8884158
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/Node.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common;
+
+import com.alibaba.dubbo.common.URL;
+
+/**
+ * Node. (API/SPI, Prototype, ThreadSafe)
+ *
+ * @author william.liangf
+ */
+public interface Node {
+
+ /**
+ * get url.
+ *
+ * @return url.
+ */
+ URL getUrl();
+
+ /**
+ * is available.
+ *
+ * @return available.
+ */
+ boolean isAvailable();
+
+ /**
+ * destroy.
+ */
+ void destroy();
+
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/Parameters.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/Parameters.java
new file mode 100644
index 0000000..449132b
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/Parameters.java
@@ -0,0 +1,261 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common;
+
+import java.io.UnsupportedEncodingException;
+import java.net.URLDecoder;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import com.alibaba.dubbo.common.logger.Logger;
+import com.alibaba.dubbo.common.logger.LoggerFactory;
+import com.alibaba.dubbo.common.utils.StringUtils;
+
+/**
+ * 兼容2.0.5之前版本
+ * @author tony.chenl
+ */
+@Deprecated
+public class Parameters {
+ private final Map<String, String> parameters;
+
+ protected static final Logger logger = LoggerFactory.getLogger(Parameters.class);
+
+ public Parameters(String... pairs) {
+ this(toMap(pairs));
+ }
+
+ public Parameters(Map<String, String> parameters){
+ this.parameters = Collections.unmodifiableMap(parameters != null ? new HashMap<String, String>(parameters) : new HashMap<String, String>(0));
+ }
+
+ private static Map<String, String> toMap(String... pairs) {
+ Map<String, String> parameters = new HashMap<String, String>();
+ if (pairs.length > 0) {
+ if (pairs.length % 2 != 0) {
+ throw new IllegalArgumentException("pairs must be even.");
+ }
+ for (int i = 0; i < pairs.length; i = i + 2) {
+ parameters.put(pairs[i], pairs[i + 1]);
+ }
+ }
+ return parameters;
+ }
+
+ public Map<String, String> getParameters() {
+ return parameters;
+ }
+
+ public <T> T getExtension(Class<T> type, String key) {
+ String name = getParameter(key);
+ return ExtensionLoader.getExtensionLoader(type).getExtension(name);
+ }
+
+ public <T> T getExtension(Class<T> type, String key, String defaultValue) {
+ String name = getParameter(key, defaultValue);
+ return ExtensionLoader.getExtensionLoader(type).getExtension(name);
+ }
+
+ public <T> T getMethodExtension(Class<T> type, String method, String key) {
+ String name = getMethodParameter(method, key);
+ return ExtensionLoader.getExtensionLoader(type).getExtension(name);
+ }
+
+ public <T> T getMethodExtension(Class<T> type, String method, String key, String defaultValue) {
+ String name = getMethodParameter(method, key, defaultValue);
+ return ExtensionLoader.getExtensionLoader(type).getExtension(name);
+ }
+
+ public String getDecodedParameter(String key) {
+ return getDecodedParameter(key, null);
+ }
+
+ public String getDecodedParameter(String key, String defaultValue) {
+ String value = getParameter(key, defaultValue);
+ if (value != null && value.length() > 0) {
+ try {
+ value = URLDecoder.decode(value, "UTF-8");
+ } catch (UnsupportedEncodingException e) {
+ logger.error(e.getMessage(), e);
+ }
+ }
+ return value;
+ }
+
+ public String getParameter(String key) {
+ String value = parameters.get(key);
+ if (value == null || value.length() == 0) {
+ value = parameters.get(Constants.HIDE_KEY_PREFIX + key);
+ }
+ if (value == null || value.length() == 0) {
+ value = parameters.get(Constants.DEFAULT_KEY_PREFIX + key);
+ }
+ if (value == null || value.length() == 0) {
+ value = parameters.get(Constants.HIDE_KEY_PREFIX + Constants.DEFAULT_KEY_PREFIX + key);
+ }
+ return value;
+ }
+
+ public String getParameter(String key, String defaultValue) {
+ String value = getParameter(key);
+ if (value == null || value.length() == 0) {
+ return defaultValue;
+ }
+ return value;
+ }
+
+ public int getIntParameter(String key) {
+ String value = getParameter(key);
+ if (value == null || value.length() == 0) {
+ return 0;
+ }
+ return Integer.parseInt(value);
+ }
+
+ public int getIntParameter(String key, int defaultValue) {
+ String value = getParameter(key);
+ if (value == null || value.length() == 0) {
+ return defaultValue;
+ }
+ return Integer.parseInt(value);
+ }
+
+ public int getPositiveIntParameter(String key, int defaultValue) {
+ if (defaultValue <= 0) {
+ throw new IllegalArgumentException("defaultValue <= 0");
+ }
+ String value = getParameter(key);
+ if (value == null || value.length() == 0) {
+ return defaultValue;
+ }
+ int i = Integer.parseInt(value);
+ if (i > 0) {
+ return i;
+ }
+ return defaultValue;
+ }
+
+ public boolean getBooleanParameter(String key) {
+ String value = getParameter(key);
+ if (value == null || value.length() == 0) {
+ return false;
+ }
+ return Boolean.parseBoolean(value);
+ }
+
+ public boolean getBooleanParameter(String key, boolean defaultValue) {
+ String value = getParameter(key);
+ if (value == null || value.length() == 0) {
+ return defaultValue;
+ }
+ return Boolean.parseBoolean(value);
+ }
+
+ public boolean hasParamter(String key) {
+ String value = getParameter(key);
+ return value != null && value.length() > 0;
+ }
+
+ public String getMethodParameter(String method, String key) {
+ String value = parameters.get(method + "." + key);
+ if (value == null || value.length() == 0) {
+ value = parameters.get(Constants.HIDE_KEY_PREFIX + method + "." + key);
+ }
+ if (value == null || value.length() == 0) {
+ return getParameter(key);
+ }
+ return value;
+ }
+
+ public String getMethodParameter(String method, String key, String defaultValue) {
+ String value = getMethodParameter(method, key);
+ if (value == null || value.length() == 0) {
+ return defaultValue;
+ }
+ return value;
+ }
+
+ public int getMethodIntParameter(String method, String key) {
+ String value = getMethodParameter(method, key);
+ if (value == null || value.length() == 0) {
+ return 0;
+ }
+ return Integer.parseInt(value);
+ }
+
+ public int getMethodIntParameter(String method, String key, int defaultValue) {
+ String value = getMethodParameter(method, key);
+ if (value == null || value.length() == 0) {
+ return defaultValue;
+ }
+ return Integer.parseInt(value);
+ }
+
+ public int getMethodPositiveIntParameter(String method, String key, int defaultValue) {
+ if (defaultValue <= 0) {
+ throw new IllegalArgumentException("defaultValue <= 0");
+ }
+ String value = getMethodParameter(method, key);
+ if (value == null || value.length() == 0) {
+ return defaultValue;
+ }
+ int i = Integer.parseInt(value);
+ if (i > 0) {
+ return i;
+ }
+ return defaultValue;
+ }
+
+ public boolean getMethodBooleanParameter(String method, String key) {
+ String value = getMethodParameter(method, key);
+ if (value == null || value.length() == 0) {
+ return false;
+ }
+ return Boolean.parseBoolean(value);
+ }
+
+ public boolean getMethodBooleanParameter(String method, String key, boolean defaultValue) {
+ String value = getMethodParameter(method, key);
+ if (value == null || value.length() == 0) {
+ return defaultValue;
+ }
+ return Boolean.parseBoolean(value);
+ }
+
+ public boolean hasMethodParamter(String method, String key) {
+ String value = getMethodParameter(method, key);
+ return value != null && value.length() > 0;
+ }
+
+ public static Parameters parseParameters(String query) {
+ return new Parameters(StringUtils.parseQueryString(query));
+ }
+
+ public boolean equals(Object o) {
+ return parameters.equals(o);
+ }
+
+ public int hashCode() {
+ return parameters.hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return StringUtils.toQueryString(getParameters());
+ }
+
+ }
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/Resetable.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/Resetable.java
new file mode 100644
index 0000000..1ef0985
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/Resetable.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common;
+
+import com.alibaba.dubbo.common.URL;
+
+/**
+ * Resetable.
+ *
+ * @author william.liangf
+ */
+public interface Resetable {
+
+ /**
+ * reset.
+ *
+ * @param url
+ */
+ void reset(URL url);
+
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/URL.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/URL.java
new file mode 100644
index 0000000..0597c3e
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/URL.java
@@ -0,0 +1,1119 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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;
+
+ private final transient Map<String, Number> numbers = new ConcurrentHashMap<String, Number>();
+
+ protected URL() {
+ this.protocol = null;
+ this.username = null;
+ this.password = null;
+ this.host = null;
+ this.port = 0;
+ this.path = null;
+ this.parameters = null;
+ }
+
+ public URL(String protocol, String host, int port) {
+ this(protocol, null, null, host, port, null, (Map<String, String>) null);
+ }
+
+ public URL(String protocol, String host, int port, String... pairs) {
+ this(protocol, null, null, host, port, null, CollectionUtils.toStringMap(pairs));
+ }
+
+ public URL(String protocol, String host, int port, Map<String, String> parameters) {
+ this(protocol, null, null, host, port, null, parameters);
+ }
+
+ public URL(String protocol, String host, int port, String path) {
+ this(protocol, null, null, host, port, path, (Map<String, String>) null);
+ }
+
+ public URL(String protocol, String host, int port, String path, String... pairs) {
+ this(protocol, null, null, host, port, path, CollectionUtils.toStringMap(pairs));
+ }
+
+ public URL(String protocol, String host, int port, String path, Map<String, String> parameters) {
+ this(protocol, null, null, host, port, path, parameters);
+ }
+
+ public URL(String protocol, String username, String password, String host, int port, String path) {
+ this(protocol, username, password, host, port, path, (Map<String, String>) null);
+ }
+
+ public URL(String protocol, String username, String password, String host, int port, String path, String... pairs) {
+ this(protocol, username, password, host, port, path, CollectionUtils.toStringMap(pairs));
+ }
+
+ public URL(String protocol, String username, String password, String host, int port, String path, Map<String, String> parameters) {
+ if ((username == null || username.length() == 0)
+ && password != null && password.length() > 0) {
+ throw new IllegalArgumentException("Invalid url, password without username!");
+ }
+ this.protocol = protocol;
+ this.username = username;
+ this.password = password;
+ this.host = host != null && host.length() > 0 ? NetUtils.filterLocalHost(host) : host;
+ this.port = (port < 0 ? 0 : port);
+ this.path = path;
+ // trim the beginning "/"
+ while(path != null && path.startsWith("/")) {
+ path = path.substring(1);
+ }
+ this.parameters = Collections.unmodifiableMap(parameters != null ? new HashMap<String, String>(parameters) : new HashMap<String, String>(0));
+ }
+
+ /**
+ * Parse url string
+ *
+ * @param url URL string
+ * @return URL instance
+ * @see URL
+ */
+ public static URL valueOf(String url) {
+ if (url == null || (url = url.trim()).length() == 0) {
+ throw new IllegalArgumentException("url == null");
+ }
+ String protocol = null;
+ String username = null;
+ String password = null;
+ String host = null;
+ int port = 0;
+ String path = null;
+ Map<String, String> parameters = null;
+ int i = url.indexOf("?"); // seperator between body and parameters
+ if (i >= 0) {
+ String[] parts = url.substring(i + 1).split("\\&");
+ parameters = new HashMap<String, String>();
+ for (String part : parts) {
+ part = part.trim();
+ if (part.length() > 0) {
+ int j = part.indexOf('=');
+ if (j >= 0) {
+ parameters.put(part.substring(0, j), part.substring(j + 1));
+ } else {
+ parameters.put(part, part);
+ }
+ }
+ }
+ url = url.substring(0, i);
+ }
+ i = url.indexOf("://");
+ if (i >= 0) {
+ if(i == 0) throw new IllegalStateException("url missing protocol: \"" + url + "\"");
+ protocol = url.substring(0, i);
+ url = url.substring(i + 3);
+ }
+ else {
+ // case: file:/path/to/file.txt
+ i = url.indexOf(":/");
+ if(i>=0) {
+ if(i == 0) throw new IllegalStateException("url missing protocol: \"" + url + "\"");
+ protocol = url.substring(0, i);
+ url = url.substring(i + 1);
+ }
+ }
+
+ i = url.indexOf("/");
+ if (i >= 0) {
+ path = url.substring(i + 1);
+ url = url.substring(0, i);
+ }
+ i = url.indexOf("@");
+ if (i >= 0) {
+ username = url.substring(0, i);
+ int j = username.indexOf(":");
+ if (j >= 0) {
+ password = username.substring(j + 1);
+ username = username.substring(0, j);
+ }
+ url = url.substring(i + 1);
+ }
+ i = url.indexOf(":");
+ if (i >= 0 && i < url.length() - 1) {
+ port = Integer.parseInt(url.substring(i + 1));
+ url = url.substring(0, i);
+ }
+ if(url.length() > 0) host = url;
+ return new URL(protocol, username, password, host, port, path, parameters);
+ }
+
+ public String getProtocol() {
+ return protocol;
+ }
+
+ public String getUsername() {
+ return username;
+ }
+
+ public String getPassword() {
+ return password;
+ }
+
+ public String getHost() {
+ return host;
+ }
+
+ public int getPort() {
+ return port;
+ }
+
+ public String getAddress() {
+ return port <= 0 ? host : host + ":" + port;
+ }
+
+ public String getPath() {
+ return path;
+ }
+
+ public String getAbsolutePath() {
+ if (path != null && !path.startsWith("/")) {
+ return "/" + path;
+ }
+ return path;
+ }
+
+ public URL setProtocol(String protocol) {
+ return new URL(protocol, username, password, host, port, path, getParameters());
+ }
+
+ public URL setUsername(String username) {
+ return new URL(protocol, username, password, host, port, path, getParameters());
+ }
+
+ public URL setPassword(String password) {
+ return new URL(protocol, username, password, host, port, path, getParameters());
+ }
+
+ public URL setAddress(String address) {
+ int i = address.lastIndexOf(':');
+ String host;
+ int port = this.port;
+ if (i >= 0) {
+ host = address.substring(0, i);
+ port = Integer.parseInt(address.substring(i + 1));
+ } else {
+ host = address;
+ }
+ return new URL(protocol, username, password, host, port, path, getParameters());
+ }
+
+ public URL setHost(String host) {
+ return new URL(protocol, username, password, host, port, path, getParameters());
+ }
+
+ public URL setPort(int port) {
+ return new URL(protocol, username, password, host, port, path, getParameters());
+ }
+
+ public URL setPath(String path) {
+ return new URL(protocol, username, password, host, port, path, getParameters());
+ }
+
+ public Map<String, String> getParameters() {
+ return parameters;
+ }
+
+ public String getParameterAndDecoded(String key) {
+ return getParameterAndDecoded(key, null);
+ }
+
+ public String getParameterAndDecoded(String key, String defaultValue) {
+ return decode(getParameter(key, defaultValue));
+ }
+
+ public String getParameter(String key) {
+ String value = parameters.get(key);
+ if (value == null || value.length() == 0) {
+ value = parameters.get(Constants.DEFAULT_KEY_PREFIX + key);
+ }
+ return value;
+ }
+
+ public String getParameter(String key, String defaultValue) {
+ String value = getParameter(key);
+ if (value == null || value.length() == 0) {
+ return defaultValue;
+ }
+ return value;
+ }
+
+ public double getParameter(String key, double defaultValue) {
+ Number n = numbers.get(key);
+ if (n != null) {
+ return n.doubleValue();
+ }
+ String value = getParameter(key);
+ if (value == null || value.length() == 0) {
+ return defaultValue;
+ }
+ double d = Double.parseDouble(value);
+ numbers.put(key, d);
+ return d;
+ }
+
+ public float getParameter(String key, float defaultValue) {
+ Number n = numbers.get(key);
+ if (n != null) {
+ return n.floatValue();
+ }
+ String value = getParameter(key);
+ if (value == null || value.length() == 0) {
+ return defaultValue;
+ }
+ float f = Float.parseFloat(value);
+ numbers.put(key, f);
+ return f;
+ }
+
+ public long getParameter(String key, long defaultValue) {
+ Number n = numbers.get(key);
+ if (n != null) {
+ return n.longValue();
+ }
+ String value = getParameter(key);
+ if (value == null || value.length() == 0) {
+ return defaultValue;
+ }
+ long l = Long.parseLong(value);
+ numbers.put(key, l);
+ return l;
+ }
+
+ public int getParameter(String key, int defaultValue) {
+ Number n = numbers.get(key);
+ if (n != null) {
+ return n.intValue();
+ }
+ String value = getParameter(key);
+ if (value == null || value.length() == 0) {
+ return defaultValue;
+ }
+ int i = Integer.parseInt(value);
+ numbers.put(key, i);
+ return i;
+ }
+
+ public short getParameter(String key, short defaultValue) {
+ Number n = numbers.get(key);
+ if (n != null) {
+ return n.shortValue();
+ }
+ String value = getParameter(key);
+ if (value == null || value.length() == 0) {
+ return defaultValue;
+ }
+ short s = Short.parseShort(value);
+ numbers.put(key, s);
+ return s;
+ }
+
+ public byte getParameter(String key, byte defaultValue) {
+ Number n = numbers.get(key);
+ if (n != null) {
+ return n.byteValue();
+ }
+ String value = getParameter(key);
+ if (value == null || value.length() == 0) {
+ return defaultValue;
+ }
+ byte b = Byte.parseByte(value);
+ numbers.put(key, b);
+ return b;
+ }
+
+ public float getPositiveParameter(String key, float defaultValue) {
+ if (defaultValue <= 0) {
+ throw new IllegalArgumentException("defaultValue <= 0");
+ }
+ float value = getParameter(key, defaultValue);
+ if (value <= 0) {
+ return defaultValue;
+ }
+ return value;
+ }
+
+ public double getPositiveParameter(String key, double defaultValue) {
+ if (defaultValue <= 0) {
+ throw new IllegalArgumentException("defaultValue <= 0");
+ }
+ double value = getParameter(key, defaultValue);
+ if (value <= 0) {
+ return defaultValue;
+ }
+ return value;
+ }
+
+ public long getPositiveParameter(String key, long defaultValue) {
+ if (defaultValue <= 0) {
+ throw new IllegalArgumentException("defaultValue <= 0");
+ }
+ long value = getParameter(key, defaultValue);
+ if (value <= 0) {
+ return defaultValue;
+ }
+ return value;
+ }
+
+ public int getPositiveParameter(String key, int defaultValue) {
+ if (defaultValue <= 0) {
+ throw new IllegalArgumentException("defaultValue <= 0");
+ }
+ int value = getParameter(key, defaultValue);
+ if (value <= 0) {
+ return defaultValue;
+ }
+ return value;
+ }
+
+ public short getPositiveParameter(String key, short defaultValue) {
+ if (defaultValue <= 0) {
+ throw new IllegalArgumentException("defaultValue <= 0");
+ }
+ short value = getParameter(key, defaultValue);
+ if (value <= 0) {
+ return defaultValue;
+ }
+ return value;
+ }
+
+ public byte getPositiveParameter(String key, byte defaultValue) {
+ if (defaultValue <= 0) {
+ throw new IllegalArgumentException("defaultValue <= 0");
+ }
+ byte value = getParameter(key, defaultValue);
+ if (value <= 0) {
+ return defaultValue;
+ }
+ return value;
+ }
+
+ public char getParameter(String key, char defaultValue) {
+ String value = getParameter(key);
+ if (value == null || value.length() == 0) {
+ return defaultValue;
+ }
+ return value.charAt(0);
+ }
+
+ public boolean getParameter(String key, boolean defaultValue) {
+ String value = getParameter(key);
+ if (value == null || value.length() == 0) {
+ return defaultValue;
+ }
+ return Boolean.parseBoolean(value);
+ }
+
+ public boolean hasParameter(String key) {
+ String value = getParameter(key);
+ return value != null && value.length() > 0;
+ }
+
+ public String getMethodParameter(String method, String key) {
+ String value = parameters.get(method + "." + key);
+ if (value == null || value.length() == 0) {
+ value = parameters.get(Constants.HIDE_KEY_PREFIX + method + "." + key);
+ }
+ if (value == null || value.length() == 0) {
+ return getParameter(key);
+ }
+ return value;
+ }
+
+ public String getMethodParameter(String method, String key, String defaultValue) {
+ String value = getMethodParameter(method, key);
+ if (value == null || value.length() == 0) {
+ return defaultValue;
+ }
+ return value;
+ }
+
+ public double getMethodParameter(String method, String key, double defaultValue) {
+ String methodKey = method + "." + key;
+ Number n = numbers.get(methodKey);
+ if (n != null) {
+ return n.intValue();
+ }
+ String value = getMethodParameter(method, key);
+ if (value == null || value.length() == 0) {
+ return defaultValue;
+ }
+ double d = Double.parseDouble(value);
+ numbers.put(methodKey, d);
+ return d;
+ }
+
+ public float getMethodParameter(String method, String key, float defaultValue) {
+ String methodKey = method + "." + key;
+ Number n = numbers.get(methodKey);
+ if (n != null) {
+ return n.intValue();
+ }
+ String value = getMethodParameter(method, key);
+ if (value == null || value.length() == 0) {
+ return defaultValue;
+ }
+ float f = Float.parseFloat(value);
+ numbers.put(methodKey, f);
+ return f;
+ }
+
+ public long getMethodParameter(String method, String key, long defaultValue) {
+ String methodKey = method + "." + key;
+ Number n = numbers.get(methodKey);
+ if (n != null) {
+ return n.intValue();
+ }
+ String value = getMethodParameter(method, key);
+ if (value == null || value.length() == 0) {
+ return defaultValue;
+ }
+ long l = Long.parseLong(value);
+ numbers.put(methodKey, l);
+ return l;
+ }
+
+ public int getMethodParameter(String method, String key, int defaultValue) {
+ String methodKey = method + "." + key;
+ Number n = numbers.get(methodKey);
+ if (n != null) {
+ return n.intValue();
+ }
+ String value = getMethodParameter(method, key);
+ if (value == null || value.length() == 0) {
+ return defaultValue;
+ }
+ int i = Integer.parseInt(value);
+ numbers.put(methodKey, i);
+ return i;
+ }
+
+ public short getMethodParameter(String method, String key, short defaultValue) {
+ String methodKey = method + "." + key;
+ Number n = numbers.get(methodKey);
+ if (n != null) {
+ return n.shortValue();
+ }
+ String value = getMethodParameter(method, key);
+ if (value == null || value.length() == 0) {
+ return defaultValue;
+ }
+ short s = Short.parseShort(value);
+ numbers.put(methodKey, s);
+ return s;
+ }
+
+ public byte getMethodParameter(String method, String key, byte defaultValue) {
+ String methodKey = method + "." + key;
+ Number n = numbers.get(methodKey);
+ if (n != null) {
+ return n.byteValue();
+ }
+ String value = getMethodParameter(method, key);
+ if (value == null || value.length() == 0) {
+ return defaultValue;
+ }
+ byte b = Byte.parseByte(value);
+ numbers.put(methodKey, b);
+ return b;
+ }
+
+ public double getMethodPositiveParameter(String method, String key, double defaultValue) {
+ if (defaultValue <= 0) {
+ throw new IllegalArgumentException("defaultValue <= 0");
+ }
+ double value = getMethodParameter(method, key, defaultValue);
+ if (value <= 0) {
+ return defaultValue;
+ }
+ return value;
+ }
+
+ public float getMethodPositiveParameter(String method, String key, float defaultValue) {
+ if (defaultValue <= 0) {
+ throw new IllegalArgumentException("defaultValue <= 0");
+ }
+ float value = getMethodParameter(method, key, defaultValue);
+ if (value <= 0) {
+ return defaultValue;
+ }
+ return value;
+ }
+
+ public long getMethodPositiveParameter(String method, String key, long defaultValue) {
+ if (defaultValue <= 0) {
+ throw new IllegalArgumentException("defaultValue <= 0");
+ }
+ long value = getMethodParameter(method, key, defaultValue);
+ if (value <= 0) {
+ return defaultValue;
+ }
+ return value;
+ }
+
+ public int getMethodPositiveParameter(String method, String key, int defaultValue) {
+ if (defaultValue <= 0) {
+ throw new IllegalArgumentException("defaultValue <= 0");
+ }
+ int value = getMethodParameter(method, key, defaultValue);
+ if (value <= 0) {
+ return defaultValue;
+ }
+ return value;
+ }
+
+ public short getMethodPositiveParameter(String method, String key, short defaultValue) {
+ if (defaultValue <= 0) {
+ throw new IllegalArgumentException("defaultValue <= 0");
+ }
+ short value = getMethodParameter(method, key, defaultValue);
+ if (value <= 0) {
+ return defaultValue;
+ }
+ return value;
+ }
+
+ public byte getMethodPositiveParameter(String method, String key, byte defaultValue) {
+ if (defaultValue <= 0) {
+ throw new IllegalArgumentException("defaultValue <= 0");
+ }
+ byte value = getMethodParameter(method, key, defaultValue);
+ if (value <= 0) {
+ return defaultValue;
+ }
+ return value;
+ }
+
+ public char getMethodParameter(String method, String key, char defaultValue) {
+ String value = getMethodParameter(method, key);
+ if (value == null || value.length() == 0) {
+ return defaultValue;
+ }
+ return value.charAt(0);
+ }
+
+ public boolean getMethodParameter(String method, String key, boolean defaultValue) {
+ String value = getMethodParameter(method, key);
+ if (value == null || value.length() == 0) {
+ return defaultValue;
+ }
+ return Boolean.parseBoolean(value);
+ }
+
+ public boolean hasMethodParamter(String method, String key) {
+ String value = getMethodParameter(method, key);
+ return value != null && value.length() > 0;
+ }
+
+ public URL addParameterAndEncoded(String key, String value) {
+ if(value == null || value.length() == 0) {
+ return this;
+ }
+ return addParameter(key, encode(value));
+ }
+
+ public URL addParameter(String key, boolean value) {
+ return addParameter(key, String.valueOf(value));
+ }
+
+ public URL addParameter(String key, char value) {
+ return addParameter(key, String.valueOf(value));
+ }
+
+ public URL addParameter(String key, byte value) {
+ return addParameter(key, String.valueOf(value));
+ }
+
+ public URL addParameter(String key, short value) {
+ return addParameter(key, String.valueOf(value));
+ }
+
+ public URL addParameter(String key, int value) {
+ return addParameter(key, String.valueOf(value));
+ }
+
+ public URL addParameter(String key, long value) {
+ return addParameter(key, String.valueOf(value));
+ }
+
+ public URL addParameter(String key, float value) {
+ return addParameter(key, String.valueOf(value));
+ }
+
+ public URL addParameter(String key, double value) {
+ return addParameter(key, String.valueOf(value));
+ }
+
+ public URL addParameter(String key, Enum<?> value) {
+ if(value == null) return this;
+ return addParameter(key, String.valueOf(value));
+ }
+
+ public URL addParameter(String key, Number value) {
+ if(value == null) return this;
+ return addParameter(key, String.valueOf(value));
+ }
+
+ public URL addParameter(String key, CharSequence value) {
+ if(value == null || value.length() == 0) return this;
+ return addParameter(key, String.valueOf(value));
+ }
+
+ public URL addParameter(String key, String value) {
+ if (key == null || key.length() == 0
+ || value == null || value.length() == 0) {
+ return this;
+ }
+ Map<String, String> map = new HashMap<String, String>(getParameters());
+ map.put(key, value);
+ return new URL(protocol, username, password, host, port, path, map);
+ }
+
+ public URL addParameterIfAbsent(String key, String value) {
+ if (key == null || key.length() == 0
+ || value == null || value.length() == 0) {
+ return this;
+ }
+ if (hasParameter(key)) {
+ return this;
+ }
+ Map<String, String> map = new HashMap<String, String>(getParameters());
+ map.put(key, value);
+ return new URL(protocol, username, password, host, port, path, map);
+ }
+
+ /**
+ * Add parameters to a new url.
+ *
+ * @param parameters
+ * @return A new URL
+ */
+ public URL addParameters(Map<String, String> parameters) {
+ if (parameters == null || parameters.size() == 0) {
+ return this;
+ }
+ Map<String, String> map = new HashMap<String, String>(getParameters());
+ map.putAll(parameters);
+ return new URL(protocol, username, password, host, port, path, map);
+ }
+
+ public URL addParametersIfAbsent(Map<String, String> parameters) {
+ if (parameters == null || parameters.size() == 0) {
+ return this;
+ }
+ Map<String, String> map = new HashMap<String, String>(parameters);
+ map.putAll(getParameters());
+ return new URL(protocol, username, password, host, port, path, map);
+ }
+
+ public URL addParameters(String... pairs) {
+ if (pairs == null || pairs.length == 0) {
+ return this;
+ }
+ if (pairs.length % 2 != 0) {
+ throw new IllegalArgumentException("Map pairs can not be odd number.");
+ }
+ Map<String, String> map = new HashMap<String, String>();
+ int len = pairs.length / 2;
+ for (int i = 0; i < len; i ++) {
+ map.put(pairs[2 * i], pairs[2 * i + 1]);
+ }
+ return addParameters(map);
+ }
+
+ public URL addParameterString(String query) {
+ if (query == null || query.length() == 0) {
+ return this;
+ }
+ return addParameters(StringUtils.parseQueryString(query));
+ }
+
+ public URL removeParameter(String key) {
+ if (key == null || key.length() == 0) {
+ return this;
+ }
+ return removeParameters(key);
+ }
+
+ public URL removeParameters(Collection<String> keys) {
+ if (keys == null || keys.size() == 0) {
+ return this;
+ }
+ return removeParameters(keys.toArray(new String[0]));
+ }
+
+ public URL removeParameters(String... keys) {
+ if (keys == null || keys.length == 0) {
+ return this;
+ }
+ Map<String, String> map = new HashMap<String, String>(getParameters());
+ for (String key : keys) {
+ map.remove(key);
+ }
+ if (map.size() == getParameters().size()) {
+ return this;
+ }
+ return new URL(protocol, username, password, host, port, path, map);
+ }
+
+ public URL cleatParameters() {
+ return new URL(protocol, username, password, host, port, path, new HashMap<String, String>());
+ }
+
+ public String toString() {
+ return buildString(false, true); // no show username and password
+ }
+
+ public String toString(String... parameters) {
+ return buildString(false, true, parameters); // no show username and password
+ }
+
+ public String toIdentityString() {
+ return buildString(false, false); // only return identity message, see the method "equals" and "hashCode"
+ }
+
+ public String toIdentityString(String... parameters) {
+ return buildString(false, false, parameters); // only return identity message, see the method "equals" and "hashCode"
+ }
+
+ public String toFullString() {
+ return buildString(true, true);
+ }
+
+ public String toFullString(String... parameters) {
+ return buildString(true, true, parameters);
+ }
+
+ public String toParameterString() {
+ return toParameterString(new String[0]);
+ }
+
+ public String toParameterString(String... parameters) {
+ StringBuilder buf = new StringBuilder();
+ buildParameters(buf, false, parameters);
+ return buf.toString();
+ }
+
+ private void buildParameters(StringBuilder buf, boolean concat, String[] parameters) {
+ if (getParameters() !=null && getParameters().size() > 0) {
+ List<String> includes = (parameters == null || parameters.length == 0 ? null : Arrays.asList(parameters));
+ boolean first = true;
+ for (Map.Entry<String, String> entry : new TreeMap<String, String>(getParameters()).entrySet()) {
+ if (entry.getKey() != null && entry.getKey().length() > 0
+ && (includes == null || includes.contains(entry.getKey()))) {
+ if (first) {
+ if (concat) {
+ buf.append("?");
+ }
+ first = false;
+ } else {
+ buf.append("&");
+ }
+ buf.append(entry.getKey());
+ buf.append("=");
+ buf.append(entry.getValue() == null ? "" : entry.getValue().trim());
+ }
+ }
+ }
+ }
+
+ private String buildString(boolean u, boolean p, String... parameters) {
+ StringBuilder buf = new StringBuilder();
+ if (protocol != null && protocol.length() > 0) {
+ buf.append(protocol);
+ buf.append("://");
+ }
+ if (u && username != null && username.length() > 0) {
+ buf.append(username);
+ if (password != null && password.length() > 0) {
+ buf.append(":");
+ buf.append(password);
+ }
+ buf.append("@");
+ }
+ if(host != null && host.length() > 0) {
+ buf.append(host);
+ if (port > 0) {
+ buf.append(":");
+ buf.append(port);
+ }
+ }
+ if (path != null && path.length() > 0) {
+ buf.append("/");
+ buf.append(path);
+ }
+ if (p) {
+ buildParameters(buf, true, parameters);
+ }
+ return buf.toString();
+ }
+
+ public java.net.URL toJavaURL() {
+ try {
+ return new java.net.URL(toString());
+ } catch (MalformedURLException e) {
+ throw new IllegalStateException(e.getMessage(), e);
+ }
+ }
+
+ public InetSocketAddress toInetSocketAddress() {
+ return new InetSocketAddress(host, port);
+ }
+
+ public String getServiceKey() {
+ String inf = getServiceName();
+ if (inf == null) return null;
+ StringBuilder buf = new StringBuilder();
+ String group = getParameter(Constants.GROUP_KEY);
+ if (group != null && group.length() > 0) {
+ buf.append(group).append("/");
+ }
+ buf.append(inf);
+ String version = getParameter(Constants.VERSION_KEY);
+ if (version != null && version.length() > 0) {
+ buf.append(":").append(version);
+ }
+ return buf.toString();
+ }
+
+ public String getServiceName() {
+ return getParameter(Constants.INTERFACE_KEY, path);
+ }
+
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((host == null) ? 0 : host.hashCode());
+ result = prime * result + ((path == null) ? 0 : path.hashCode());
+ result = prime * result + port;
+ result = prime * result
+ + ((protocol == null) ? 0 : protocol.hashCode());
+ return result;
+ }
+
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ URL other = (URL) obj;
+ if (host == null) {
+ if (other.host != null)
+ return false;
+ } else if (!host.equals(other.host))
+ return false;
+ if (path == null) {
+ if (other.path != null)
+ return false;
+ } else if (!path.equals(other.path))
+ return false;
+ if (port != other.port)
+ return false;
+ if (protocol == null) {
+ if (other.protocol != null)
+ return false;
+ } else if (!protocol.equals(other.protocol))
+ return false;
+ return true;
+ }
+
+ /**
+ * @deprecated Replace to <code>getParameter(String, int)</code>
+ * @see #getParameter(String, int)
+ */
+ @Deprecated
+ public int getIntParameter(String key) {
+ return getParameter(key, 0);
+ }
+
+ /**
+ * @deprecated Replace to <code>getParameter(String, int)</code>
+ * @see #getParameter(String, int)
+ */
+ @Deprecated
+ public int getIntParameter(String key, int defaultValue) {
+ return getParameter(key, defaultValue);
+ }
+
+ /**
+ * @deprecated Replace to <code>getPositiveParameter(String, int)</code>
+ * @see #getPositiveParameter(String, int)
+ */
+ @Deprecated
+ public int getPositiveIntParameter(String key, int defaultValue) {
+ return getPositiveParameter(key, defaultValue);
+ }
+
+ /**
+ * @deprecated Replace to <code>getParameter(String, boolean)</code>
+ * @see #getParameter(String, boolean)
+ */
+ @Deprecated
+ public boolean getBooleanParameter(String key) {
+ return getParameter(key, false);
+ }
+
+ /**
+ * @deprecated Replace to <code>getParameter(String, boolean)</code>
+ * @see #getParameter(String, boolean)
+ */
+ @Deprecated
+ public boolean getBooleanParameter(String key, boolean defaultValue) {
+ return getParameter(key, defaultValue);
+ }
+
+ /**
+ * @deprecated Replace to <code>getMethodParameter(String, int)</code>
+ * @see #getMethodParameter(String, int)
+ */
+ @Deprecated
+ public int getMethodIntParameter(String method, String key) {
+ return getMethodParameter(method, key, 0);
+ }
+
+ /**
+ * @deprecated Replace to <code>getMethodParameter(String, int)</code>
+ * @see #getMethodParameter(String, int)
+ */
+ @Deprecated
+ public int getMethodIntParameter(String method, String key, int defaultValue) {
+ return getMethodParameter(method, key, defaultValue);
+ }
+
+ /**
+ * @deprecated Replace to <code>getMethodPositiveParameter(String, int)</code>
+ * @see #getMethodPositiveParameter(String, int)
+ */
+ @Deprecated
+ public int getMethodPositiveIntParameter(String method, String key, int defaultValue) {
+ return getMethodPositiveParameter(method, key, defaultValue);
+ }
+
+ /**
+ * @deprecated Replace to <code>getMethodParameter(String, boolean)</code>
+ * @see #getMethodParameter(String, boolean)
+ */
+ @Deprecated
+ public boolean getMethodBooleanParameter(String method, String key) {
+ return getMethodParameter(method, key, false);
+ }
+
+ /**
+ * @deprecated Replace to <code>getMethodParameter(String, boolean)</code>
+ * @see #getMethodParameter(String, boolean)
+ */
+ @Deprecated
+ public boolean getMethodBooleanParameter(String method, String key, boolean defaultValue) {
+ return getMethodParameter(method, key, defaultValue);
+ }
+
+ public static String encode(String value) {
+ if (value == null || value.length() == 0) {
+ return "";
+ }
+ try {
+ return URLEncoder.encode(value, "UTF-8");
+ } catch (UnsupportedEncodingException e) {
+ throw new RuntimeException(e.getMessage(), e);
+ }
+ }
+
+ public static String decode(String value) {
+ if (value == null || value.length() == 0) {
+ return "";
+ }
+ try {
+ return URLDecoder.decode(value, "UTF-8");
+ } catch (UnsupportedEncodingException e) {
+ throw new RuntimeException(e.getMessage(), e);
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/Version.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/Version.java
new file mode 100644
index 0000000..8afbadb
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/Version.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common;
+
+import java.net.URL;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.Set;
+
+import com.alibaba.dubbo.common.logger.Logger;
+import com.alibaba.dubbo.common.logger.LoggerFactory;
+
+/**
+ * Version
+ *
+ * @author william.liangf
+ */
+public final class Version {
+
+ private Version() {}
+
+ private static final Logger logger = LoggerFactory.getLogger(Version.class);
+
+ private static final String VERSION = getVersion(Version.class, "2.0.0");
+
+ private static final boolean INTERNAL = hasResource("com/alibaba/dubbo/registry/support/remote/RemoteRegistry.class");
+
+ private static final boolean COMPATIBLE = hasResource("com/alibaba/dubbo/rpc/dubbo/internal/DubboRequest.class");
+
+ static {
+ // 检查是否存在重复的jar包
+ Version.checkDuplicate(Version.class);
+ }
+
+ public static String getVersion(){
+ return VERSION;
+ }
+
+ public static boolean isInternalVersion() {
+ return INTERNAL;
+ }
+
+ public static boolean isCompatibleVersion() {
+ return COMPATIBLE;
+ }
+
+ public static boolean hasResource(String path) {
+ try {
+ return Version.class.getClassLoader().getResource(path) != null;
+ } catch (Throwable t) {
+ return false;
+ }
+ }
+
+ public static String getVersion(Class<?> cls, String defaultVersion) {
+ try {
+ // 首先查找MANIFEST.MF规范中的版本号
+ String version = cls.getPackage().getImplementationVersion();
+ if (version == null || version.length() == 0) {
+ version = cls.getPackage().getSpecificationVersion();
+ }
+ if (version == null || version.length() == 0) {
+ // 如果规范中没有版本号,基于jar包名获取版本号
+ String file = cls.getProtectionDomain().getCodeSource().getLocation().getFile();
+ if (file != null && file.length() > 0 && file.endsWith(".jar")) {
+ file = file.substring(0, file.length() - 4);
+ int i = file.lastIndexOf('/');
+ if (i >= 0) {
+ file = file.substring(i + 1);
+ }
+ i = file.indexOf("-");
+ if (i >= 0) {
+ file = file.substring(i + 1);
+ }
+ while (file.length() > 0 && ! Character.isDigit(file.charAt(0))) {
+ i = file.indexOf("-");
+ if (i >= 0) {
+ file = file.substring(i + 1);
+ } else {
+ break;
+ }
+ }
+ version = file;
+ }
+ }
+ // 返回版本号,如果为空返回缺省版本号
+ return version == null || version.length() == 0 ? defaultVersion : version;
+ } catch (Throwable e) { // 防御性容错
+ // 忽略异常,返回缺省版本号
+ logger.error(e.getMessage(), e);
+ return defaultVersion;
+ }
+ }
+
+ public static void checkDuplicate(Class<?> cls) {
+ checkDuplicate(cls.getName().replace('.', '/') + ".class");
+ }
+
+ public static void checkDuplicate(String path) {
+ try {
+ // 在ClassPath搜文件
+ Enumeration<URL> urls = Thread.currentThread().getContextClassLoader().getResources(path);
+ Set<String> files = new HashSet<String>();
+ while (urls.hasMoreElements()) {
+ URL url = urls.nextElement();
+ if (url != null) {
+ String file = url.getFile();
+ if (file != null && file.length() > 0) {
+ files.add(file);
+ }
+ }
+ }
+ // 如果有多个,就表示重复
+ if (files.size() > 1) {
+ logger.error("Duplicate class " + path + " in " + files.size() + " jar " + files);
+ }
+ } catch (Throwable e) { // 防御性容错
+ logger.error(e.getMessage(), e);
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/bytecode/ClassGenerator.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/bytecode/ClassGenerator.java
new file mode 100644
index 0000000..82d8cae
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/bytecode/ClassGenerator.java
@@ -0,0 +1,382 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.bytecode;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicLong;
+
+import javassist.CannotCompileException;
+import javassist.ClassPool;
+import javassist.CtClass;
+import javassist.CtConstructor;
+import javassist.CtField;
+import javassist.CtMethod;
+import javassist.CtNewConstructor;
+import javassist.CtNewMethod;
+import javassist.LoaderClassPath;
+import javassist.NotFoundException;
+
+import com.alibaba.dubbo.common.utils.ReflectUtils;
+
+/**
+ * ClassGenerator
+ *
+ * @author qian.lei
+ */
+
+public final class ClassGenerator
+{
+ public static interface DC{} // dynamic class tag interface.
+
+ private static final AtomicLong CLASS_NAME_COUNTER = new AtomicLong(0);
+
+ private static final String SIMPLE_NAME_TAG = "<init>";
+
+ private static final Map<ClassLoader, ClassPool> POOL_MAP = new ConcurrentHashMap<ClassLoader, ClassPool>(); //ClassLoader - ClassPool
+
+ public static ClassGenerator newInstance()
+ {
+ return new ClassGenerator(getClassPool(Thread.currentThread().getContextClassLoader()));
+ }
+
+ public static ClassGenerator newInstance(ClassLoader loader)
+ {
+ return new ClassGenerator(getClassPool(loader));
+ }
+
+ public static boolean isDynamicClass(Class<?> cl)
+ {
+ return ClassGenerator.DC.class.isAssignableFrom(cl);
+ }
+
+ public static ClassPool getClassPool(ClassLoader loader)
+ {
+ if( loader == null )
+ return ClassPool.getDefault();
+
+ ClassPool pool = POOL_MAP.get(loader);
+ if( pool == null )
+ {
+ pool = new ClassPool(true);
+ pool.appendClassPath(new LoaderClassPath(loader));
+ POOL_MAP.put(loader, pool);
+ }
+ return pool;
+ }
+
+ private ClassPool mPool;
+
+ private CtClass mCtc;
+
+ private String mClassName, mSuperClass;
+
+ private Set<String> mInterfaces;
+
+ private List<String> mFields, mConstructors, mMethods;
+
+ private Map<String, Method> mCopyMethods; // <method desc,method instance>
+
+ private Map<String, Constructor<?>> mCopyConstructors; // <constructor desc,constructor instance>
+
+ private boolean mDefaultConstructor = false;
+
+ private ClassGenerator(){}
+
+ private ClassGenerator(ClassPool pool)
+ {
+ mPool = pool;
+ }
+
+ public String getClassName()
+ {
+ return mClassName;
+ }
+
+ public ClassGenerator setClassName(String name)
+ {
+ mClassName = name;
+ return this;
+ }
+
+ public ClassGenerator addInterface(String cn)
+ {
+ if( mInterfaces == null )
+ mInterfaces = new HashSet<String>();
+ mInterfaces.add(cn);
+ return this;
+ }
+
+ public ClassGenerator addInterface(Class<?> cl)
+ {
+ return addInterface(cl.getName());
+ }
+
+ public ClassGenerator setSuperClass(String cn)
+ {
+ mSuperClass = cn;
+ return this;
+ }
+
+ public ClassGenerator setSuperClass(Class<?> cl)
+ {
+ mSuperClass = cl.getName();
+ return this;
+ }
+
+ public ClassGenerator addField(String code)
+ {
+ if( mFields == null )
+ mFields = new ArrayList<String>();
+ mFields.add(code);
+ return this;
+ }
+
+ public ClassGenerator addField(String name, int mod, Class<?> type)
+ {
+ return addField(name, mod, type, null);
+ }
+
+ public ClassGenerator addField(String name, int mod, Class<?> type, String def)
+ {
+ StringBuilder sb = new StringBuilder();
+ sb.append(modifier(mod)).append(' ').append(ReflectUtils.getName(type)).append(' ');
+ sb.append(name);
+ if( def != null && def.length() > 0 )
+ {
+ sb.append('=');
+ sb.append(def);
+ }
+ sb.append(';');
+ return addField(sb.toString());
+ }
+
+ public ClassGenerator addMethod(String code)
+ {
+ if( mMethods == null )
+ mMethods = new ArrayList<String>();
+ mMethods.add(code);
+ return this;
+ }
+
+ public ClassGenerator addMethod(String name, int mod, Class<?> rt, Class<?>[] pts, String body)
+ {
+ return addMethod(name, mod, rt, pts, null, body);
+ }
+
+ public ClassGenerator addMethod(String name, int mod, Class<?> rt, Class<?>[] pts, Class<?>[] ets, String body)
+ {
+ StringBuilder sb = new StringBuilder();
+ sb.append(modifier(mod)).append(' ').append(ReflectUtils.getName(rt)).append(' ').append(name);
+ sb.append('(');
+ for(int i=0;i<pts.length;i++)
+ {
+ if( i > 0 )
+ sb.append(',');
+ sb.append(ReflectUtils.getName(pts[i]));
+ sb.append(" arg").append(i);
+ }
+ sb.append(')');
+ if( ets != null && ets.length > 0 )
+ {
+ sb.append(" throws ");
+ for(int i=0;i<ets.length;i++)
+ {
+ if( i > 0 )
+ sb.append(',');
+ sb.append(ReflectUtils.getName(ets[i]));
+ }
+ }
+ sb.append('{').append(body).append('}');
+ return addMethod(sb.toString());
+ }
+
+ public ClassGenerator addMethod(Method m)
+ {
+ addMethod(m.getName(), m);
+ return this;
+ }
+
+ public ClassGenerator addMethod(String name, Method m)
+ {
+ String desc = name + ReflectUtils.getDescWithoutMethodName(m);
+ addMethod(':' + desc);
+ if( mCopyMethods == null )
+ mCopyMethods = new ConcurrentHashMap<String, Method>(8);
+ mCopyMethods.put(desc, m);
+ return this;
+ }
+
+ public ClassGenerator addConstructor(String code)
+ {
+ if( mConstructors == null )
+ mConstructors = new LinkedList<String>();
+ mConstructors.add(code);
+ return this;
+ }
+
+ public ClassGenerator addConstructor(int mod, Class<?>[] pts, String body)
+ {
+ return addConstructor(mod, pts, null, body);
+ }
+
+ public ClassGenerator addConstructor(int mod, Class<?>[] pts, Class<?>[] ets, String body)
+ {
+ StringBuilder sb = new StringBuilder();
+ sb.append(modifier(mod)).append(' ').append(SIMPLE_NAME_TAG);
+ sb.append('(');
+ for(int i=0;i<pts.length;i++)
+ {
+ if( i > 0 )
+ sb.append(',');
+ sb.append(ReflectUtils.getName(pts[i]));
+ sb.append(" arg").append(i);
+ }
+ sb.append(')');
+ if( ets != null && ets.length > 0 )
+ {
+ sb.append(" throws ");
+ for(int i=0;i<ets.length;i++)
+ {
+ if( i > 0 )
+ sb.append(',');
+ sb.append(ReflectUtils.getName(ets[i]));
+ }
+ }
+ sb.append('{').append(body).append('}');
+ return addConstructor(sb.toString());
+ }
+
+ public ClassGenerator addConstructor(Constructor<?> c)
+ {
+ String desc = ReflectUtils.getDesc(c);
+ addConstructor(":"+desc);
+ if( mCopyConstructors == null )
+ mCopyConstructors = new ConcurrentHashMap<String, Constructor<?>>(4);
+ mCopyConstructors.put(desc, c);
+ return this;
+ }
+
+ public ClassGenerator addDefaultConstructor()
+ {
+ mDefaultConstructor = true;
+ return this;
+ }
+
+ public Class<?> toClass()
+ {
+ if( mCtc != null )
+ mCtc.detach();
+ long id = CLASS_NAME_COUNTER.getAndIncrement();
+ try
+ {
+ CtClass ctcs = mSuperClass == null ? null : mPool.get(mSuperClass);
+ if( mClassName == null )
+ mClassName = ( mSuperClass == null || javassist.Modifier.isPublic(ctcs.getModifiers())
+ ? ClassGenerator.class.getName() : mSuperClass + "$sc" ) + id;
+ mCtc = mPool.makeClass(mClassName);
+ if( mSuperClass != null )
+ mCtc.setSuperclass(ctcs);
+ mCtc.addInterface(mPool.get(DC.class.getName())); // add dynamic class tag.
+ if( mInterfaces != null )
+ for( String cl : mInterfaces ) mCtc.addInterface(mPool.get(cl));
+ if( mFields != null )
+ for( String code : mFields ) mCtc.addField(CtField.make(code, mCtc));
+ if( mMethods != null )
+ {
+ for( String code : mMethods )
+ {
+ if( code.charAt(0) == ':' )
+ mCtc.addMethod(CtNewMethod.copy(getCtMethod(mCopyMethods.get(code.substring(1))), code.substring(1, code.indexOf('(')), mCtc, null));
+ else
+ mCtc.addMethod(CtNewMethod.make(code, mCtc));
+ }
+ }
+ if( mDefaultConstructor )
+ mCtc.addConstructor(CtNewConstructor.defaultConstructor(mCtc));
+ if( mConstructors != null )
+ {
+ for( String code : mConstructors )
+ {
+ if( code.charAt(0) == ':' )
+ {
+ mCtc.addConstructor(CtNewConstructor.copy(getCtConstructor(mCopyConstructors.get(code.substring(1))), mCtc, null));
+ }
+ else
+ {
+ String[] sn = mCtc.getSimpleName().split("\\$+"); // inner class name include $.
+ mCtc.addConstructor(CtNewConstructor.make(code.replaceFirst(SIMPLE_NAME_TAG, sn[sn.length-1]), mCtc));
+ }
+ }
+ }
+ return mCtc.toClass();
+ }
+ catch(RuntimeException e)
+ {
+ throw e;
+ }
+ catch(NotFoundException e)
+ {
+ throw new RuntimeException(e.getMessage(), e);
+ }
+ catch(CannotCompileException e)
+ {
+ throw new RuntimeException(e.getMessage(), e);
+ }
+ }
+
+ public void release()
+ {
+ if( mCtc != null ) mCtc.detach();
+ if( mInterfaces != null ) mInterfaces.clear();
+ if( mFields != null ) mFields.clear();
+ if( mMethods != null ) mMethods.clear();
+ if( mConstructors != null ) mConstructors.clear();
+ if( mCopyMethods != null ) mCopyMethods.clear();
+ if( mCopyConstructors != null ) mCopyConstructors.clear();
+ }
+
+ private CtClass getCtClass(Class<?> c) throws NotFoundException
+ {
+ return mPool.get(c.getName());
+ }
+
+ private CtMethod getCtMethod(Method m) throws NotFoundException
+ {
+ return getCtClass(m.getDeclaringClass()).getMethod(m.getName(),ReflectUtils.getDescWithoutMethodName(m));
+ }
+
+ private CtConstructor getCtConstructor(Constructor<?> c) throws NotFoundException
+ {
+ return getCtClass(c.getDeclaringClass()).getConstructor(ReflectUtils.getDesc(c));
+ }
+
+ private static String modifier(int mod)
+ {
+ if( Modifier.isPublic(mod) ) return "public";
+ if( Modifier.isProtected(mod) ) return "protected";
+ if( Modifier.isPrivate(mod) ) return "private";
+ return "";
+ }
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/bytecode/Mixin.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/bytecode/Mixin.java
new file mode 100644
index 0000000..751a662
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/bytecode/Mixin.java
@@ -0,0 +1,241 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.bytecode;
+
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicLong;
+
+import com.alibaba.dubbo.common.utils.ClassHelper;
+import com.alibaba.dubbo.common.utils.ReflectUtils;
+
+/**
+ * Mixin
+ *
+ * @author qian.lei
+ */
+
+public abstract class Mixin
+{
+ private static AtomicLong MIXIN_CLASS_COUNTER = new AtomicLong(0);
+
+ private static final String PACKAGE_NAME = Mixin.class.getPackage().getName();
+
+ public static interface MixinAware{ void setMixinInstance(Object instance); }
+
+ /**
+ * mixin interface and delegates.
+ * all class must be public.
+ *
+ * @param ics interface class array.
+ * @param dc delegate class.
+ * @return Mixin instance.
+ */
+ public static Mixin mixin(Class<?>[] ics, Class<?> dc)
+ {
+ return mixin(ics, new Class[]{dc});
+ }
+
+ /**
+ * mixin interface and delegates.
+ * all class must be public.
+ *
+ * @param ics interface class array.
+ * @param dc delegate class.
+ * @param cl class loader.
+ * @return Mixin instance.
+ */
+ public static Mixin mixin(Class<?>[] ics, Class<?> dc, ClassLoader cl)
+ {
+ return mixin(ics, new Class[]{dc}, cl);
+ }
+
+ /**
+ * mixin interface and delegates.
+ * all class must be public.
+ *
+ * @param ics interface class array.
+ * @param dcs delegate class array.
+ * @return Mixin instance.
+ */
+ public static Mixin mixin(Class<?>[] ics, Class<?>[] dcs)
+ {
+ return mixin(ics, dcs, ClassHelper.getClassLoader(ics[0]));
+ }
+
+ /**
+ * mixin interface and delegates.
+ * all class must be public.
+ *
+ * @param ics interface class array.
+ * @param dcs delegate class array.
+ * @param cl class loader.
+ * @return Mixin instance.
+ */
+ public static Mixin mixin(Class<?>[] ics, Class<?>[] dcs, ClassLoader cl)
+ {
+ assertInterfaceArray(ics);
+
+ long id = MIXIN_CLASS_COUNTER.getAndIncrement();
+ String pkg = null;
+ ClassGenerator ccp = null, ccm = null;
+ try
+ {
+ ccp = ClassGenerator.newInstance(cl);
+
+ // impl constructor
+ StringBuilder code = new StringBuilder();
+ for(int i=0;i<dcs.length;i++)
+ {
+ if( !Modifier.isPublic(dcs[i].getModifiers()) )
+ {
+ String npkg = dcs[i].getPackage().getName();
+ if( pkg == null )
+ {
+ pkg = npkg;
+ }
+ else
+ {
+ if( !pkg.equals(npkg) )
+ throw new IllegalArgumentException("non-public interfaces class from different packages");
+ }
+ }
+
+ ccp.addField("private " + dcs[i].getName() + " d" + i + ";");
+
+ code.append("d").append(i).append(" = (").append(dcs[i].getName()).append(")$1[").append(i).append("];\n");
+ if( MixinAware.class.isAssignableFrom(dcs[i]) )
+ code.append("d").append(i).append(".setMixinInstance(this);\n");
+ }
+ ccp.addConstructor(Modifier.PUBLIC, new Class<?>[]{ Object[].class }, code.toString());
+
+ // impl methods.
+ Set<String> worked = new HashSet<String>();
+ for(int i=0;i<ics.length;i++)
+ {
+ if( !Modifier.isPublic(ics[i].getModifiers()) )
+ {
+ String npkg = ics[i].getPackage().getName();
+ if( pkg == null )
+ {
+ pkg = npkg;
+ }
+ else
+ {
+ if( !pkg.equals(npkg) )
+ throw new IllegalArgumentException("non-public delegate class from different packages");
+ }
+ }
+
+ ccp.addInterface(ics[i]);
+
+ for( Method method : ics[i].getMethods() )
+ {
+ if( "java.lang.Object".equals(method.getDeclaringClass().getName()) )
+ continue;
+
+ String desc = ReflectUtils.getDesc(method);
+ if( worked.contains(desc) )
+ continue;
+ worked.add(desc);
+
+ int ix = findMethod(dcs, desc);
+ if( ix < 0 )
+ throw new RuntimeException("Missing method [" + desc + "] implement.");
+
+ Class<?> rt = method.getReturnType();
+ String mn = method.getName();
+ if( Void.TYPE.equals(rt) )
+ ccp.addMethod(mn, method.getModifiers(), rt, method.getParameterTypes(), method.getExceptionTypes(),
+ "d" + ix + "." + mn + "($$);");
+ else
+ ccp.addMethod(mn, method.getModifiers(), rt, method.getParameterTypes(), method.getExceptionTypes(),
+ "return ($r)d" + ix + "." + mn + "($$);");
+ }
+ }
+
+ if( pkg == null )
+ pkg = PACKAGE_NAME;
+
+ // create MixinInstance class.
+ String micn = pkg + ".mixin" + id;
+ ccp.setClassName(micn);
+ ccp.toClass();
+
+ // create Mixin class.
+ String fcn = Mixin.class.getName() + id;
+ ccm = ClassGenerator.newInstance(cl);
+ ccm.setClassName(fcn);
+ ccm.addDefaultConstructor();
+ ccm.setSuperClass(Mixin.class.getName());
+ ccm.addMethod("public Object newInstance(Object[] delegates){ return new " + micn + "($1); }");
+ Class<?> mixin = ccm.toClass();
+ return (Mixin)mixin.newInstance();
+ }
+ catch(RuntimeException e)
+ {
+ throw e;
+ }
+ catch(Exception e)
+ {
+ throw new RuntimeException(e.getMessage(), e);
+ }
+ finally
+ {
+ // release ClassGenerator
+ if( ccp != null )
+ ccp.release();
+ if( ccm != null )
+ ccm.release();
+ }
+ }
+
+ /**
+ * new Mixin instance.
+ *
+ * @param ds delegates instance.
+ * @return instance.
+ */
+ abstract public Object newInstance(Object[] ds);
+
+ protected Mixin(){}
+
+ private static int findMethod(Class<?>[] dcs, String desc)
+ {
+ Class<?> cl;
+ Method[] methods;
+ for(int i=0;i<dcs.length;i++)
+ {
+ cl = dcs[i];
+ methods = cl.getMethods();
+ for( Method method : methods )
+ {
+ if( desc.equals(ReflectUtils.getDesc(method)) )
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ private static void assertInterfaceArray(Class<?>[] ics)
+ {
+ for(int i=0;i<ics.length;i++)
+ if( !ics[i].isInterface() )
+ throw new RuntimeException("Class " + ics[i].getName() + " is not a interface.");
+ }
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/bytecode/NoSuchMethodException.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/bytecode/NoSuchMethodException.java
new file mode 100644
index 0000000..a8083e8
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/bytecode/NoSuchMethodException.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.bytecode;
+
+/**
+ * NoSuchMethodException.
+ *
+ * @author qian.lei
+ */
+
+public class NoSuchMethodException extends RuntimeException
+{
+ private static final long serialVersionUID = -2725364246023268766L;
+
+ public NoSuchMethodException()
+ {
+ super();
+ }
+
+ public NoSuchMethodException(String msg)
+ {
+ super(msg);
+ }
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/bytecode/NoSuchPropertyException.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/bytecode/NoSuchPropertyException.java
new file mode 100644
index 0000000..1384fdd
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/bytecode/NoSuchPropertyException.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.bytecode;
+
+/**
+ * NoSuchPropertyException.
+ *
+ * @author qian.lei
+ */
+
+public class NoSuchPropertyException extends RuntimeException
+{
+ private static final long serialVersionUID = -2725364246023268766L;
+
+ public NoSuchPropertyException()
+ {
+ super();
+ }
+
+ public NoSuchPropertyException(String msg)
+ {
+ super(msg);
+ }
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/bytecode/Proxy.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/bytecode/Proxy.java
new file mode 100644
index 0000000..03b27f9
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/bytecode/Proxy.java
@@ -0,0 +1,286 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.bytecode;
+
+import java.lang.ref.Reference;
+import java.lang.ref.WeakReference;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.WeakHashMap;
+import java.util.concurrent.atomic.AtomicLong;
+
+import com.alibaba.dubbo.common.utils.ClassHelper;
+import com.alibaba.dubbo.common.utils.ReflectUtils;
+
+/**
+ * Proxy.
+ *
+ * @author qian.lei
+ */
+
+public abstract class Proxy
+{
+ private static final AtomicLong PROXY_CLASS_COUNTER = new AtomicLong(0);
+
+ private static final String PACKAGE_NAME = Proxy.class.getPackage().getName();
+
+ public static final InvocationHandler RETURN_NULL_INVOKER = new InvocationHandler(){
+ public Object invoke(Object proxy, Method method, Object[] args){ return null; }
+ };
+
+ public static final InvocationHandler THROW_UNSUPPORTED_INVOKER = new InvocationHandler(){
+ public Object invoke(Object proxy, Method method, Object[] args){ throw new UnsupportedOperationException("Method [" + ReflectUtils.getName(method) + "] unimplemented."); }
+ };
+
+ private static final Map<ClassLoader, Map<String, Object>> ProxyCacheMap = new WeakHashMap<ClassLoader, Map<String, Object>>();
+
+ private static final Object PendingGenerationMarker = new Object();
+
+ /**
+ * Get proxy.
+ *
+ * @param ics interface class array.
+ * @return Proxy instance.
+ */
+ public static Proxy getProxy(Class<?>... ics)
+ {
+ return getProxy(ClassHelper.getClassLoader(ics[0]), ics);
+ }
+
+ /**
+ * Get proxy.
+ * @param cl class loader.
+ * @param ics interface class array.
+ *
+ * @return Proxy instance.
+ */
+ public static Proxy getProxy(ClassLoader cl, Class<?>... ics)
+ {
+ if( ics.length > 65535 )
+ throw new IllegalArgumentException("interface limit exceeded");
+
+ StringBuilder sb = new StringBuilder();
+ for(int i=0;i<ics.length;i++)
+ {
+ String itf = ics[i].getName();
+ if( !ics[i].isInterface() )
+ throw new RuntimeException(itf + " is not a interface.");
+
+ Class<?> tmp = null;
+ try
+ {
+ tmp = Class.forName(itf, false, cl);
+ }
+ catch(ClassNotFoundException e)
+ {}
+
+ if( tmp != ics[i] )
+ throw new IllegalArgumentException(ics[i] + " is not visible from class loader");
+
+ sb.append(itf).append(';');
+ }
+
+ // use interface class name list as key.
+ String key = sb.toString();
+
+ // get cache by class loader.
+ Map<String, Object> cache;
+ synchronized( ProxyCacheMap )
+ {
+ cache = ProxyCacheMap.get(cl);
+ if( cache == null )
+ {
+ cache = new HashMap<String, Object>();
+ ProxyCacheMap.put(cl, cache);
+ }
+ }
+
+ Proxy proxy = null;
+ synchronized( cache )
+ {
+ do
+ {
+ Object value = cache.get(key);
+ if( value instanceof Reference<?> )
+ {
+ proxy = (Proxy)((Reference<?>)value).get();
+ if( proxy != null )
+ return proxy;
+ }
+
+ if( value == PendingGenerationMarker )
+ {
+ try{ cache.wait(); }catch(InterruptedException e){}
+ }
+ else
+ {
+ cache.put(key, PendingGenerationMarker);
+ break;
+ }
+ }
+ while( true );
+ }
+
+ long id = PROXY_CLASS_COUNTER.getAndIncrement();
+ String pkg = null;
+ ClassGenerator ccp = null, ccm = null;
+ try
+ {
+ ccp = ClassGenerator.newInstance(cl);
+
+ Set<String> worked = new HashSet<String>();
+ List<Method> methods = new ArrayList<Method>();
+
+ for(int i=0;i<ics.length;i++)
+ {
+ if( !Modifier.isPublic(ics[i].getModifiers()) )
+ {
+ String npkg = ics[i].getPackage().getName();
+ if( pkg == null )
+ {
+ pkg = npkg;
+ }
+ else
+ {
+ if( !pkg.equals(npkg) )
+ throw new IllegalArgumentException("non-public interfaces from different packages");
+ }
+ }
+ ccp.addInterface(ics[i]);
+
+ for( Method method : ics[i].getMethods() )
+ {
+ String desc = ReflectUtils.getDesc(method);
+ if( worked.contains(desc) )
+ continue;
+ worked.add(desc);
+
+ int ix = methods.size();
+ Class<?> rt = method.getReturnType();
+ Class<?>[] pts = method.getParameterTypes();
+
+ StringBuilder code = new StringBuilder("Object[] args = new Object[").append(pts.length).append("];");
+ for(int j=0;j<pts.length;j++)
+ code.append(" args[").append(j).append("] = ($w)$").append(j+1).append(";");
+ code.append(" Object ret = handler.invoke(this, methods[" + ix + "], args);");
+ if( !Void.TYPE.equals(rt) )
+ code.append(" return ").append(asArgument(rt, "ret")).append(";");
+
+ methods.add(method);
+ ccp.addMethod(method.getName(), method.getModifiers(), rt, pts, method.getExceptionTypes(), code.toString());
+ }
+ }
+
+ if( pkg == null )
+ pkg = PACKAGE_NAME;
+
+ // create ProxyInstance class.
+ String pcn = pkg + ".proxy" + id;
+ ccp.setClassName(pcn);
+ ccp.addField("public static java.lang.reflect.Method[] methods;");
+ ccp.addField("private " + InvocationHandler.class.getName() + " handler;");
+ ccp.addConstructor(Modifier.PUBLIC, new Class<?>[]{ InvocationHandler.class }, new Class<?>[0], "handler=$1;");
+ Class<?> clazz = ccp.toClass();
+ clazz.getField("methods").set(null, methods.toArray(new Method[0]));
+
+ // create Proxy class.
+ String fcn = Proxy.class.getName() + id;
+ ccm = ClassGenerator.newInstance(cl);
+ ccm.setClassName(fcn);
+ ccm.addDefaultConstructor();
+ ccm.setSuperClass(Proxy.class);
+ ccm.addMethod("public Object newInstance(" + InvocationHandler.class.getName() + " h){ return new " + pcn + "($1); }");
+ Class<?> pc = ccm.toClass();
+ proxy = (Proxy)pc.newInstance();
+ }
+ catch(RuntimeException e)
+ {
+ throw e;
+ }
+ catch(Exception e)
+ {
+ throw new RuntimeException(e.getMessage(), e);
+ }
+ finally
+ {
+ // release ClassGenerator
+ if( ccp != null )
+ ccp.release();
+ if( ccm != null )
+ ccm.release();
+ synchronized( cache )
+ {
+ if( proxy == null )
+ cache.remove(key);
+ else
+ cache.put(key, new WeakReference<Proxy>(proxy));
+ cache.notifyAll();
+ }
+ }
+ return proxy;
+ }
+
+ /**
+ * get instance with default handler.
+ *
+ * @return instance.
+ */
+ public Object newInstance()
+ {
+ return newInstance(THROW_UNSUPPORTED_INVOKER);
+ }
+
+ /**
+ * get instance with special handler.
+ *
+ * @return instance.
+ */
+ abstract public Object newInstance(InvocationHandler handler);
+
+ protected Proxy(){}
+
+ private static String asArgument(Class<?> cl, String name)
+ {
+ if( cl.isPrimitive() )
+ {
+ if( Boolean.TYPE == cl )
+ return name + "==null?false:((Boolean)" + name + ").booleanValue()";
+ if( Byte.TYPE == cl )
+ return name + "==null?(byte)0:((Byte)" + name + ").byteValue()";
+ if( Character.TYPE == cl )
+ return name + "==null?(char)0:((Character)" + name + ").charValue()";
+ if( Double.TYPE == cl )
+ return name + "==null?(double)0:((Double)" + name + ").doubleValue()";
+ if( Float.TYPE == cl )
+ return name + "==null?(float)0:((Float)" + name + ").floatValue()";
+ if( Integer.TYPE == cl )
+ return name + "==null?(int)0:((Integer)" + name + ").intValue()";
+ if( Long.TYPE == cl )
+ return name + "==null?(long)0:((Long)" + name + ").longValue()";
+ if( Short.TYPE == cl )
+ return name + "==null?(short)0:((Short)" + name + ").shortValue()";
+ throw new RuntimeException(name+" is unknown primitive type.");
+ }
+ return "(" + ReflectUtils.getName(cl) + ")"+name;
+ }
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/bytecode/Wrapper.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/bytecode/Wrapper.java
new file mode 100644
index 0000000..3cd08b5
--- /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 pn property name array.
+ * @param pv 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 ) ");
+
+ boolean override = false;
+ for( Method m2 : methods ) {
+ if (m != m2 && m.getName().equals(m2.getName())) {
+ override = true;
+ break;
+ }
+ }
+ if (override) {
+ int len = m.getParameterTypes().length;
+ c3.append(" && ").append(" $3.length == ").append(len);
+ if (len > 0) {
+ for (int l = 0; l < len; l ++) {
+ c3.append(" && ").append(" $3[").append(l).append("].getName().equals(\"")
+ .append(m.getParameterTypes()[l].getName()).append("\")");
+ }
+ }
+ }
+
+ c3.append(" ) { ");
+
+ if( m.getReturnType() == Void.TYPE )
+ c3.append(" w.").append(mn).append('(').append(args(m.getParameterTypes(), "$4")).append(");").append(" return null;");
+ else
+ c3.append(" return ($w)w.").append(mn).append('(').append(args(m.getParameterTypes(), "$4")).append(");");
+
+ c3.append(" }");
+
+ mns.add(mn);
+ if( m.getDeclaringClass() == c )
+ dmns.add(mn);
+ ms.put(ReflectUtils.getDesc(m), m);
+ }
+ c3.append(" } catch(Throwable e) { " );
+ c3.append(" throw new java.lang.reflect.InvocationTargetException(e); " );
+ c3.append(" }");
+ c3.append(" throw new " + NoSuchMethodException.class.getName() + "(\"Not found method \\\"\"+$2+\"\\\" in class " + c.getName() + ".\"); }");
+
+ // deal with get/set method.
+ Matcher matcher;
+ for( Map.Entry<String,Method> entry : ms.entrySet() )
+ {
+ String md = entry.getKey();
+ Method method = (Method)entry.getValue();
+ if( ( matcher = ReflectUtils.GETTER_METHOD_DESC_PATTERN.matcher(md) ).matches() )
+ {
+ String pn = propertyName(matcher.group(1));
+ c2.append(" if( $2.equals(\"").append(pn).append("\") ){ return ($w)w.").append(method.getName()).append("(); }");
+ pts.put(pn, method.getReturnType());
+ }
+ else if( ( matcher = ReflectUtils.IS_HAS_CAN_METHOD_DESC_PATTERN.matcher(md) ).matches() )
+ {
+ String pn = propertyName(matcher.group(1));
+ c2.append(" if( $2.equals(\"").append(pn).append("\") ){ return ($w)w.").append(method.getName()).append("(); }");
+ pts.put(pn, method.getReturnType());
+ }
+ else if( ( matcher = ReflectUtils.SETTER_METHOD_DESC_PATTERN.matcher(md) ).matches() )
+ {
+ Class<?> pt = method.getParameterTypes()[0];
+ String pn = propertyName(matcher.group(1));
+ c1.append(" if( $2.equals(\"").append(pn).append("\") ){ w.").append(method.getName()).append("(").append(arg(pt,"$3")).append("); return; }");
+ pts.put(pn, pt);
+ }
+ }
+ c1.append(" throw new " + NoSuchPropertyException.class.getName() + "(\"Not found property \\\"\"+$2+\"\\\" filed or setter method in class " + c.getName() + ".\"); }");
+ c2.append(" throw new " + NoSuchPropertyException.class.getName() + "(\"Not found property \\\"\"+$2+\"\\\" filed or setter method in class " + c.getName() + ".\"); }");
+
+ // make class
+ long id = WRAPPER_CLASS_COUNTER.getAndIncrement();
+ ClassGenerator cc = ClassGenerator.newInstance(cl);
+ cc.setClassName( ( Modifier.isPublic(c.getModifiers()) ? Wrapper.class.getName() : c.getName() + "$sw" ) + id );
+ cc.setSuperClass(Wrapper.class);
+
+ cc.addDefaultConstructor();
+ cc.addField("public static String[] pns;"); // property name array.
+ cc.addField("public static " + Map.class.getName() + " pts;"); // property type map.
+ cc.addField("public static String[] mns;"); // all method name array.
+ cc.addField("public static String[] dmns;"); // declared method name array.
+ for(int i=0,len=ms.size();i<len;i++)
+ cc.addField("public static Class[] mts" + i + ";");
+
+ cc.addMethod("public String[] getPropertyNames(){ return pns; }");
+ cc.addMethod("public boolean hasProperty(String n){ return pts.containsKey($1); }");
+ cc.addMethod("public Class getPropertyType(String n){ return (Class)pts.get($1); }");
+ cc.addMethod("public String[] getMethodNames(){ return mns; }");
+ cc.addMethod("public String[] getDeclaredMethodNames(){ return dmns; }");
+ cc.addMethod(c1.toString());
+ cc.addMethod(c2.toString());
+ cc.addMethod(c3.toString());
+
+ try
+ {
+ Class<?> wc = cc.toClass();
+ // setup static field.
+ wc.getField("pts").set(null, pts);
+ wc.getField("pns").set(null, pts.keySet().toArray(new String[0]));
+ wc.getField("mns").set(null, mns.toArray(new String[0]));
+ wc.getField("dmns").set(null, dmns.toArray(new String[0]));
+ int ix = 0;
+ for( Method m : ms.values() )
+ wc.getField("mts" + ix++).set(null, m.getParameterTypes());
+ return (Wrapper)wc.newInstance();
+ }
+ catch(RuntimeException e)
+ {
+ throw e;
+ }
+ catch(Throwable e)
+ {
+ throw new RuntimeException(e.getMessage(), e);
+ }
+ finally
+ {
+ cc.release();
+ ms.clear();
+ mns.clear();
+ dmns.clear();
+ }
+ }
+
+ private static String arg(Class<?> cl, String name)
+ {
+ if( cl.isPrimitive() )
+ {
+ if( cl == Boolean.TYPE )
+ return "((Boolean)" + name + ").booleanValue()";
+ if( cl == Byte.TYPE )
+ return "((Byte)" + name + ").byteValue()";
+ if( cl == Character.TYPE )
+ return "((Character)" + name + ").charValue()";
+ if( cl == Double.TYPE )
+ return "((Number)" + name + ").doubleValue()";
+ if( cl == Float.TYPE )
+ return "((Number)" + name + ").floatValue()";
+ if( cl == Integer.TYPE )
+ return "((Number)" + name + ").intValue()";
+ if( cl == Long.TYPE )
+ return "((Number)" + name + ").longValue()";
+ if( cl == Short.TYPE )
+ return "((Number)" + name + ").shortValue()";
+ throw new RuntimeException("Unknown primitive type: " + cl.getName());
+ }
+ return "(" + ReflectUtils.getName(cl) + ")" + name;
+ }
+
+ private static String args(Class<?>[] cs,String name)
+ {
+ int len = cs.length;
+ if( len == 0 ) return "";
+ StringBuilder sb = new StringBuilder();
+ for(int i=0;i<len;i++)
+ {
+ if( i > 0 )
+ sb.append(',');
+ sb.append(arg(cs[i],name+"["+i+"]"));
+ }
+ return sb.toString();
+ }
+
+ private static String propertyName(String pn)
+ {
+ return pn.length() == 1 || Character.isLowerCase(pn.charAt(1)) ? Character.toLowerCase(pn.charAt(0)) + pn.substring(1) : pn;
+ }
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/io/Bytes.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/io/Bytes.java
new file mode 100644
index 0000000..c774da9
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/io/Bytes.java
@@ -0,0 +1,970 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.io;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.zip.DeflaterOutputStream;
+import java.util.zip.InflaterInputStream;
+
+import com.alibaba.dubbo.common.utils.IOUtils;
+
+/**
+ * CodecUtils.
+ *
+ * @author qian.lei
+ */
+
+public class Bytes
+{
+ private static final String C64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; //default base64.
+
+ private static final char[] BASE16 = {'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'}, BASE64 = C64.toCharArray();
+
+ private static final int MASK4 = 0x0f, MASK6 = 0x3f, MASK8 = 0xff;
+
+ private static final Map<Integer, byte[]> DECODE_TABLE_MAP = new ConcurrentHashMap<Integer, byte[]>();
+
+ private static ThreadLocal<MessageDigest> MD = new ThreadLocal<MessageDigest>();
+
+ /**
+ * byte array copy.
+ *
+ * @param src src.
+ * @param length new length.
+ * @return new byte array.
+ */
+ public static byte[] copyOf(byte[] src, int length)
+ {
+ byte[] dest = new byte[length];
+ System.arraycopy(src, 0, dest, 0, Math.min(src.length, length));
+ return dest;
+ }
+
+ /**
+ * to byte array.
+ *
+ * @param v value.
+ * @return byte[].
+ */
+ public static byte[] short2bytes(short v)
+ {
+ byte[] ret = { 0, 0 };
+ short2bytes(v, ret);
+ return ret;
+ }
+
+ /**
+ * to byte array.
+ *
+ * @param v value.
+ * @param b byte array.
+ */
+ public static void short2bytes(short v, byte[] b)
+ {
+ short2bytes(v, b, 0);
+ }
+
+ /**
+ * to byte array.
+ *
+ * @param v value.
+ * @param b byte array.
+ */
+ public static void short2bytes(short v, byte[] b, int off)
+ {
+ b[off + 1] = (byte) v;
+ b[off + 0] = (byte) (v >>> 8);
+ }
+
+ /**
+ * to byte array.
+ *
+ * @param v value.
+ * @return byte[].
+ */
+ public static byte[] int2bytes(int v)
+ {
+ byte[] ret = { 0, 0, 0, 0 };
+ int2bytes(v, ret);
+ return ret;
+ }
+
+ /**
+ * to byte array.
+ *
+ * @param v value.
+ * @param b byte array.
+ */
+ public static void int2bytes(int v, byte[] b)
+ {
+ int2bytes(v, b, 0);
+ }
+
+ /**
+ * to byte array.
+ *
+ * @param v value.
+ * @param b byte array.
+ * @param off array offset.
+ */
+ public static void int2bytes(int v, byte[] b, int off)
+ {
+ b[off + 3] = (byte) v;
+ b[off + 2] = (byte) (v >>> 8);
+ b[off + 1] = (byte) (v >>> 16);
+ b[off + 0] = (byte) (v >>> 24);
+ }
+
+ /**
+ * to byte array.
+ *
+ * @param v value.
+ * @return byte[].
+ */
+ public static byte[] float2bytes(float v)
+ {
+ byte[] ret = { 0, 0, 0, 0 };
+ float2bytes(v, ret);
+ return ret;
+ }
+
+ /**
+ * to byte array.
+ *
+ * @param v value.
+ * @param b byte array.
+ */
+ public static void float2bytes(float v, byte[] b)
+ {
+ float2bytes(v, b, 0);
+ }
+
+ /**
+ * to byte array.
+ *
+ * @param v value.
+ * @param b byte array.
+ * @param off array offset.
+ */
+ public static void float2bytes(float v, byte[] b, int off)
+ {
+ int i = Float.floatToIntBits(v);
+ b[off + 3] = (byte) i;
+ b[off + 2] = (byte) (i >>> 8);
+ b[off + 1] = (byte) (i >>> 16);
+ b[off + 0] = (byte) (i >>> 24);
+ }
+
+ /**
+ * to byte array.
+ *
+ * @param v value.
+ * @return byte[].
+ */
+ public static byte[] long2bytes(long v)
+ {
+ byte[] ret = { 0, 0, 0, 0, 0, 0, 0, 0 };
+ long2bytes(v, ret);
+ return ret;
+ }
+
+ /**
+ * to byte array.
+ *
+ * @param v value.
+ * @param b byte array.
+ */
+ public static void long2bytes(long v, byte[] b)
+ {
+ long2bytes(v, b, 0);
+ }
+
+ /**
+ * to byte array.
+ *
+ * @param v value.
+ * @param b byte array.
+ * @param off array offset.
+ */
+ public static void long2bytes(long v, byte[] b, int off)
+ {
+ b[off + 7] = (byte) v;
+ b[off + 6] = (byte) (v >>> 8);
+ b[off + 5] = (byte) (v >>> 16);
+ b[off + 4] = (byte) (v >>> 24);
+ b[off + 3] = (byte) (v >>> 32);
+ b[off + 2] = (byte) (v >>> 40);
+ b[off + 1] = (byte) (v >>> 48);
+ b[off + 0] = (byte) (v >>> 56);
+ }
+
+ /**
+ * to byte array.
+ *
+ * @param v value.
+ * @return byte[].
+ */
+ public static byte[] double2bytes(double v)
+ {
+ byte[] ret = { 0, 0, 0, 0, 0, 0, 0, 0 };
+ double2bytes(v, ret);
+ return ret;
+ }
+
+ /**
+ * to byte array.
+ *
+ * @param v value.
+ * @param b byte array.
+ */
+ public static void double2bytes(double v, byte[] b)
+ {
+ double2bytes(v, b, 0);
+ }
+
+ /**
+ * to byte array.
+ *
+ * @param v value.
+ * @param b byte array.
+ * @param off array offset.
+ */
+ public static void double2bytes(double v, byte[] b, int off)
+ {
+ long j = Double.doubleToLongBits(v);
+ b[off + 7] = (byte) j;
+ b[off + 6] = (byte) (j >>> 8);
+ b[off + 5] = (byte) (j >>> 16);
+ b[off + 4] = (byte) (j >>> 24);
+ b[off + 3] = (byte) (j >>> 32);
+ b[off + 2] = (byte) (j >>> 40);
+ b[off + 1] = (byte) (j >>> 48);
+ b[off + 0] = (byte) (j >>> 56);
+ }
+
+ /**
+ * to short.
+ *
+ * @param b byte array.
+ * @return short.
+ */
+ public static short bytes2short(byte[] b)
+ {
+ return bytes2short(b, 0);
+ }
+
+ /**
+ * to short.
+ *
+ * @param b byte array.
+ * @param off offset.
+ * @return short.
+ */
+ public static short bytes2short(byte[] b, int off)
+ {
+ return (short) (((b[off + 1] & 0xFF) << 0) +
+ ((b[off + 0]) << 8));
+ }
+
+ /**
+ * to int.
+ *
+ * @param b byte array.
+ * @return int.
+ */
+ public static int bytes2int(byte[] b)
+ {
+ return bytes2int(b, 0);
+ }
+
+ /**
+ * to int.
+ *
+ * @param b byte array.
+ * @param off offset.
+ * @return int.
+ */
+ public static int bytes2int(byte[] b, int off)
+ {
+ return ((b[off + 3] & 0xFF) << 0) +
+ ((b[off + 2] & 0xFF) << 8) +
+ ((b[off + 1] & 0xFF) << 16) +
+ ((b[off + 0]) << 24);
+ }
+
+ /**
+ * to int.
+ *
+ * @param b byte array.
+ * @return int.
+ */
+ public static float bytes2float(byte[] b)
+ {
+ return bytes2float(b, 0);
+ }
+
+ /**
+ * to int.
+ *
+ * @param b byte array.
+ * @param off offset.
+ * @return int.
+ */
+ public static float bytes2float(byte[] b, int off)
+ {
+ int i = ((b[off + 3] & 0xFF) << 0) +
+ ((b[off + 2] & 0xFF) << 8) +
+ ((b[off + 1] & 0xFF) << 16) +
+ ((b[off + 0]) << 24);
+ return Float.intBitsToFloat(i);
+ }
+
+ /**
+ * to long.
+ *
+ * @param b byte array.
+ * @return long.
+ */
+ public static long bytes2long(byte[] b)
+ {
+ return bytes2long(b,0);
+ }
+
+ /**
+ * to long.
+ *
+ * @param b byte array.
+ * @param off offset.
+ * @return long.
+ */
+ public static long bytes2long(byte[] b,int off)
+ {
+ return ((b[off + 7] & 0xFFL) << 0) +
+ ((b[off + 6] & 0xFFL) << 8) +
+ ((b[off + 5] & 0xFFL) << 16) +
+ ((b[off + 4] & 0xFFL) << 24) +
+ ((b[off + 3] & 0xFFL) << 32) +
+ ((b[off + 2] & 0xFFL) << 40) +
+ ((b[off + 1] & 0xFFL) << 48) +
+ (((long) b[off + 0]) << 56);
+ }
+
+ /**
+ * to long.
+ *
+ * @param b byte array.
+ * @return double.
+ */
+ public static double bytes2double(byte[] b)
+ {
+ return bytes2double(b,0);
+ }
+
+ /**
+ * to long.
+ *
+ * @param b byte array.
+ * @param off offset.
+ * @return double.
+ */
+ public static double bytes2double(byte[] b, int off)
+ {
+ long j = ((b[off + 7] & 0xFFL) << 0) +
+ ((b[off + 6] & 0xFFL) << 8) +
+ ((b[off + 5] & 0xFFL) << 16) +
+ ((b[off + 4] & 0xFFL) << 24) +
+ ((b[off + 3] & 0xFFL) << 32) +
+ ((b[off + 2] & 0xFFL) << 40) +
+ ((b[off + 1] & 0xFFL) << 48) +
+ (((long) b[off + 0]) << 56);
+ return Double.longBitsToDouble(j);
+ }
+
+ /**
+ * to hex string.
+ *
+ * @param bs byte array.
+ * @return hex string.
+ */
+ public static String bytes2hex(byte[] bs)
+ {
+ return bytes2hex(bs, 0, bs.length);
+ }
+
+ /**
+ * to hex string.
+ *
+ * @param bs byte array.
+ * @param off offset.
+ * @param len length.
+ * @return hex string.
+ */
+ public static String bytes2hex(byte[] bs, int off, int len)
+ {
+ if( off < 0 )
+ throw new IndexOutOfBoundsException("bytes2hex: offset < 0, offset is " + off );
+ if( len < 0 )
+ throw new IndexOutOfBoundsException("bytes2hex: length < 0, length is " + len );
+ if( off + len > bs.length )
+ throw new IndexOutOfBoundsException("bytes2hex: offset + length > array length.");
+
+ byte b;
+ int r = off, w = 0;
+ char[] cs = new char[len*2];
+ for(int i=0;i<len;i++)
+ {
+ b = bs[r++];
+ cs[w++] = BASE16[ b >> 4 & MASK4 ];
+ cs[w++] = BASE16[ b & MASK4 ];
+ }
+ return new String(cs);
+ }
+
+ /**
+ * from hex string.
+ *
+ * @param str hex string.
+ * @return byte array.
+ */
+ public static byte[] hex2bytes(String str)
+ {
+ return hex2bytes(str, 0, str.length());
+ }
+
+ /**
+ * from hex string.
+ *
+ * @param str hex string.
+ * @param off offset.
+ * @param len length.
+ * @return byte array.
+ */
+ public static byte[] hex2bytes(final String str, final int off, int len)
+ {
+ if( ( len & 1 ) == 1 )
+ throw new IllegalArgumentException("hex2bytes: ( len & 1 ) == 1.");
+
+ if( off < 0 )
+ throw new IndexOutOfBoundsException("hex2bytes: offset < 0, offset is " + off );
+ if( len < 0 )
+ throw new IndexOutOfBoundsException("hex2bytes: length < 0, length is " + len );
+ if( off + len > str.length() )
+ throw new IndexOutOfBoundsException("hex2bytes: offset + length > array length.");
+
+ int num = len / 2, r = off, w = 0;
+ byte[] b = new byte[num];
+ for(int i=0;i<num;i++)
+ b[w++] = (byte)( hex(str.charAt(r++)) << 4 | hex(str.charAt(r++)) );
+ return b;
+ }
+
+ /**
+ * to base64 string.
+ *
+ * @param b byte array.
+ * @return base64 string.
+ */
+ public static String bytes2base64(byte[] b)
+ {
+ return bytes2base64(b, 0, b.length, BASE64);
+ }
+
+ /**
+ * to base64 string.
+ *
+ * @param b byte array.
+ * @return base64 string.
+ */
+ public static String bytes2base64(byte[] b, int offset, int length)
+ {
+ return bytes2base64(b, offset, length, BASE64);
+ }
+
+ /**
+ * to base64 string.
+ *
+ * @param b byte array.
+ * @param code base64 code string(0-63 is base64 char,64 is pad char).
+ * @return base64 string.
+ */
+ public static String bytes2base64(byte[] b, String code)
+ {
+ return bytes2base64(b, 0, b.length, code);
+ }
+
+ /**
+ * to base64 string.
+ *
+ * @param b byte array.
+ * @param code base64 code string(0-63 is base64 char,64 is pad char).
+ * @return base64 string.
+ */
+ public static String bytes2base64(byte[] b, int offset, int length, String code)
+ {
+ if( code.length() < 64 )
+ throw new IllegalArgumentException("Base64 code length < 64.");
+
+ return bytes2base64(b, offset, length, code.toCharArray());
+ }
+
+ /**
+ * to base64 string.
+ *
+ * @param b byte array.
+ * @param code base64 code(0-63 is base64 char,64 is pad char).
+ * @return base64 string.
+ */
+ public static String bytes2base64(byte[] b, char[] code)
+ {
+ return bytes2base64(b, 0, b.length, code);
+ }
+
+ /**
+ * to base64 string.
+ *
+ * @param bs byte array.
+ * @param off offset.
+ * @param len length.
+ * @param code base64 code(0-63 is base64 char,64 is pad char).
+ * @return base64 string.
+ */
+ public static String bytes2base64(final byte[] bs, final int off, final int len, final char[] code)
+ {
+ if( off < 0 )
+ throw new IndexOutOfBoundsException("bytes2base64: offset < 0, offset is " + off );
+ if( len < 0 )
+ throw new IndexOutOfBoundsException("bytes2base64: length < 0, length is " + len );
+ if( off + len > bs.length )
+ throw new IndexOutOfBoundsException("bytes2base64: offset + length > array length.");
+
+ if( code.length < 64 )
+ throw new IllegalArgumentException("Base64 code length < 64.");
+
+ boolean pad = code.length > 64; // has pad char.
+ int num = len / 3, rem = len % 3, r = off, w = 0;
+ char[] cs = new char[ num * 4 + ( rem == 0 ? 0 : pad ? 4 : rem + 1 ) ];
+
+ for(int i=0;i<num;i++)
+ {
+ int b1 = bs[r++] & MASK8, b2 = bs[r++] & MASK8, b3 = bs[r++] & MASK8;
+
+ cs[w++] = code[ b1 >> 2 ];
+ cs[w++] = code[ ( b1 << 4 ) & MASK6 | ( b2 >> 4 ) ];
+ cs[w++] = code[ ( b2 << 2 ) & MASK6 | ( b3 >> 6 ) ];
+ cs[w++] = code[ b3 & MASK6 ];
+ }
+
+ if( rem == 1 )
+ {
+ int b1 = bs[r++] & MASK8;
+ cs[w++] = code[ b1 >> 2 ];
+ cs[w++] = code[ ( b1 << 4 ) & MASK6 ];
+ if( pad )
+ {
+ cs[w++] = code[64];
+ cs[w++] = code[64];
+ }
+ }
+ else if( rem == 2 )
+ {
+ int b1 = bs[r++] & MASK8, b2 = bs[r++] & MASK8;
+ cs[w++] = code[ b1 >> 2 ];
+ cs[w++] = code[ ( b1 << 4 ) & MASK6 | ( b2 >> 4 ) ];
+ cs[w++] = code[ ( b2 << 2 ) & MASK6 ];
+ if( pad )
+ cs[w++] = code[64];
+ }
+ return new String(cs);
+ }
+
+ /**
+ * from base64 string.
+ *
+ * @param str base64 string.
+ * @return byte array.
+ */
+ public static byte[] base642bytes(String str)
+ {
+ return base642bytes(str, 0, str.length());
+ }
+
+ /**
+ * from base64 string.
+ *
+ * @param str base64 string.
+ * @param offset offset.
+ * @param length length.
+ * @return byte array.
+ */
+ public static byte[] base642bytes(String str, int offset, int length)
+ {
+ return base642bytes(str, offset, length, C64);
+ }
+
+ /**
+ * from base64 string.
+ *
+ * @param str base64 string.
+ * @param code base64 code(0-63 is base64 char,64 is pad char).
+ * @return byte array.
+ */
+ public static byte[] base642bytes(String str, String code)
+ {
+ return base642bytes(str, 0, str.length(), code);
+ }
+
+ /**
+ * from base64 string.
+ *
+ * @param str base64 string.
+ * @param off offset.
+ * @param len length.
+ * @param code base64 code(0-63 is base64 char,64 is pad char).
+ * @return byte array.
+ */
+ public static byte[] base642bytes(final String str, final int off, final int len, final String code)
+ {
+ if( off < 0 )
+ throw new IndexOutOfBoundsException("base642bytes: offset < 0, offset is " + off );
+ if( len < 0 )
+ throw new IndexOutOfBoundsException("base642bytes: length < 0, length is " + len );
+ if( off + len > str.length() )
+ throw new IndexOutOfBoundsException("base642bytes: offset + length > string length.");
+
+ if( code.length() < 64 )
+ throw new IllegalArgumentException("Base64 code length < 64.");
+
+ int rem = len % 4;
+ if( rem == 1 )
+ throw new IllegalArgumentException("base642bytes: base64 string length % 4 == 1.");
+
+ int num = len / 4, size = num * 3;
+ if( code.length() > 64 )
+ {
+ if( rem != 0 )
+ throw new IllegalArgumentException("base642bytes: base64 string length error.");
+
+ char pc = code.charAt(64);
+ if( str.charAt(off+len-2) == pc )
+ {
+ size -= 2;
+ --num;
+ rem = 2;
+ }
+ else if( str.charAt(off+len-1) == pc )
+ {
+ size--;
+ --num;
+ rem = 3;
+ }
+ }
+ else
+ {
+ if( rem == 2 )
+ size++;
+ else if( rem == 3 )
+ size += 2;
+ }
+
+ int r = off, w = 0;
+ byte[] b = new byte[size], t = decodeTable(code);
+ for(int i=0;i<num;i++)
+ {
+ int c1 = t[str.charAt(r++)], c2 = t[str.charAt(r++)];
+ int c3 = t[str.charAt(r++)], c4 = t[str.charAt(r++)];
+
+ b[w++] = (byte)( ( c1 << 2 ) | ( c2 >> 4 ) );
+ b[w++] = (byte)( ( c2 << 4 ) | ( c3 >> 2 ) );
+ b[w++] = (byte)( ( c3 << 6 ) | c4 );
+ }
+
+ if( rem == 2 )
+ {
+ int c1 = t[str.charAt(r++)], c2 = t[str.charAt(r++)];
+
+ b[w++] = (byte)( ( c1 << 2 ) | ( c2 >> 4 ) );
+ }
+ else if( rem == 3 )
+ {
+ int c1 = t[str.charAt(r++)], c2 = t[str.charAt(r++)], c3 = t[str.charAt(r++)];
+
+ b[w++] = (byte)( ( c1 << 2 ) | ( c2 >> 4 ) );
+ b[w++] = (byte)( ( c2 << 4 ) | ( c3 >> 2 ) );
+ }
+ return b;
+ }
+
+ /**
+ * from base64 string.
+ *
+ * @param str base64 string.
+ * @param code base64 code(0-63 is base64 char,64 is pad char).
+ * @return byte array.
+ */
+ public static byte[] base642bytes(String str, char[] code)
+ {
+ return base642bytes(str, 0, str.length(), code);
+ }
+
+ /**
+ * from base64 string.
+ *
+ * @param str base64 string.
+ * @param off offset.
+ * @param len length.
+ * @param code base64 code(0-63 is base64 char,64 is pad char).
+ * @return byte array.
+ */
+ public static byte[] base642bytes(final String str, final int off, final int len, final char[] code)
+ {
+ if( off < 0 )
+ throw new IndexOutOfBoundsException("base642bytes: offset < 0, offset is " + off );
+ if( len < 0 )
+ throw new IndexOutOfBoundsException("base642bytes: length < 0, length is " + len );
+ if( off + len > str.length() )
+ throw new IndexOutOfBoundsException("base642bytes: offset + length > string length.");
+
+ if( code.length < 64 )
+ throw new IllegalArgumentException("Base64 code length < 64.");
+
+ int rem = len % 4;
+ if( rem == 1 )
+ throw new IllegalArgumentException("base642bytes: base64 string length % 4 == 1.");
+
+ int num = len / 4, size = num * 3;
+ if( code.length > 64 )
+ {
+ if( rem != 0 )
+ throw new IllegalArgumentException("base642bytes: base64 string length error.");
+
+ char pc = code[64];
+ if( str.charAt(off+len-2) == pc )
+ size -= 2;
+ else if( str.charAt(off+len-1) == pc )
+ size--;
+ }
+ else
+ {
+ if( rem == 2 )
+ size++;
+ else if( rem == 3 )
+ size += 2;
+ }
+
+ int r = off, w = 0;
+ byte[] b = new byte[size];
+ for(int i=0;i<num;i++)
+ {
+ int c1 = indexOf(code, str.charAt(r++)), c2 = indexOf(code, str.charAt(r++));
+ int c3 = indexOf(code, str.charAt(r++)), c4 = indexOf(code, str.charAt(r++));
+
+ b[w++] = (byte)( ( c1 << 2 ) | ( c2 >> 4 ) );
+ b[w++] = (byte)( ( c2 << 4 ) | ( c3 >> 2 ) );
+ b[w++] = (byte)( ( c3 << 6 ) | c4 );
+ }
+
+ if( rem == 2 )
+ {
+ int c1 = indexOf(code, str.charAt(r++)), c2 = indexOf(code, str.charAt(r++));
+
+ b[w++] = (byte)( ( c1 << 2 ) | ( c2 >> 4 ) );
+ }
+ else if( rem == 3 )
+ {
+ int c1 = indexOf(code, str.charAt(r++)), c2 = indexOf(code, str.charAt(r++)), c3 = indexOf(code, str.charAt(r++));
+
+ b[w++] = (byte)( ( c1 << 2 ) | ( c2 >> 4 ) );
+ b[w++] = (byte)( ( c2 << 4 ) | ( c3 >> 2 ) );
+ }
+ return b;
+ }
+
+ /**
+ * zip.
+ *
+ * @param bytes source.
+ * @return compressed byte array.
+ * @throws IOException.
+ */
+ public static byte[] zip(byte[] bytes) throws IOException
+ {
+ UnsafeByteArrayOutputStream bos = new UnsafeByteArrayOutputStream();
+ OutputStream os = new DeflaterOutputStream(bos);
+ try
+ {
+ os.write(bytes);
+ }
+ finally
+ {
+ os.close();
+ bos.close();
+ }
+ return bos.toByteArray();
+ }
+
+ /**
+ * unzip.
+ *
+ * @param bytes compressed byte array.
+ * @return byte uncompressed array.
+ * @throws IOException
+ */
+ public static byte[] unzip(byte[] bytes) throws IOException
+ {
+ UnsafeByteArrayInputStream bis = new UnsafeByteArrayInputStream(bytes);
+ UnsafeByteArrayOutputStream bos = new UnsafeByteArrayOutputStream();
+ InputStream is = new InflaterInputStream(bis);
+ try
+ {
+ IOUtils.write(is, bos);
+ return bos.toByteArray();
+ }
+ finally
+ {
+ is.close();
+ bis.close();
+ bos.close();
+ }
+ }
+
+ /**
+ * get md5.
+ *
+ * @param str input string.
+ * @return MD5 byte array.
+ */
+ public static byte[] getMD5(String str)
+ {
+ return getMD5(str.getBytes());
+ }
+
+ /**
+ * get md5.
+ *
+ * @param source byte array source.
+ * @return MD5 byte array.
+ */
+ public static byte[] getMD5(byte[] source)
+ {
+ MessageDigest md = getMessageDigest();
+ return md.digest(source);
+ }
+
+ /**
+ * get md5.
+ *
+ * @param file file source.
+ * @return MD5 byte array.
+ */
+ public static byte[] getMD5(File file) throws IOException
+ {
+ InputStream is = new FileInputStream(file);
+ try{ return getMD5(is); }
+ finally{ is.close(); }
+ }
+
+ /**
+ * get md5.
+ *
+ * @param is input stream.
+ * @return MD5 byte array.
+ */
+ public static byte[] getMD5(InputStream is) throws IOException
+ {
+ return getMD5(is, 1024 * 8);
+ }
+
+ private static byte hex(char c)
+ {
+ if( c <= '9' ) return (byte)( c - '0' );
+ if( c >= 'a' && c <= 'f' ) return (byte)( c - 'a' + 10 );
+ if( c >= 'A' && c <= 'F' ) return (byte)( c - 'A' + 10 );
+ throw new IllegalArgumentException("hex string format error [" + c + "].");
+ }
+
+ private static int indexOf(char[] cs, char c)
+ {
+ for(int i=0,len=cs.length;i<len;i++)
+ if( cs[i] == c ) return i;
+ return -1;
+ }
+
+ private static byte[] decodeTable(String code)
+ {
+ int hash = code.hashCode();
+ byte[] ret = DECODE_TABLE_MAP.get(hash);
+ if( ret == null )
+ {
+ if( code.length() < 64 )
+ throw new IllegalArgumentException("Base64 code length < 64.");
+ // create new decode table.
+ ret = new byte[128];
+ for(int i=0;i<128;i++) // init table.
+ ret[i] = -1;
+ for(int i=0;i<64;i++)
+ ret[code.charAt(i)] = (byte)i;
+ DECODE_TABLE_MAP.put(hash, ret);
+ }
+ return ret;
+ }
+
+ private static byte[] getMD5(InputStream is, int bs) throws IOException
+ {
+ MessageDigest md = getMessageDigest();
+ byte[] buf = new byte[bs];
+ while( is.available() > 0 )
+ {
+ int read, total = 0;
+ do
+ {
+ if( ( read = is.read(buf, total, bs-total) ) <= 0 )
+ break;
+ total += read;
+ }
+ while( total < bs );
+ md.update(buf);
+ }
+ return md.digest();
+ }
+
+ private static MessageDigest getMessageDigest()
+ {
+ MessageDigest ret = MD.get();
+ if( ret == null )
+ {
+ try
+ {
+ ret = MessageDigest.getInstance("MD5");
+ MD.set(ret);
+ }
+ catch(NoSuchAlgorithmException e)
+ {
+ throw new RuntimeException(e);
+ }
+ }
+ return ret;
+ }
+
+ private Bytes(){}
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/io/ClassDescriptorMapper.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/io/ClassDescriptorMapper.java
new file mode 100644
index 0000000..96076bb
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/io/ClassDescriptorMapper.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.io;
+
+public interface ClassDescriptorMapper
+{
+ /**
+ * get Class-Descriptor by index.
+ *
+ * @param index index.
+ * @return string.
+ */
+ String getDescriptor(int index);
+
+ /**
+ * get Class-Descriptor index
+ *
+ * @param desc Class-Descriptor
+ * @return index.
+ */
+ int getDescriptorIndex(String desc);
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/io/StreamUtils.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/io/StreamUtils.java
new file mode 100644
index 0000000..a63330c
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/io/StreamUtils.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.common.io;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * Stream utils.
+ *
+ * @author qian.lei
+ */
+
+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
+ {}
+ };
+ }
+
+ /**
+ * @author ding.lid
+ */
+ 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;
+ }
+ };
+ }
+
+ /**
+ * @author ding.lid
+ */
+ 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..a0449e1
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/json/JSONArray.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.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.
+ * @param def default value.
+ * @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.
+ * @param def default value.
+ * @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.
+ * @param def default value.
+ * @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.
+ * @return json string.
+ */
+ 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..7147d26
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/json/JSONObject.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.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.
+ * @param def default value.
+ * @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.
+ * @param def default value.
+ * @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.
+ * @param def default value.
+ * @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.
+ * @return json string.
+ */
+ public void writeJSON(JSONConverter jc, JSONWriter jb, boolean writeClass) throws IOException
+ {
+ String key;
+ Object value;
+ jb.objectBegin();
+ for( Map.Entry<String,Object> entry : mMap.entrySet() )
+ {
+ key = entry.getKey();
+ jb.objectItem(key);
+ value = entry.getValue();
+ if( value == null )
+ jb.valueNull();
+ else
+ jc.writeValue(value, jb, writeClass);
+ }
+ jb.objectEnd();
+ }
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/json/JSONReader.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/json/JSONReader.java
new file mode 100644
index 0000000..f32a721
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/json/JSONReader.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.json;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.io.UnsupportedEncodingException;
+
+/**
+ * JSON reader.
+ *
+ * @author qian.lei
+ */
+
+public class JSONReader
+{
+ private static ThreadLocal<Yylex> LOCAL_LEXER = new ThreadLocal<Yylex>(){};
+
+ private Yylex mLex;
+
+ public JSONReader(InputStream is, String charset) throws UnsupportedEncodingException
+ {
+ this(new InputStreamReader(is, charset));
+ }
+
+ public JSONReader(Reader reader)
+ {
+ mLex = getLexer(reader);
+ }
+
+ public JSONToken nextToken() throws IOException, ParseException
+ {
+ return mLex.yylex();
+ }
+
+ public JSONToken nextToken(int expect) throws IOException, ParseException
+ {
+ JSONToken ret = mLex.yylex();
+ if( ret == null )
+ throw new ParseException("EOF error.");
+ if( expect != JSONToken.ANY && expect != ret.type )
+ throw new ParseException("Unexcepted token.");
+ return ret;
+ }
+
+ private static Yylex getLexer(Reader reader)
+ {
+ Yylex ret = LOCAL_LEXER.get();
+ if( ret == null )
+ {
+ ret = new Yylex(reader);
+ LOCAL_LEXER.set(ret);
+ }
+ else
+ {
+ ret.yyreset(reader);
+ }
+ return ret;
+ }
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/json/JSONToken.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/json/JSONToken.java
new file mode 100644
index 0000000..b3c925d
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/json/JSONToken.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.json;
+
+/**
+ * JSONToken.
+ *
+ * @author qian.lei
+ */
+
+public class JSONToken
+{
+ // token type
+ public static final int ANY = 0, IDENT = 0x01, LBRACE = 0x02, LSQUARE = 0x03, RBRACE = 0x04, RSQUARE = 0x05, COMMA = 0x06, COLON = 0x07;
+
+ public static final int NULL = 0x10, BOOL = 0x11, INT = 0x12, FLOAT = 0x13, STRING = 0x14, ARRAY = 0x15, OBJECT = 0x16;
+
+ public final int type;
+
+ public final Object value;
+
+ JSONToken(int t)
+ {
+ this(t, null);
+ }
+
+ JSONToken(int t, Object v)
+ {
+ type = t;
+ value = v;
+ }
+
+ static String token2string(int t)
+ {
+ switch( t )
+ {
+ case LBRACE: return "{";
+ case RBRACE: return "}";
+ case LSQUARE: return "[";
+ case RSQUARE: return "]";
+ case COMMA: return ",";
+ case COLON: return ":";
+ case IDENT: return "IDENT";
+ case NULL: return "NULL";
+ case BOOL: return "BOOL VALUE";
+ case INT: return "INT VALUE";
+ case FLOAT: return "FLOAT VALUE";
+ case STRING: return "STRING VALUE";
+ default: return "ANY";
+ }
+ }
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/json/JSONVisitor.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/json/JSONVisitor.java
new file mode 100644
index 0000000..d04f531
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/json/JSONVisitor.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.json;
+
+/**
+ * JSONVisitor.
+ *
+ * @author qian.lei
+ */
+
+public interface JSONVisitor
+{
+ public static final String CLASS_PROPERTY = "class";
+
+ /**
+ * parse begin .
+ */
+ void begin();
+
+ /**
+ * parse end.
+ *
+ * @param obj root obj.
+ * @param isValue is json value.
+ * @return parse result.
+ * @throws ParseException
+ */
+ Object end(Object obj, boolean isValue) throws ParseException;
+
+ /**
+ * object begin.
+ *
+ * @throws ParseException
+ */
+ void objectBegin() throws ParseException;
+
+ /**
+ * object end, return object value.
+ *
+ * @param count property count.
+ * @return object value.
+ * @throws ParseException
+ */
+ Object objectEnd(int count) throws ParseException;
+
+ /**
+ * object property name.
+ *
+ * @param name name.
+ * @throws ParseException
+ */
+ void objectItem(String name) throws ParseException;
+
+ /**
+ * object property value.
+ *
+ * @param obj obj.
+ * @param isValue is json value.
+ * @throws ParseException
+ */
+ void objectItemValue(Object obj, boolean isValue) throws ParseException;
+
+ /**
+ * array begin.
+ *
+ * @throws ParseException
+ */
+ void arrayBegin() throws ParseException;
+
+ /**
+ * array end, return array value.
+ *
+ * @param count count.
+ * @return array value.
+ * @throws ParseException
+ */
+ Object arrayEnd(int count) throws ParseException;
+
+ /**
+ * array item.
+ *
+ * @param index index.
+ * @throws ParseException
+ */
+ void arrayItem(int index) throws ParseException;
+
+ /**
+ * array item.
+ *
+ * @param index index.
+ * @param obj item.
+ * @param isValue is json value.
+ * @throws ParseException
+ */
+ void arrayItemValue(int index, Object obj, boolean isValue) throws ParseException;
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/json/JSONWriter.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/json/JSONWriter.java
new file mode 100644
index 0000000..9d90bf0
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/json/JSONWriter.java
@@ -0,0 +1,325 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.json;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.UnsupportedEncodingException;
+import java.io.Writer;
+
+import com.alibaba.dubbo.common.utils.Stack;
+
+/**
+ * JSON Writer.
+ *
+ * w.objectBegin().objectItem("name").valueString("qianlei").objectEnd() = {name:"qianlei"}.
+ *
+ * @author qian.lei
+ */
+
+public class JSONWriter
+{
+ private static final byte UNKNOWN = 0, ARRAY = 1, OBJECT = 2, OBJECT_VALUE = 3;
+
+ private static class State
+ {
+ private byte type;
+ private int itemCount = 0;
+
+ State(byte t){ type = t; }
+ }
+
+ private Writer mWriter;
+
+ private State mState = new State(UNKNOWN);
+
+ private Stack<State> mStack = new Stack<State>();
+
+ public JSONWriter(Writer writer)
+ {
+ mWriter = writer;
+ }
+
+ public JSONWriter(OutputStream is, String charset) throws UnsupportedEncodingException
+ {
+ mWriter = new OutputStreamWriter(is, charset);
+ }
+
+ /**
+ * object begin.
+ *
+ * @return this.
+ * @throws IOException.
+ */
+ public JSONWriter objectBegin() throws IOException
+ {
+ beforeValue();
+
+ mWriter.write(JSON.LBRACE);
+ mStack.push(mState);
+ mState = new State(OBJECT);
+ return this;
+ }
+
+ /**
+ * object end.
+ *
+ * @return this.
+ * @throws IOException.
+ */
+ public JSONWriter objectEnd() throws IOException
+ {
+ mWriter.write(JSON.RBRACE);
+ mState = mStack.pop();
+ return this;
+ }
+
+ /**
+ * object item.
+ *
+ * @param name name.
+ * @return this.
+ * @throws IOException.
+ */
+ public JSONWriter objectItem(String name) throws IOException
+ {
+ beforeObjectItem();
+
+ mWriter.write(JSON.QUOTE);
+ mWriter.write(escape(name));
+ mWriter.write(JSON.QUOTE);
+ mWriter.write(JSON.COLON);
+ return this;
+ }
+
+ /**
+ * array begin.
+ *
+ * @return this.
+ * @throws IOException.
+ */
+ public JSONWriter arrayBegin() throws IOException
+ {
+ beforeValue();
+
+ mWriter.write(JSON.LSQUARE);
+ mStack.push(mState);
+ mState = new State(ARRAY);
+ return this;
+ }
+
+ /**
+ * array end, return array value.
+ *
+ * @return this.
+ * @throws IOException.
+ */
+ public JSONWriter arrayEnd() throws IOException
+ {
+ mWriter.write(JSON.RSQUARE);
+ mState = mStack.pop();
+ return this;
+ }
+
+ /**
+ * value.
+ *
+ * @return this.
+ * @throws IOException.
+ */
+ public JSONWriter valueNull() throws IOException
+ {
+ beforeValue();
+
+ mWriter.write(JSON.NULL);
+ return this;
+ }
+
+ /**
+ * value.
+ *
+ * @param value value.
+ * @return this.
+ * @throws IOException
+ */
+ public JSONWriter valueString(String value) throws IOException
+ {
+ beforeValue();
+
+ mWriter.write(JSON.QUOTE);
+ mWriter.write(escape(value));
+ mWriter.write(JSON.QUOTE);
+ return this;
+ }
+
+ /**
+ * value.
+ *
+ * @param value value.
+ * @return this.
+ * @throws IOException
+ */
+ public JSONWriter valueBoolean(boolean value) throws IOException
+ {
+ beforeValue();
+
+ mWriter.write(value ? "true" : "false");
+ return this;
+ }
+
+ /**
+ * value.
+ *
+ * @param value value.
+ * @return this.
+ * @throws IOException
+ */
+ public JSONWriter valueInt(int value) throws IOException
+ {
+ beforeValue();
+
+ mWriter.write(String.valueOf(value));
+ return this;
+ }
+
+ /**
+ * value.
+ *
+ * @param value value.
+ * @return this.
+ * @throws IOException
+ */
+ public JSONWriter valueLong(long value) throws IOException
+ {
+ beforeValue();
+
+ mWriter.write(String.valueOf(value));
+ return this;
+ }
+
+ /**
+ * value.
+ *
+ * @param value value.
+ * @return this.
+ * @throws IOException
+ */
+ public JSONWriter valueFloat(float value) throws IOException
+ {
+ beforeValue();
+
+ mWriter.write(String.valueOf(value));
+ return this;
+ }
+
+ /**
+ * value.
+ *
+ * @param value value.
+ * @return this.
+ * @throws IOException
+ */
+ public JSONWriter valueDouble(double value) throws IOException
+ {
+ beforeValue();
+
+ mWriter.write(String.valueOf(value));
+ return this;
+ }
+
+ private void beforeValue() throws IOException
+ {
+ switch( mState.type )
+ {
+ case ARRAY:
+ if( mState.itemCount++ > 0 )
+ mWriter.write(JSON.COMMA);
+ return;
+ case OBJECT:
+ throw new IOException("Must call objectItem first.");
+ case OBJECT_VALUE:
+ mState.type = OBJECT;
+ return;
+ }
+ }
+
+ private void beforeObjectItem() throws IOException
+ {
+ switch( mState.type )
+ {
+ case OBJECT_VALUE:
+ mWriter.write(JSON.NULL);
+ case OBJECT:
+ mState.type = OBJECT_VALUE;
+ if( mState.itemCount++ > 0 )
+ mWriter.write(JSON.COMMA);
+ return;
+ default:
+ throw new IOException("Must call objectBegin first.");
+ }
+ }
+
+ private static final String[] CONTROL_CHAR_MAP = new String[]{
+ "\\u0000","\\u0001","\\u0002","\\u0003","\\u0004","\\u0005","\\u0006","\\u0007",
+ "\\b","\\t","\\n","\\u000b","\\f","\\r","\\u000e","\\u000f",
+ "\\u0010","\\u0011","\\u0012","\\u0013","\\u0014","\\u0015","\\u0016","\\u0017",
+ "\\u0018","\\u0019","\\u001a","\\u001b","\\u001c","\\u001d","\\u001e","\\u001f"
+ };
+
+ private static String escape(String str)
+ {
+ if( str == null )
+ return str;
+ int len = str.length();
+ if( len == 0 )
+ return str;
+
+ char c;
+ StringBuilder sb = null;
+ for(int i=0;i<len;i++)
+ {
+ c = str.charAt(i);
+ if( c < ' ' ) // control char.
+ {
+ if( sb == null )
+ {
+ sb = new StringBuilder(len<<1);
+ sb.append(str,0,i);
+ }
+ sb.append(CONTROL_CHAR_MAP[c]);
+ }
+ else
+ {
+ switch( c )
+ {
+ case '\\': case '/': case '"':
+ if( sb == null )
+ {
+ sb = new StringBuilder(len<<1);
+ sb.append(str,0,i);
+ }
+ sb.append('\\').append(c);
+ break;
+ default:
+ if( sb != null )
+ sb.append(c);
+ }
+ }
+ }
+ return sb == null ? str : sb.toString();
+ }
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/json/ParseException.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/json/ParseException.java
new file mode 100644
index 0000000..5bda123
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/json/ParseException.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.json;
+
+/**
+ * ParseException.
+ *
+ * @author qian.lei
+ */
+
+public class ParseException extends Exception
+{
+ private static final long serialVersionUID = 8611884051738966316L;
+
+ public ParseException()
+ {
+ super();
+ }
+
+ public ParseException(String message)
+ {
+ super(message);
+ }
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/json/Yylex.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/json/Yylex.java
new file mode 100644
index 0000000..0e3163b
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/json/Yylex.java
@@ -0,0 +1,800 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.json;
+
+/**
+ * This class is a scanner generated by
+ * <a href="http://www.jflex.de/">JFlex</a> 1.4.3
+ * on 7/3/10 3:12 AM from the specification file
+ * <tt>/Users/qianlei/dev/proj/dubbo-1.1/dubbo.common/src/main/java/com/alibaba/dubbo/common/json/json.flex</tt>
+ */
+public class Yylex {
+
+ /** This character denotes the end of file */
+ public static final int YYEOF = -1;
+
+ /** initial size of the lookahead buffer */
+ private static final int ZZ_BUFFERSIZE = 16384;
+
+ /** lexical states */
+ public static final int STR2 = 4;
+ public static final int STR1 = 2;
+ public static final int YYINITIAL = 0;
+
+ /**
+ * ZZ_LEXSTATE[l] is the state in the DFA for the lexical state l
+ * ZZ_LEXSTATE[l+1] is the state in the DFA for the lexical state l
+ * at the beginning of a line
+ * l is of the form l = 2*k, k a non negative integer
+ */
+ private static final int ZZ_LEXSTATE[] = {
+ 0, 0, 1, 1, 2, 2
+ };
+
+ /**
+ * Translates characters to character classes
+ */
+ private static final String ZZ_CMAP_PACKED =
+ "\11\0\1\13\1\13\2\0\1\13\22\0\1\13\1\0\1\10\1\0"+
+ "\1\2\2\0\1\11\3\0\1\7\1\43\1\4\1\5\1\14\12\1"+
+ "\1\44\6\0\1\33\3\3\1\6\1\32\5\2\1\34\1\2\1\36"+
+ "\3\2\1\25\1\35\1\24\1\26\5\2\1\41\1\12\1\42\1\0"+
+ "\1\2\1\0\1\27\1\15\2\3\1\23\1\16\5\2\1\30\1\2"+
+ "\1\17\3\2\1\20\1\31\1\21\1\22\5\2\1\37\1\0\1\40"+
+ "\uff82\0";
+
+ /**
+ * Translates characters to character classes
+ */
+ private static final char [] ZZ_CMAP = zzUnpackCMap(ZZ_CMAP_PACKED);
+
+ /**
+ * Translates DFA states to action switch labels.
+ */
+ private static final int [] ZZ_ACTION = zzUnpackAction();
+
+ private static final String ZZ_ACTION_PACKED_0 =
+ "\3\0\1\1\1\2\1\3\1\1\1\4\1\5\1\6"+
+ "\6\3\1\7\1\10\1\11\1\12\1\13\1\14\1\15"+
+ "\1\16\1\0\1\15\3\0\6\3\1\17\1\20\1\21"+
+ "\1\22\1\23\1\24\1\25\1\26\1\0\1\27\2\30"+
+ "\1\0\6\3\1\0\1\3\1\31\1\32\1\3\1\0"+
+ "\1\33\1\0\1\34";
+
+ private static int [] zzUnpackAction() {
+ int [] result = new int[63];
+ int offset = 0;
+ offset = zzUnpackAction(ZZ_ACTION_PACKED_0, offset, result);
+ return result;
+ }
+
+ private static int zzUnpackAction(String packed, int offset, int [] result) {
+ int i = 0; /* index in packed string */
+ int j = offset; /* index in unpacked array */
+ int l = packed.length();
+ while (i < l) {
+ int count = packed.charAt(i++);
+ int value = packed.charAt(i++);
+ do result[j++] = value; while (--count > 0);
+ }
+ return j;
+ }
+
+
+ /**
+ * Translates a state to a row index in the transition table
+ */
+ private static final int [] ZZ_ROWMAP = zzUnpackRowMap();
+
+ private static final String ZZ_ROWMAP_PACKED_0 =
+ "\0\0\0\45\0\112\0\157\0\224\0\271\0\336\0\157"+
+ "\0\157\0\u0103\0\u0128\0\u014d\0\u0172\0\u0197\0\u01bc\0\u01e1"+
+ "\0\157\0\157\0\157\0\157\0\157\0\157\0\u0206\0\157"+
+ "\0\u022b\0\u0250\0\u0275\0\u029a\0\u02bf\0\u02e4\0\u0309\0\u032e"+
+ "\0\u0353\0\u0378\0\u039d\0\157\0\157\0\157\0\157\0\157"+
+ "\0\157\0\157\0\157\0\u03c2\0\157\0\u03e7\0\u040c\0\u040c"+
+ "\0\u0431\0\u0456\0\u047b\0\u04a0\0\u04c5\0\u04ea\0\u050f\0\u0534"+
+ "\0\271\0\271\0\u0559\0\u057e\0\271\0\u05a3\0\157";
+
+ private static int [] zzUnpackRowMap() {
+ int [] result = new int[63];
+ int offset = 0;
+ offset = zzUnpackRowMap(ZZ_ROWMAP_PACKED_0, offset, result);
+ return result;
+ }
+
+ private static int zzUnpackRowMap(String packed, int offset, int [] result) {
+ int i = 0; /* index in packed string */
+ int j = offset; /* index in unpacked array */
+ int l = packed.length();
+ while (i < l) {
+ int high = packed.charAt(i++) << 16;
+ result[j++] = high | packed.charAt(i++);
+ }
+ return j;
+ }
+
+ /**
+ * The transition table of the DFA
+ */
+ private static final int ZZ_TRANS [] = {
+ 3, 4, 5, 5, 6, 3, 5, 3, 7, 8,
+ 3, 9, 3, 5, 10, 11, 5, 12, 5, 5,
+ 13, 5, 5, 5, 5, 5, 14, 5, 5, 5,
+ 15, 16, 17, 18, 19, 20, 21, 22, 22, 22,
+ 22, 22, 22, 22, 22, 23, 22, 24, 22, 22,
+ 22, 22, 22, 22, 22, 22, 22, 22, 22, 22,
+ 22, 22, 22, 22, 22, 22, 22, 22, 22, 22,
+ 22, 22, 22, 22, 25, 25, 25, 25, 25, 25,
+ 25, 25, 25, 23, 26, 25, 25, 25, 25, 25,
+ 25, 25, 25, 25, 25, 25, 25, 25, 25, 25,
+ 25, 25, 25, 25, 25, 25, 25, 25, 25, 25,
+ 25, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 4,
+ -1, -1, -1, 27, 28, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 28, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 5, 5, 5, -1,
+ -1, 5, -1, -1, -1, -1, -1, -1, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, -1, -1, -1, -1,
+ -1, -1, -1, 4, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 9, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 5, 5, 5,
+ -1, -1, 5, -1, -1, -1, -1, -1, -1, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 29,
+ 5, 5, 5, 5, 5, 5, 5, -1, -1, -1,
+ -1, -1, -1, -1, 5, 5, 5, -1, -1, 5,
+ -1, -1, -1, -1, -1, -1, 5, 5, 5, 5,
+ 5, 30, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, -1, -1, -1, -1, -1, -1,
+ -1, 5, 5, 5, -1, -1, 5, -1, -1, -1,
+ -1, -1, -1, 5, 5, 5, 31, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, -1, -1, -1, -1, -1, -1, -1, 5, 5,
+ 5, -1, -1, 5, -1, -1, -1, -1, -1, -1,
+ 5, 5, 5, 5, 5, 5, 5, 5, 32, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, -1, -1,
+ -1, -1, -1, -1, -1, 5, 5, 5, -1, -1,
+ 5, -1, -1, -1, -1, -1, -1, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 33, 5, 5, 5, -1, -1, -1, -1, -1,
+ -1, -1, 5, 5, 5, -1, -1, 5, -1, -1,
+ -1, -1, -1, -1, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 34, 5, 5, 5, 5, 5, 5,
+ 5, 5, -1, -1, -1, -1, -1, -1, 22, 22,
+ 22, 22, 22, 22, 22, 22, -1, 22, -1, 22,
+ 22, 22, 22, 22, 22, 22, 22, 22, 22, 22,
+ 22, 22, 22, 22, 22, 22, 22, 22, 22, 22,
+ 22, 22, 22, 22, 22, -1, -1, -1, -1, -1,
+ -1, -1, -1, 35, -1, 36, -1, 37, 38, 39,
+ 40, 41, 42, 43, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 25, 25, 25, 25, 25, 25, 25, 25,
+ 25, -1, -1, 25, 25, 25, 25, 25, 25, 25,
+ 25, 25, 25, 25, 25, 25, 25, 25, 25, 25,
+ 25, 25, 25, 25, 25, 25, 25, 25, 25, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, 44, 36,
+ -1, 37, 38, 39, 40, 41, 42, 43, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 45, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 46, -1, -1, 47, -1, -1,
+ 47, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 5, 5, 5, -1, -1, 5, -1, -1, -1,
+ -1, -1, -1, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 48, 5, 5, 5, 5, 5,
+ 5, -1, -1, -1, -1, -1, -1, -1, 5, 5,
+ 5, -1, -1, 5, -1, -1, -1, -1, -1, -1,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 49, 5, 5, 5, 5, 5, 5, -1, -1,
+ -1, -1, -1, -1, -1, 5, 5, 5, -1, -1,
+ 5, -1, -1, -1, -1, -1, -1, 5, 5, 5,
+ 5, 5, 50, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, -1, -1, -1, -1, -1,
+ -1, -1, 5, 5, 5, -1, -1, 5, -1, -1,
+ -1, -1, -1, -1, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 51, 5, 5, 5, 5, 5, 5,
+ 5, 5, -1, -1, -1, -1, -1, -1, -1, 5,
+ 5, 5, -1, -1, 5, -1, -1, -1, -1, -1,
+ -1, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 52, 5, 5, -1,
+ -1, -1, -1, -1, -1, -1, 5, 5, 5, -1,
+ -1, 5, -1, -1, -1, -1, -1, -1, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 53, 5, 5, -1, -1, -1, -1,
+ -1, -1, -1, 54, -1, 54, -1, -1, 54, -1,
+ -1, -1, -1, -1, -1, 54, 54, -1, -1, -1,
+ -1, 54, -1, -1, -1, 54, -1, -1, 54, 54,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 45, -1, -1, -1, -1, 28, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, 28, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 46, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 5, 5, 5, -1, -1, 5,
+ -1, -1, -1, -1, -1, -1, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 55, 5,
+ 5, 5, 5, 5, -1, -1, -1, -1, -1, -1,
+ -1, 5, 5, 5, -1, -1, 5, -1, -1, -1,
+ -1, -1, -1, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 56, 5, 5, 5, 5, 5,
+ 5, -1, -1, -1, -1, -1, -1, -1, 5, 5,
+ 5, -1, -1, 5, -1, -1, -1, -1, -1, -1,
+ 5, 5, 5, 5, 5, 5, 57, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, -1, -1,
+ -1, -1, -1, -1, -1, 5, 5, 5, -1, -1,
+ 57, -1, -1, -1, -1, -1, -1, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, -1, -1, -1, -1, -1,
+ -1, -1, 5, 5, 5, -1, -1, 5, -1, -1,
+ -1, -1, -1, -1, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 58, 5, -1, -1, -1, -1, -1, -1, -1, 5,
+ 5, 5, -1, -1, 5, -1, -1, -1, -1, -1,
+ -1, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 56, 5, 5, -1,
+ -1, -1, -1, -1, -1, -1, 59, -1, 59, -1,
+ -1, 59, -1, -1, -1, -1, -1, -1, 59, 59,
+ -1, -1, -1, -1, 59, -1, -1, -1, 59, -1,
+ -1, 59, 59, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 5, 5, 5, -1, -1, 5, -1,
+ -1, -1, -1, -1, -1, 5, 5, 5, 5, 5,
+ 5, 60, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, -1, -1, -1, -1, -1, -1, -1,
+ 5, 5, 5, -1, -1, 60, -1, -1, -1, -1,
+ -1, -1, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ -1, -1, -1, -1, -1, -1, -1, 61, -1, 61,
+ -1, -1, 61, -1, -1, -1, -1, -1, -1, 61,
+ 61, -1, -1, -1, -1, 61, -1, -1, -1, 61,
+ -1, -1, 61, 61, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 62, -1, 62, -1, -1, 62,
+ -1, -1, -1, -1, -1, -1, 62, 62, -1, -1,
+ -1, -1, 62, -1, -1, -1, 62, -1, -1, 62,
+ 62, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ };
+
+ /* error codes */
+ private static final int ZZ_UNKNOWN_ERROR = 0;
+ private static final int ZZ_NO_MATCH = 1;
+ private static final int ZZ_PUSHBACK_2BIG = 2;
+
+ /* error messages for the codes above */
+ private static final String ZZ_ERROR_MSG[] = {
+ "Unkown internal scanner error",
+ "Error: could not match input",
+ "Error: pushback value was too large"
+ };
+
+ /**
+ * ZZ_ATTRIBUTE[aState] contains the attributes of state <code>aState</code>
+ */
+ private static final int [] ZZ_ATTRIBUTE = zzUnpackAttribute();
+
+ private static final String ZZ_ATTRIBUTE_PACKED_0 =
+ "\3\0\1\11\3\1\2\11\7\1\6\11\1\1\1\11"+
+ "\1\0\1\1\3\0\6\1\10\11\1\0\1\11\2\1"+
+ "\1\0\6\1\1\0\4\1\1\0\1\1\1\0\1\11";
+
+ private static int [] zzUnpackAttribute() {
+ int [] result = new int[63];
+ int offset = 0;
+ offset = zzUnpackAttribute(ZZ_ATTRIBUTE_PACKED_0, offset, result);
+ return result;
+ }
+
+ private static int zzUnpackAttribute(String packed, int offset, int [] result) {
+ int i = 0; /* index in packed string */
+ int j = offset; /* index in unpacked array */
+ int l = packed.length();
+ while (i < l) {
+ int count = packed.charAt(i++);
+ int value = packed.charAt(i++);
+ do result[j++] = value; while (--count > 0);
+ }
+ return j;
+ }
+
+ /** the input device */
+ private java.io.Reader zzReader;
+
+ /** the current state of the DFA */
+ private int zzState;
+
+ /** the current lexical state */
+ private int zzLexicalState = YYINITIAL;
+
+ /** this buffer contains the current text to be matched and is
+ the source of the yytext() string */
+ private char zzBuffer[] = new char[ZZ_BUFFERSIZE];
+
+ /** the textposition at the last accepting state */
+ private int zzMarkedPos;
+
+ /** the current text position in the buffer */
+ private int zzCurrentPos;
+
+ /** startRead marks the beginning of the yytext() string in the buffer */
+ private int zzStartRead;
+
+ /** endRead marks the last character in the buffer, that has been read
+ from input */
+ private int zzEndRead;
+
+ /** number of newlines encountered up to the start of the matched text */
+ //private int yyline;
+
+ /** the number of characters up to the start of the matched text */
+ //private int yychar;
+
+ /**
+ * the number of characters from the last newline up to the start of the
+ * matched text
+ */
+ //private int yycolumn;
+
+ /**
+ * zzAtBOL == true <=> the scanner is currently at the beginning of a line
+ */
+ //private boolean zzAtBOL = true;
+
+ /** zzAtEOF == true <=> the scanner is at the EOF */
+ private boolean zzAtEOF;
+
+ /** denotes if the user-EOF-code has already been executed */
+ //private boolean zzEOFDone;
+
+ /* user code: */
+private StringBuffer sb;
+
+
+ /**
+ * Creates a new scanner
+ * There is also a java.io.InputStream version of this constructor.
+ *
+ * @param in the java.io.Reader to read input from.
+ */
+ Yylex(java.io.Reader in) {
+ this.zzReader = in;
+ }
+
+ /**
+ * Creates a new scanner.
+ * There is also java.io.Reader version of this constructor.
+ *
+ * @param in the java.io.Inputstream to read input from.
+ */
+ Yylex(java.io.InputStream in) {
+ this(new java.io.InputStreamReader(in));
+ }
+
+ /**
+ * Unpacks the compressed character translation table.
+ *
+ * @param packed the packed character translation table
+ * @return the unpacked character translation table
+ */
+ private static char [] zzUnpackCMap(String packed) {
+ char [] map = new char[0x10000];
+ int i = 0; /* index in packed string */
+ int j = 0; /* index in unpacked array */
+ while (i < 122) {
+ int count = packed.charAt(i++);
+ char value = packed.charAt(i++);
+ do map[j++] = value; while (--count > 0);
+ }
+ return map;
+ }
+
+
+ /**
+ * Refills the input buffer.
+ *
+ * @return <code>false</code>, iff there was new input.
+ *
+ * @exception java.io.IOException if any I/O-Error occurs
+ */
+ private boolean zzRefill() throws java.io.IOException {
+
+ /* first: make room (if you can) */
+ if (zzStartRead > 0) {
+ System.arraycopy(zzBuffer, zzStartRead,
+ zzBuffer, 0,
+ zzEndRead-zzStartRead);
+
+ /* translate stored positions */
+ zzEndRead-= zzStartRead;
+ zzCurrentPos-= zzStartRead;
+ zzMarkedPos-= zzStartRead;
+ zzStartRead = 0;
+ }
+
+ /* is the buffer big enough? */
+ if (zzCurrentPos >= zzBuffer.length) {
+ /* if not: blow it up */
+ char newBuffer[] = new char[zzCurrentPos*2];
+ System.arraycopy(zzBuffer, 0, newBuffer, 0, zzBuffer.length);
+ zzBuffer = newBuffer;
+ }
+
+ /* finally: fill the buffer with new input */
+ int numRead = zzReader.read(zzBuffer, zzEndRead,
+ zzBuffer.length-zzEndRead);
+
+ if (numRead > 0) {
+ zzEndRead+= numRead;
+ return false;
+ }
+ // unlikely but not impossible: read 0 characters, but not at end of stream
+ if (numRead == 0) {
+ int c = zzReader.read();
+ if (c == -1) {
+ return true;
+ } else {
+ zzBuffer[zzEndRead++] = (char) c;
+ return false;
+ }
+ }
+
+ // numRead < 0
+ return true;
+ }
+
+
+ /**
+ * Closes the input stream.
+ */
+ public final void yyclose() throws java.io.IOException {
+ zzAtEOF = true; /* indicate end of file */
+ zzEndRead = zzStartRead; /* invalidate buffer */
+
+ if (zzReader != null)
+ zzReader.close();
+ }
+
+
+ /**
+ * Resets the scanner to read from a new input stream.
+ * Does not close the old reader.
+ *
+ * All internal variables are reset, the old input stream
+ * <b>cannot</b> be reused (internal buffer is discarded and lost).
+ * Lexical state is set to <tt>ZZ_INITIAL</tt>.
+ *
+ * @param reader the new input stream
+ */
+ public final void yyreset(java.io.Reader reader) {
+ zzReader = reader;
+ //zzAtBOL = true;
+ zzAtEOF = false;
+ //zzEOFDone = false;
+ zzEndRead = zzStartRead = 0;
+ zzCurrentPos = zzMarkedPos = 0;
+ //yyline = yychar = yycolumn = 0;
+ zzLexicalState = YYINITIAL;
+ }
+
+
+ /**
+ * Returns the current lexical state.
+ */
+ public final int yystate() {
+ return zzLexicalState;
+ }
+
+
+ /**
+ * Enters a new lexical state
+ *
+ * @param newState the new lexical state
+ */
+ public final void yybegin(int newState) {
+ zzLexicalState = newState;
+ }
+
+
+ /**
+ * Returns the text matched by the current regular expression.
+ */
+ public final String yytext() {
+ return new String( zzBuffer, zzStartRead, zzMarkedPos-zzStartRead );
+ }
+
+
+ /**
+ * Returns the character at position <tt>pos</tt> from the
+ * matched text.
+ *
+ * It is equivalent to yytext().charAt(pos), but faster
+ *
+ * @param pos the position of the character to fetch.
+ * A value from 0 to yylength()-1.
+ *
+ * @return the character at position pos
+ */
+ public final char yycharat(int pos) {
+ return zzBuffer[zzStartRead+pos];
+ }
+
+
+ /**
+ * Returns the length of the matched text region.
+ */
+ public final int yylength() {
+ return zzMarkedPos-zzStartRead;
+ }
+
+
+ /**
+ * Reports an error that occured while scanning.
+ *
+ * In a wellformed scanner (no or only correct usage of
+ * yypushback(int) and a match-all fallback rule) this method
+ * will only be called with things that "Can't Possibly Happen".
+ * If this method is called, something is seriously wrong
+ * (e.g. a JFlex bug producing a faulty scanner etc.).
+ *
+ * Usual syntax/scanner level error handling should be done
+ * in error fallback rules.
+ *
+ * @param errorCode the code of the errormessage to display
+ */
+ private void zzScanError(int errorCode) {
+ String message;
+ try {
+ message = ZZ_ERROR_MSG[errorCode];
+ }
+ catch (ArrayIndexOutOfBoundsException e) {
+ message = ZZ_ERROR_MSG[ZZ_UNKNOWN_ERROR];
+ }
+
+ throw new Error(message);
+ }
+
+
+ /**
+ * Pushes the specified amount of characters back into the input stream.
+ *
+ * They will be read again by then next call of the scanning method
+ *
+ * @param number the number of characters to be read again.
+ * This number must not be greater than yylength()!
+ */
+ public void yypushback(int number) {
+ if ( number > yylength() )
+ zzScanError(ZZ_PUSHBACK_2BIG);
+
+ zzMarkedPos -= number;
+ }
+
+
+ /**
+ * Resumes scanning until the next regular expression is matched,
+ * the end of input is encountered or an I/O-Error occurs.
+ *
+ * @return the next token
+ * @exception java.io.IOException if any I/O-Error occurs
+ */
+ public JSONToken yylex() throws java.io.IOException, ParseException {
+ int zzInput;
+ int zzAction;
+
+ // cached fields:
+ int zzCurrentPosL;
+ int zzMarkedPosL;
+ int zzEndReadL = zzEndRead;
+ char [] zzBufferL = zzBuffer;
+ char [] zzCMapL = ZZ_CMAP;
+
+ int [] zzTransL = ZZ_TRANS;
+ int [] zzRowMapL = ZZ_ROWMAP;
+ int [] zzAttrL = ZZ_ATTRIBUTE;
+
+ while (true) {
+ zzMarkedPosL = zzMarkedPos;
+
+ zzAction = -1;
+
+ zzCurrentPosL = zzCurrentPos = zzStartRead = zzMarkedPosL;
+
+ zzState = ZZ_LEXSTATE[zzLexicalState];
+
+
+ zzForAction: {
+ while (true) {
+
+ if (zzCurrentPosL < zzEndReadL)
+ zzInput = zzBufferL[zzCurrentPosL++];
+ else if (zzAtEOF) {
+ zzInput = YYEOF;
+ break zzForAction;
+ }
+ else {
+ // store back cached positions
+ zzCurrentPos = zzCurrentPosL;
+ zzMarkedPos = zzMarkedPosL;
+ boolean eof = zzRefill();
+ // get translated positions and possibly new buffer
+ zzCurrentPosL = zzCurrentPos;
+ zzMarkedPosL = zzMarkedPos;
+ zzBufferL = zzBuffer;
+ zzEndReadL = zzEndRead;
+ if (eof) {
+ zzInput = YYEOF;
+ break zzForAction;
+ }
+ else {
+ zzInput = zzBufferL[zzCurrentPosL++];
+ }
+ }
+ int zzNext = zzTransL[ zzRowMapL[zzState] + zzCMapL[zzInput] ];
+ if (zzNext == -1) break zzForAction;
+ zzState = zzNext;
+
+ int zzAttributes = zzAttrL[zzState];
+ if ( (zzAttributes & 1) == 1 ) {
+ zzAction = zzState;
+ zzMarkedPosL = zzCurrentPosL;
+ if ( (zzAttributes & 8) == 8 ) break zzForAction;
+ }
+
+ }
+ }
+
+ // store back cached position
+ zzMarkedPos = zzMarkedPosL;
+
+ switch (zzAction < 0 ? zzAction : ZZ_ACTION[zzAction]) {
+ case 25:
+ { return new JSONToken(JSONToken.NULL, null);
+ }
+ case 29: break;
+ case 13:
+ { sb.append(yytext());
+ }
+ case 30: break;
+ case 18:
+ { sb.append('\b');
+ }
+ case 31: break;
+ case 9:
+ { return new JSONToken(JSONToken.LSQUARE);
+ }
+ case 32: break;
+ case 2:
+ { Long val = Long.valueOf(yytext()); return new JSONToken(JSONToken.INT, val);
+ }
+ case 33: break;
+ case 16:
+ { sb.append('\\');
+ }
+ case 34: break;
+ case 8:
+ { return new JSONToken(JSONToken.RBRACE);
+ }
+ case 35: break;
+ case 26:
+ { return new JSONToken(JSONToken.BOOL, Boolean.TRUE);
+ }
+ case 36: break;
+ case 23:
+ { sb.append('\'');
+ }
+ case 37: break;
+ case 5:
+ { sb = new StringBuffer(); yybegin(STR2);
+ }
+ case 38: break;
+ case 27:
+ { return new JSONToken(JSONToken.BOOL, Boolean.FALSE);
+ }
+ case 39: break;
+ case 12:
+ { return new JSONToken(JSONToken.COLON);
+ }
+ case 40: break;
+ case 21:
+ { sb.append('\r');
+ }
+ case 41: break;
+ case 3:
+ { return new JSONToken(JSONToken.IDENT, yytext());
+ }
+ case 42: break;
+ case 28:
+ { try{ sb.append((char)Integer.parseInt(yytext().substring(2),16)); }catch(Exception e){ throw new ParseException(e.getMessage()); }
+ }
+ case 43: break;
+ case 10:
+ { return new JSONToken(JSONToken.RSQUARE);
+ }
+ case 44: break;
+ case 17:
+ { sb.append('/');
+ }
+ case 45: break;
+ case 11:
+ { return new JSONToken(JSONToken.COMMA);
+ }
+ case 46: break;
+ case 15:
+ { sb.append('"');
+ }
+ case 47: break;
+ case 24:
+ { Double val = Double.valueOf(yytext()); return new JSONToken(JSONToken.FLOAT, val);
+ }
+ case 48: break;
+ case 1:
+ { throw new ParseException("Unexpected char [" + yytext() +"]");
+ }
+ case 49: break;
+ case 19:
+ { sb.append('\f');
+ }
+ case 50: break;
+ case 7:
+ { return new JSONToken(JSONToken.LBRACE);
+ }
+ case 51: break;
+ case 14:
+ { yybegin(YYINITIAL); return new JSONToken(JSONToken.STRING, sb.toString());
+ }
+ case 52: break;
+ case 22:
+ { sb.append('\t');
+ }
+ case 53: break;
+ case 4:
+ { sb = new StringBuffer(); yybegin(STR1);
+ }
+ case 54: break;
+ case 20:
+ { sb.append('\n');
+ }
+ case 55: break;
+ case 6:
+ {
+ }
+ case 56: break;
+ default:
+ if (zzInput == YYEOF && zzStartRead == zzCurrentPos) {
+ zzAtEOF = true;
+ return null;
+ }
+ else {
+ zzScanError(ZZ_NO_MATCH);
+ }
+ }
+ }
+ }
+
+
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/json/json.flex b/dubbo-common/src/main/java/com/alibaba/dubbo/common/json/json.flex
new file mode 100644
index 0000000..c6d4015
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/json/json.flex
@@ -0,0 +1,68 @@
+package com.alibaba.dubbo.common.json;
+%%
+
+%{
+private StringBuffer sb;
+%}
+
+%table
+%unicode
+%state STR1,STR2
+
+%yylexthrow ParseException
+
+HEX = [a-fA-F0-9]
+HEX4 = {HEX}{HEX}{HEX}{HEX}
+
+IDENT = [a-zA-Z_$] [a-zA-Z0-9_$]*
+INT_LITERAL = [-]? [0-9]+
+FLOAT_LITERAL = {INT_LITERAL} ( ( \.[0-9]+ ) ? ( [eE][-+]? [0-9]+ )? )
+
+ESC1 = [^\"\\]
+ESC2 = [^\'\\]
+
+SKIP = [ \t\r\n]
+OTHERS = .
+%%
+
+<STR1>{
+ \" { yybegin(YYINITIAL); return new JSONToken(JSONToken.STRING, sb.toString()); }
+ {ESC1}+ { sb.append(yytext()); }
+ \\\" { sb.append('"'); }
+}
+
+<STR2>{
+ \' { yybegin(YYINITIAL); return new JSONToken(JSONToken.STRING, sb.toString()); }
+ {ESC2}+ { sb.append(yytext()); }
+ \\\' { sb.append('\''); }
+}
+
+<STR1,STR2>{
+ \\\\ { sb.append('\\'); }
+ \\\/ { sb.append('/'); }
+ \\b { sb.append('\b'); }
+ \\f { sb.append('\f'); }
+ \\n { sb.append('\n'); }
+ \\r { sb.append('\r'); }
+ \\t { sb.append('\t'); }
+ \\u{HEX4} { try{ sb.append((char)Integer.parseInt(yytext().substring(2),16)); }catch(Exception e){ throw new ParseException(e.getMessage()); } }
+}
+
+<YYINITIAL>{
+ \" { sb = new StringBuffer(); yybegin(STR1); }
+ \' { sb = new StringBuffer(); yybegin(STR2); }
+ {INT_LITERAL} { Long val = Long.valueOf(yytext()); return new JSONToken(JSONToken.INT, val); }
+ {FLOAT_LITERAL} { Double val = Double.valueOf(yytext()); return new JSONToken(JSONToken.FLOAT, val); }
+ "true"|"TRUE" { return new JSONToken(JSONToken.BOOL, Boolean.TRUE); }
+ "false"|"FALSE" { return new JSONToken(JSONToken.BOOL, Boolean.FALSE); }
+ "null"|"NULL" { return new JSONToken(JSONToken.NULL, null); }
+ {IDENT} { return new JSONToken(JSONToken.IDENT, yytext()); }
+ "{" { return new JSONToken(JSONToken.LBRACE); }
+ "}" { return new JSONToken(JSONToken.RBRACE); }
+ "[" { return new JSONToken(JSONToken.LSQUARE); }
+ "]" { return new JSONToken(JSONToken.RSQUARE); }
+ "," { return new JSONToken(JSONToken.COMMA); }
+ ":" { return new JSONToken(JSONToken.COLON); }
+ {SKIP}+ {}
+ {OTHERS} { throw new ParseException("Unexpected char [" + yytext() +"]"); }
+}
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/logger/Level.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/logger/Level.java
new file mode 100644
index 0000000..f1a1834
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/logger/Level.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.logger;
+
+public enum Level {
+
+ ALL,
+
+ TRACE,
+
+ DEBUG,
+
+ INFO,
+
+ WARN,
+
+ ERROR,
+
+ 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..a8c2ea8
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/logger/Logger.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.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 msg 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();
+
+}
\ 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..647d0a3
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/logger/LoggerFactory.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.logger;
+
+import java.io.File;
+
+import com.alibaba.dubbo.common.logger.support.FailsafeLogger;
+import com.alibaba.dubbo.common.logger.support.JdkLoggerFactory;
+import com.alibaba.dubbo.common.logger.support.Log4jLoggerFactory;
+
+/**
+ * 日志输出器工厂
+ *
+ * @author liangfei0201@163.com
+ *
+ */
+public class LoggerFactory {
+
+ private LoggerFactory() {
+ }
+
+ private static volatile LoggerFactorySupport LOGGER_FACTORY;
+
+ // 查找常用的日志框架
+ static {
+ try {
+ setLoggerFactory(new Log4jLoggerFactory());
+ } catch (Throwable e1) {
+ setLoggerFactory(new JdkLoggerFactory());
+ }
+ }
+
+ /**
+ * 设置日志输出器供给器
+ *
+ * @param loggerFactory
+ * 日志输出器供给器
+ */
+ public static void setLoggerFactory(LoggerFactorySupport loggerFactory) {
+ if (loggerFactory != null) {
+ Logger logger = loggerFactory.getLogger(LoggerFactory.class.getName());
+ logger.info("using logger: " + loggerFactory.getClass().getName());
+ LoggerFactory.LOGGER_FACTORY = loggerFactory;
+ }
+ }
+
+ /**
+ * 获取日志输出器
+ *
+ * @param key
+ * 分类键
+ * @return 日志输出器, 后验条件: 不返回null.
+ */
+ public static Logger getLogger(Class<?> key) {
+ return new FailsafeLogger(LOGGER_FACTORY.getLogger(key));
+ }
+
+ /**
+ * 获取日志输出器
+ *
+ * @param key
+ * 分类键
+ * @return 日志输出器, 后验条件: 不返回null.
+ */
+ public static Logger getLogger(String key) {
+ return new FailsafeLogger(LOGGER_FACTORY.getLogger(key));
+ }
+
+ /**
+ * 动态设置输出日志级别
+ *
+ * @param level 日志级别
+ */
+ public static void setLevel(Level level) {
+ LOGGER_FACTORY.setLevel(level);
+ }
+
+ /**
+ * 获取日志级别
+ *
+ * @return 日志级别
+ */
+ public static Level getLevel() {
+ return LOGGER_FACTORY.getLevel();
+ }
+
+ /**
+ * 获取日志文件
+ *
+ * @return 日志文件
+ */
+ public static File getFile() {
+ return LOGGER_FACTORY.getFile();
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/logger/LoggerFactorySupport.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/logger/LoggerFactorySupport.java
new file mode 100644
index 0000000..b3fccee
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/logger/LoggerFactorySupport.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.logger;
+
+import java.io.File;
+
+
+/**
+ * 日志输出器供给器
+ *
+ * @author liangfei0201@163.com
+ *
+ */
+public interface LoggerFactorySupport {
+
+ /**
+ * 获取日志输出器
+ *
+ * @param key 分类键
+ * @return 日志输出器, 后验条件: 不返回null.
+ */
+ Logger getLogger(Class<?> key);
+
+ /**
+ * 获取日志输出器
+ *
+ * @param key 分类键
+ * @return 日志输出器, 后验条件: 不返回null.
+ */
+ Logger getLogger(String key);
+
+ /**
+ * 设置输出等级
+ *
+ * @param level
+ */
+ void setLevel(Level level);
+
+ /**
+ *
+ * @return
+ */
+ Level getLevel();
+
+ /**
+ *
+ * @return
+ */
+ File getFile();
+
+ /**
+ *
+ * @param file
+ */
+ void setFile(File file);
+
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/logger/support/FailsafeLogger.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/logger/support/FailsafeLogger.java
new file mode 100644
index 0000000..37f3cd6
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/logger/support/FailsafeLogger.java
@@ -0,0 +1,165 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.logger.support;
+
+import com.alibaba.dubbo.common.logger.Logger;
+
+public class FailsafeLogger implements Logger {
+
+ private final Logger logger;
+
+ public FailsafeLogger(Logger logger) {
+ this.logger = logger;
+ }
+
+ public void trace(String msg, Throwable e) {
+ try {
+ logger.trace(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(msg);
+ } catch (Throwable t) {
+ }
+ }
+
+ public void debug(String msg, Throwable e) {
+ try {
+ logger.debug(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(msg);
+ } catch (Throwable t) {
+ }
+ }
+
+ public void info(String msg, Throwable e) {
+ try {
+ logger.info(msg, e);
+ } catch (Throwable t) {
+ }
+ }
+
+ public void info(String msg) {
+ try {
+ logger.info(msg);
+ } catch (Throwable t) {
+ }
+ }
+
+ public void warn(String msg, Throwable e) {
+ try {
+ logger.warn(msg, e);
+ } catch (Throwable t) {
+ }
+ }
+
+ public void warn(String msg) {
+ try {
+ logger.warn(msg);
+ } catch (Throwable t) {
+ }
+ }
+
+ public void error(String msg, Throwable e) {
+ try {
+ logger.error(msg, e);
+ } catch (Throwable t) {
+ }
+ }
+
+ public void error(String msg) {
+ try {
+ logger.error(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;
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/logger/support/JdkLogger.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/logger/support/JdkLogger.java
new file mode 100644
index 0000000..ddcfebc
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/logger/support/JdkLogger.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.logger.support;
+
+import java.util.logging.Level;
+
+import com.alibaba.dubbo.common.logger.Logger;
+
+public class JdkLogger implements Logger {
+
+ private final java.util.logging.Logger logger;
+
+ public JdkLogger(java.util.logging.Logger logger) {
+ this.logger = logger;
+ }
+
+ public void trace(String msg) {
+ logger.log(Level.FINER, msg);
+ }
+
+ public void trace(Throwable e) {
+ logger.log(Level.FINER, e.getMessage(), e);
+ }
+
+ public void trace(String msg, Throwable e) {
+ logger.log(Level.FINER, msg, e);
+ }
+
+ public void debug(String msg) {
+ logger.log(Level.FINE, msg);
+ }
+
+ public void debug(Throwable e) {
+ logger.log(Level.FINE, e.getMessage(), e);
+ }
+
+ public void debug(String msg, Throwable e) {
+ logger.log(Level.FINE, msg, e);
+ }
+
+ public void info(String msg) {
+ logger.log(Level.INFO, msg);
+ }
+
+ public void info(String msg, Throwable e) {
+ logger.log(Level.INFO, msg, e);
+ }
+
+ public void warn(String msg) {
+ logger.log(Level.WARNING, msg);
+ }
+
+ public void warn(String msg, Throwable e) {
+ logger.log(Level.WARNING, msg, e);
+ }
+
+ public void error(String msg) {
+ logger.log(Level.SEVERE, msg);
+ }
+
+ public void error(String msg, Throwable e) {
+ logger.log(Level.SEVERE, msg, e);
+ }
+
+ public void error(Throwable e) {
+ logger.log(Level.SEVERE, e.getMessage(), e);
+ }
+
+ public void info(Throwable e) {
+ logger.log(Level.INFO, e.getMessage(), e);
+ }
+
+ public void warn(Throwable e) {
+ logger.log(Level.WARNING, e.getMessage(), e);
+ }
+
+ public boolean isTraceEnabled() {
+ return logger.isLoggable(Level.FINER);
+ }
+
+ public boolean isDebugEnabled() {
+ return logger.isLoggable(Level.FINE);
+ }
+
+ public boolean isInfoEnabled() {
+ return logger.isLoggable(Level.INFO);
+ }
+
+ public boolean isWarnEnabled() {
+ return logger.isLoggable(Level.WARNING);
+ }
+
+ public boolean isErrorEnabled() {
+ return logger.isLoggable(Level.SEVERE);
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/logger/support/JdkLoggerFactory.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/logger/support/JdkLoggerFactory.java
new file mode 100644
index 0000000..fb8cb6b
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/logger/support/JdkLoggerFactory.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.logger.support;
+
+import java.io.File;
+import java.io.InputStream;
+import java.lang.reflect.Field;
+import java.util.logging.FileHandler;
+import java.util.logging.Handler;
+import java.util.logging.LogManager;
+
+import com.alibaba.dubbo.common.logger.Level;
+import com.alibaba.dubbo.common.logger.Logger;
+import com.alibaba.dubbo.common.logger.LoggerFactorySupport;
+
+public class JdkLoggerFactory implements LoggerFactorySupport {
+
+ private static final String GLOBAL_LOGGER_NAME = "global";
+
+ private File file;
+
+ public JdkLoggerFactory() {
+ try {
+ InputStream in = Thread.currentThread().getContextClassLoader().getResourceAsStream("logging.properties");
+ if (in != null) {
+ LogManager.getLogManager().readConfiguration(in);
+ } else {
+ System.err.println("No such logging.properties in classpath for jdk logging config!");
+ }
+ } catch (Throwable t) {
+ System.err.println("Failed to load logging.properties in classpath for jdk logging config, cause: " + t.getMessage());
+ }
+ try {
+ Handler[] handlers = java.util.logging.Logger.getLogger(GLOBAL_LOGGER_NAME).getHandlers();
+ for (Handler handler : handlers) {
+ if (handler instanceof FileHandler) {
+ FileHandler fileHandler = (FileHandler)handler;
+ Field field = fileHandler.getClass().getField("files");
+ File[] files = (File[])field.get(fileHandler);
+ if (files != null && files.length > 0) {
+ file = files[0];
+ }
+ }
+ }
+ } catch (Throwable t) {
+ }
+ }
+
+ public Logger getLogger(Class<?> key) {
+ return new JdkLogger(java.util.logging.Logger.getLogger(key == null ? "" : key.getName()));
+ }
+
+ public Logger getLogger(String key) {
+ return new JdkLogger(java.util.logging.Logger.getLogger(key));
+ }
+
+ public void setLevel(Level level) {
+ java.util.logging.Logger.getLogger(GLOBAL_LOGGER_NAME).setLevel(toJdkLevel(level));
+ }
+
+ public Level getLevel() {
+ return fromJdkLevel(java.util.logging.Logger.getLogger(GLOBAL_LOGGER_NAME).getLevel());
+ }
+
+ public File getFile() {
+ return file;
+ }
+
+ private static java.util.logging.Level toJdkLevel(Level level) {
+ if (level == Level.ALL)
+ return java.util.logging.Level.ALL;
+ if (level == Level.TRACE)
+ return java.util.logging.Level.FINER;
+ if (level == Level.DEBUG)
+ return java.util.logging.Level.FINE;
+ if (level == Level.INFO)
+ return java.util.logging.Level.INFO;
+ if (level == Level.WARN)
+ return java.util.logging.Level.WARNING;
+ if (level == Level.ERROR)
+ return java.util.logging.Level.SEVERE;
+ // if (level == Level.OFF)
+ return java.util.logging.Level.OFF;
+ }
+
+ private static Level fromJdkLevel(java.util.logging.Level level) {
+ if (level == java.util.logging.Level.ALL)
+ return Level.ALL;
+ if (level == java.util.logging.Level.FINER)
+ return Level.TRACE;
+ if (level == java.util.logging.Level.FINE)
+ return Level.DEBUG;
+ if (level == java.util.logging.Level.INFO)
+ return Level.INFO;
+ if (level == java.util.logging.Level.WARNING)
+ return Level.WARN;
+ if (level == java.util.logging.Level.SEVERE)
+ return Level.ERROR;
+ // if (level == java.util.logging.Level.OFF)
+ return Level.OFF;
+ }
+
+ public void setFile(File file) {
+
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/logger/support/Log4jLogger.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/logger/support/Log4jLogger.java
new file mode 100644
index 0000000..7d09cf2
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/logger/support/Log4jLogger.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.logger.support;
+
+import org.apache.log4j.Level;
+
+import com.alibaba.dubbo.common.logger.Logger;
+
+public class Log4jLogger implements Logger {
+
+ private static final String FQCN = FailsafeLogger.class.getName();
+
+ private final org.apache.log4j.Logger logger;
+
+ public Log4jLogger(org.apache.log4j.Logger logger) {
+ this.logger = logger;
+ }
+
+ public void trace(String msg) {
+ logger.log(FQCN, Level.TRACE, msg, null);
+ }
+
+ public void trace(Throwable e) {
+ logger.log(FQCN, Level.TRACE, e == null ? null : e.getMessage(), e);
+ }
+
+ public void trace(String msg, Throwable e) {
+ logger.log(FQCN, Level.TRACE, msg, e);
+ }
+
+ public void debug(String msg) {
+ logger.log(FQCN, Level.DEBUG, msg, null);
+ }
+
+ public void debug(Throwable e) {
+ logger.log(FQCN, Level.DEBUG, e == null ? null : e.getMessage(), e);
+ }
+
+ public void debug(String msg, Throwable e) {
+ logger.log(FQCN, Level.DEBUG, msg, e);
+ }
+
+ public void info(String msg) {
+ logger.log(FQCN, Level.INFO, msg, null);
+ }
+
+ public void info(Throwable e) {
+ logger.log(FQCN, Level.INFO, e == null ? null : e.getMessage(), e);
+ }
+
+ public void info(String msg, Throwable e) {
+ logger.log(FQCN, Level.INFO, msg, e);
+ }
+
+ public void warn(String msg) {
+ logger.log(FQCN, Level.WARN, msg, null);
+ }
+
+ public void warn(Throwable e) {
+ logger.log(FQCN, Level.WARN, e == null ? null : e.getMessage(), e);
+ }
+
+ public void warn(String msg, Throwable e) {
+ logger.log(FQCN, Level.WARN, msg, e);
+ }
+
+ public void error(String msg) {
+ logger.log(FQCN, Level.ERROR, msg, null);
+ }
+
+ public void error(Throwable e) {
+ logger.log(FQCN, Level.ERROR, e == null ? null : e.getMessage(), e);
+ }
+
+ public void error(String msg, Throwable e) {
+ logger.log(FQCN, Level.ERROR, msg, e);
+ }
+
+ public boolean isTraceEnabled() {
+ return logger.isTraceEnabled();
+ }
+
+ public boolean isDebugEnabled() {
+ return logger.isDebugEnabled();
+ }
+
+ public boolean isInfoEnabled() {
+ return logger.isInfoEnabled();
+ }
+
+ public boolean isWarnEnabled() {
+ return logger.isEnabledFor(Level.WARN);
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/logger/support/Log4jLoggerFactory.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/logger/support/Log4jLoggerFactory.java
new file mode 100644
index 0000000..369560e
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/logger/support/Log4jLoggerFactory.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.logger.support;
+
+import java.io.File;
+import java.util.Enumeration;
+
+import org.apache.log4j.Appender;
+import org.apache.log4j.FileAppender;
+import org.apache.log4j.LogManager;
+
+import com.alibaba.dubbo.common.logger.Level;
+import com.alibaba.dubbo.common.logger.Logger;
+import com.alibaba.dubbo.common.logger.LoggerFactorySupport;
+
+public class Log4jLoggerFactory implements LoggerFactorySupport {
+
+ private File file;
+
+ @SuppressWarnings("unchecked")
+ public Log4jLoggerFactory() {
+ try {
+ org.apache.log4j.Logger logger = LogManager.getRootLogger();
+ if (logger != null) {
+ Enumeration<Appender> appenders = logger.getAllAppenders();
+ if (appenders != null) {
+ while (appenders.hasMoreElements()) {
+ Appender appender = appenders.nextElement();
+ if (appender instanceof FileAppender) {
+ FileAppender fileAppender = (FileAppender)appender;
+ String filename = fileAppender.getFile();
+ file = new File(filename);
+ break;
+ }
+ }
+ }
+ }
+ } catch (Throwable t) {
+ }
+ }
+
+ public Logger getLogger(Class<?> key) {
+ return new Log4jLogger(LogManager.getLogger(key));
+ }
+
+ public Logger getLogger(String key) {
+ return new Log4jLogger(LogManager.getLogger(key));
+ }
+
+ public void setLevel(Level level) {
+ LogManager.getRootLogger().setLevel(toLog4jLevel(level));
+ }
+
+ public Level getLevel() {
+ return fromLog4jLevel(LogManager.getRootLogger().getLevel());
+ }
+
+ public File getFile() {
+ return file;
+ }
+
+ private static org.apache.log4j.Level toLog4jLevel(Level level) {
+ if (level == Level.ALL)
+ return org.apache.log4j.Level.ALL;
+ if (level == Level.TRACE)
+ return org.apache.log4j.Level.TRACE;
+ if (level == Level.DEBUG)
+ return org.apache.log4j.Level.DEBUG;
+ if (level == Level.INFO)
+ return org.apache.log4j.Level.INFO;
+ if (level == Level.WARN)
+ return org.apache.log4j.Level.WARN;
+ if (level == Level.ERROR)
+ return org.apache.log4j.Level.ERROR;
+ // if (level == Level.OFF)
+ return org.apache.log4j.Level.OFF;
+ }
+
+ private static Level fromLog4jLevel(org.apache.log4j.Level level) {
+ if (level == org.apache.log4j.Level.ALL)
+ return Level.ALL;
+ if (level == org.apache.log4j.Level.TRACE)
+ return Level.TRACE;
+ if (level == org.apache.log4j.Level.DEBUG)
+ return Level.DEBUG;
+ if (level == org.apache.log4j.Level.INFO)
+ return Level.INFO;
+ if (level == org.apache.log4j.Level.WARN)
+ return Level.WARN;
+ if (level == org.apache.log4j.Level.ERROR)
+ return Level.ERROR;
+ // if (level == org.apache.log4j.Level.OFF)
+ return Level.OFF;
+ }
+
+ public void setFile(File file) {
+
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/DataInput.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/DataInput.java
new file mode 100644
index 0000000..68d6a26
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/DataInput.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.serialize;
+
+import java.io.IOException;
+
+/**
+ * Data input.
+ *
+ * @author qian.lei
+ */
+public interface DataInput {
+
+ /**
+ * Read boolean.
+ *
+ * @return boolean.
+ * @throws IOException.
+ */
+ boolean readBool() throws IOException;
+
+ /**
+ * Read byte.
+ *
+ * @return byte value.
+ * @throws IOException.
+ */
+ byte readByte() throws IOException;
+
+ /**
+ * Read short integer.
+ *
+ * @return short.
+ * @throws IOException.
+ */
+ short readShort() throws IOException;
+
+ /**
+ * Read integer.
+ *
+ * @return integer.
+ * @throws IOException.
+ */
+ int readInt() throws IOException;
+
+ /**
+ * Read long.
+ *
+ * @return long.
+ * @throws IOException.
+ */
+ long readLong() throws IOException;
+
+ /**
+ * Read float.
+ *
+ * @return float.
+ * @throws IOException.
+ */
+ float readFloat() throws IOException;
+
+ /**
+ * Read double.
+ *
+ * @return double.
+ * @throws IOException.
+ */
+ double readDouble() throws IOException;
+
+ /**
+ * Read UTF-8 string.
+ *
+ * @return string.
+ * @throws IOException.
+ */
+ String readUTF() throws IOException;
+
+ /**
+ * Read byte array.
+ *
+ * @return byte array.
+ * @throws IOException.
+ */
+ byte[] readBytes() throws IOException;
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/DataOutput.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/DataOutput.java
new file mode 100644
index 0000000..64a4241
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/DataOutput.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.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[] b) throws IOException;
+
+ /**
+ * Write byte array.
+ *
+ * @param v value.
+ * @param off offset.
+ * @param len length.
+ * @throws IOException.
+ */
+ void writeBytes(byte[] b, int off, int len) throws IOException;
+
+ /**
+ * Flush buffer.
+ *
+ * @param v value.
+ * @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..e8af0ca
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/ObjectInput.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.serialize;
+
+import java.io.IOException;
+
+/**
+ * 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;
+
+}
\ 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..03af2a4
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/Serialization.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.serialize;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import com.alibaba.dubbo.common.Adaptive;
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.common.URL;
+
+/**
+ * Serialization. (SPI, Singleton, ThreadSafe)
+ *
+ * @author ding.lid
+ * @author william.liangf
+ */
+@Extension("hessian2")
+public interface Serialization {
+
+ /**
+ * get content type id
+ *
+ * @return
+ */
+ 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..04e68ba
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/dubbo/Builder.java
@@ -0,0 +1,1564 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.math.BigInteger;
+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) || (!ignoreFinalModifier(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)
+ || (!ignoreFinalModifier(c) && Modifier.isFinal(mod))
+ || tf.getName().equals("this$0") ) // skip static or inner-class's 'this$0' 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 ignoreFinalModifier(Class cl)
+ {
+ if (cl.isAssignableFrom(BigInteger.class)) return true;
+ return false;
+ }
+ @SuppressWarnings("unused")
+ private static boolean isPrimitiveOrPrimitiveArray1(Class<?> cl)
+ {
+ if (cl.isPrimitive()){
+ return true;
+ } else {
+ Class clazz = cl.getClass().getComponentType();
+ if (clazz!=null && clazz.isPrimitive()){
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private static String defaultArg(Class<?> cl)
+ {
+ if( boolean.class == cl ) return "false";
+ if( int.class == cl ) return "0";
+ if( long.class == cl ) return "0l";
+ if( double.class == cl ) return "(double)0";
+ if( float.class == cl ) return "(float)0";
+ if( short.class == cl ) return "(short)0";
+ if( char.class == cl ) return "(char)0";
+ if( byte.class == cl ) return "(byte)0";
+ if( byte[].class == cl ) return "new byte[]{0}";
+ if( !cl.isPrimitive() ) return "null";
+ throw new UnsupportedOperationException();
+ }
+
+ private static int compareFieldName(String n1, String n2)
+ {
+ int l = Math.min(n1.length(), n2.length());
+ for(int i=0;i<l;i++)
+ {
+ int t = n1.charAt(i) - n2.charAt(i);
+ if( t != 0 )
+ return t;
+ }
+ return n1.length() - n2.length();
+ }
+
+ private static void addDesc(Class<?> c)
+ {
+ String desc = ReflectUtils.getDesc(c);
+ int index = mDescList.size();
+ mDescList.add(desc);
+ mDescMap.put(desc, index);
+ }
+
+ static class PropertyMetadata
+ {
+ Class<?> type;
+ String setter, getter;
+ }
+
+ public static abstract class AbstractObjectBuilder<T> extends Builder<T>
+ {
+ abstract public Class<T> getType();
+
+ public void writeTo(T obj, GenericObjectOutput out) throws IOException
+ {
+ if( obj == null )
+ {
+ out.write0(OBJECT_NULL);
+ }
+ else
+ {
+ int ref = out.getRef(obj);
+ if( ref < 0 )
+ {
+ out.addRef(obj);
+ out.write0(OBJECT);
+ writeObject(obj, out);
+ }
+ else
+ {
+ out.write0(OBJECT_REF);
+ out.writeUInt(ref);
+ }
+ }
+ }
+
+ public T parseFrom(GenericObjectInput in) throws IOException
+ {
+ byte b = in.read0();
+ switch( b )
+ {
+ case OBJECT:
+ {
+ T ret = newInstance(in);
+ in.addRef(ret);
+ readObject(ret, in);
+ return ret;
+ }
+ case OBJECT_REF:
+ return (T)in.getRef(in.readUInt());
+ case OBJECT_NULL:
+ return null;
+ default:
+ throw new IOException("Input format error, expect OBJECT|OBJECT_REF|OBJECT_NULL, get " + b);
+ }
+ }
+
+ abstract protected void writeObject(T obj, GenericObjectOutput out) throws IOException;
+
+ abstract protected T newInstance(GenericObjectInput in) throws IOException;
+
+ abstract protected void readObject(T ret, GenericObjectInput in) throws IOException;
+ }
+
+ static final Builder<Object> GenericBuilder = new Builder<Object>(){
+ @Override
+ public Class<Object> getType(){ return Object.class; }
+ @Override
+ public void writeTo(Object obj, GenericObjectOutput out) throws IOException{ out.writeObject(obj); }
+ @Override
+ public Object parseFrom(GenericObjectInput in) throws IOException{ return in.readObject(); }
+ };
+
+ static final Builder<Object[]> GenericArrayBuilder = new AbstractObjectBuilder<Object[]>(){
+ @Override
+ public Class<Object[]> getType(){ return Object[].class; }
+ @Override
+ protected Object[] newInstance(GenericObjectInput in) throws IOException
+ {
+ return new Object[in.readUInt()];
+ }
+ @Override
+ protected void readObject(Object[] ret, GenericObjectInput in) throws IOException
+ {
+ for(int i=0;i<ret.length;i++)
+ ret[i] = in.readObject();
+ }
+ @Override
+ protected void writeObject(Object[] obj, GenericObjectOutput out) throws IOException
+ {
+ out.writeUInt(obj.length);
+ for( Object item : obj )
+ out.writeObject(item);
+ }
+ };
+
+ static final Builder<Serializable> SerializableBuilder = new Builder<Serializable>(){
+ @Override
+ public Class<Serializable> getType()
+ {
+ return Serializable.class;
+ }
+ @Override
+ public void writeTo(Serializable obj, GenericObjectOutput out) throws IOException
+ {
+ if( obj == null )
+ {
+ out.write0(OBJECT_NULL);
+ }
+ else
+ {
+ out.write0(OBJECT_STREAM);
+ UnsafeByteArrayOutputStream bos = new UnsafeByteArrayOutputStream();
+ CompactedObjectOutputStream oos = new CompactedObjectOutputStream(bos);
+ oos.writeObject(obj);
+ oos.flush();
+ bos.close();
+ byte[] b = bos.toByteArray();
+ out.writeUInt(b.length);
+ out.write0(b, 0, b.length);
+ }
+ }
+ @Override
+ public Serializable parseFrom(GenericObjectInput in) throws IOException
+ {
+ byte b = in.read0();
+ if( b == OBJECT_NULL )
+ return null;
+ if( b != OBJECT_STREAM )
+ throw new IOException("Input format error, expect OBJECT_NULL|OBJECT_STREAM, get " + b + ".");
+
+ UnsafeByteArrayInputStream bis = new UnsafeByteArrayInputStream(in.read0(in.readUInt()));
+ CompactedObjectInputStream ois = new CompactedObjectInputStream(bis);
+ try{ return (Serializable)ois.readObject(); }
+ catch(ClassNotFoundException e){ throw new IOException(StringUtils.toString(e)); }
+ }
+ };
+
+ static
+ {
+ addDesc(boolean[].class);
+ addDesc(byte[].class);
+ addDesc(char[].class);
+ addDesc(short[].class);
+ addDesc(int[].class);
+ addDesc(long[].class);
+ addDesc(float[].class);
+ addDesc(double[].class);
+
+ addDesc(Boolean.class);
+ addDesc(Byte.class);
+ addDesc(Character.class);
+ addDesc(Short.class);
+ addDesc(Integer.class);
+ addDesc(Long.class);
+ addDesc(Float.class);
+ addDesc(Double.class);
+
+ addDesc(String.class);
+ addDesc(String[].class);
+
+ addDesc(ArrayList.class);
+ addDesc(HashMap.class);
+ addDesc(HashSet.class);
+ addDesc(Date.class);
+ addDesc(java.sql.Date.class);
+ addDesc(java.sql.Time.class);
+ addDesc(java.sql.Timestamp.class);
+ addDesc(java.util.LinkedList.class);
+ addDesc(java.util.LinkedHashMap.class);
+ addDesc(java.util.LinkedHashSet.class);
+
+ register(byte[].class, new Builder<byte[]>(){
+ @Override
+ public Class<byte[]> getType(){ return byte[].class; }
+ @Override
+ public void writeTo(byte[] obj, GenericObjectOutput out) throws IOException{ out.writeBytes(obj); }
+ @Override
+ public byte[] parseFrom(GenericObjectInput in) throws IOException{ return in.readBytes(); }
+ });
+ register(Boolean.class, new Builder<Boolean>(){
+ @Override
+ public Class<Boolean> getType(){ return Boolean.class; }
+ @Override
+ public void writeTo(Boolean obj, GenericObjectOutput out) throws IOException
+ {
+ if( obj == null )
+ out.write0(VARINT_N1);
+ else if( obj.booleanValue() )
+ out.write0(VARINT_1);
+ else
+ out.write0(VARINT_0);
+ }
+ @Override
+ public Boolean parseFrom(GenericObjectInput in) throws IOException
+ {
+ byte b = in.read0();
+ switch( b )
+ {
+ case VARINT_N1: return null;
+ case VARINT_0: return Boolean.FALSE;
+ case VARINT_1: return Boolean.TRUE;
+ default: throw new IOException("Input format error, expect VARINT_N1|VARINT_0|VARINT_1, get " + b + ".");
+ }
+ }
+ });
+ register(Byte.class, new Builder<Byte>(){
+ @Override
+ public Class<Byte> getType(){ return Byte.class; }
+ @Override
+ public void writeTo(Byte obj, GenericObjectOutput out) throws IOException
+ {
+ if( obj == null )
+ {
+ out.write0(OBJECT_NULL);
+ }
+ else
+ {
+ out.write0(OBJECT_VALUE);
+ out.writeByte(obj.byteValue());
+ }
+ }
+ @Override
+ public Byte parseFrom(GenericObjectInput in) throws IOException
+ {
+ byte b = in.read0();
+ if( b == OBJECT_NULL )
+ return null;
+ if( b != OBJECT_VALUE )
+ throw new IOException("Input format error, expect OBJECT_NULL|OBJECT_VALUE, get " + b + ".");
+
+ return Byte.valueOf(in.readByte());
+ }
+ });
+ register(Character.class, new Builder<Character>(){
+ @Override
+ public Class<Character> getType(){ return Character.class; }
+ @Override
+ public void writeTo(Character obj, GenericObjectOutput out) throws IOException
+ {
+ if( obj == null )
+ {
+ out.write0(OBJECT_NULL);
+ }
+ else
+ {
+ out.write0(OBJECT_VALUE);
+ out.writeShort((short)obj.charValue());
+ }
+ }
+ @Override
+ public Character parseFrom(GenericObjectInput in) throws IOException
+ {
+ byte b = in.read0();
+ if( b == OBJECT_NULL )
+ return null;
+ if( b != OBJECT_VALUE )
+ throw new IOException("Input format error, expect OBJECT_NULL|OBJECT_VALUE, get " + b + ".");
+
+ return Character.valueOf((char)in.readShort());
+ }
+ });
+ register(Short.class, new Builder<Short>(){
+ @Override
+ public Class<Short> getType(){ return Short.class; }
+ @Override
+ public void writeTo(Short obj, GenericObjectOutput out) throws IOException
+ {
+ if( obj == null )
+ {
+ out.write0(OBJECT_NULL);
+ }
+ else
+ {
+ out.write0(OBJECT_VALUE);
+ out.writeShort(obj.shortValue());
+ }
+ }
+ @Override
+ public Short parseFrom(GenericObjectInput in) throws IOException
+ {
+ byte b = in.read0();
+ if( b == OBJECT_NULL )
+ return null;
+ if( b != OBJECT_VALUE )
+ throw new IOException("Input format error, expect OBJECT_NULL|OBJECT_VALUE, get " + b + ".");
+
+ return Short.valueOf(in.readShort());
+ }
+ });
+ register(Integer.class, new Builder<Integer>(){
+ @Override
+ public Class<Integer> getType(){ return Integer.class; }
+ @Override
+ public void writeTo(Integer obj, GenericObjectOutput out) throws IOException
+ {
+ if( obj == null )
+ {
+ out.write0(OBJECT_NULL);
+ }
+ else
+ {
+ out.write0(OBJECT_VALUE);
+ out.writeInt(obj.intValue());
+ }
+ }
+ @Override
+ public Integer parseFrom(GenericObjectInput in) throws IOException
+ {
+ byte b = in.read0();
+ if( b == OBJECT_NULL )
+ return null;
+ if( b != OBJECT_VALUE )
+ throw new IOException("Input format error, expect OBJECT_NULL|OBJECT_VALUE, get " + b + ".");
+
+ return Integer.valueOf(in.readInt());
+ }
+ });
+ register(Long.class, new Builder<Long>(){
+ @Override
+ public Class<Long> getType(){ return Long.class; }
+ @Override
+ public void writeTo(Long obj, GenericObjectOutput out) throws IOException
+ {
+ if( obj == null )
+ {
+ out.write0(OBJECT_NULL);
+ }
+ else
+ {
+ out.write0(OBJECT_VALUE);
+ out.writeLong(obj.longValue());
+ }
+ }
+ @Override
+ public Long parseFrom(GenericObjectInput in) throws IOException
+ {
+ byte b = in.read0();
+ if( b == OBJECT_NULL )
+ return null;
+ if( b != OBJECT_VALUE )
+ throw new IOException("Input format error, expect OBJECT_NULL|OBJECT_VALUE, get " + b + ".");
+
+ return Long.valueOf(in.readLong());
+ }
+ });
+ register(Float.class, new Builder<Float>(){
+ @Override
+ public Class<Float> getType(){ return Float.class; }
+ @Override
+ public void writeTo(Float obj, GenericObjectOutput out) throws IOException
+ {
+ if( obj == null )
+ {
+ out.write0(OBJECT_NULL);
+ }
+ else
+ {
+ out.write0(OBJECT_VALUE);
+ out.writeFloat(obj.floatValue());
+ }
+ }
+ @Override
+ public Float parseFrom(GenericObjectInput in) throws IOException
+ {
+ byte b = in.read0();
+ if( b == OBJECT_NULL )
+ return null;
+ if( b != OBJECT_VALUE )
+ throw new IOException("Input format error, expect OBJECT_NULL|OBJECT_VALUE, get " + b + ".");
+
+ return new Float(in.readFloat());
+ }
+ });
+ register(Double.class, new Builder<Double>(){
+ @Override
+ public Class<Double> getType(){ return Double.class; }
+ @Override
+ public void writeTo(Double obj, GenericObjectOutput out) throws IOException
+ {
+ if( obj == null )
+ {
+ out.write0(OBJECT_NULL);
+ }
+ else
+ {
+ out.write0(OBJECT_VALUE);
+ out.writeDouble(obj.doubleValue());
+ }
+ }
+ @Override
+ public Double parseFrom(GenericObjectInput in) throws IOException
+ {
+ byte b = in.read0();
+ if( b == OBJECT_NULL )
+ return null;
+ if( b != OBJECT_VALUE )
+ throw new IOException("Input format error, expect OBJECT_NULL|OBJECT_VALUE, get " + b + ".");
+
+ return new Double(in.readDouble());
+ }
+ });
+ register(String.class, new Builder<String>(){
+ @Override
+ public Class<String> getType(){ return String.class; }
+ @Override
+ public String parseFrom(GenericObjectInput in) throws IOException{ return in.readUTF(); }
+ @Override
+ public void writeTo(String obj, GenericObjectOutput out) throws IOException{ out.writeUTF(obj); }
+ });
+ register(StringBuilder.class, new Builder<StringBuilder>(){
+ @Override
+ public Class<StringBuilder> getType(){ return StringBuilder.class; }
+ @Override
+ public StringBuilder parseFrom(GenericObjectInput in) throws IOException{ return new StringBuilder(in.readUTF()); }
+ @Override
+ public void writeTo(StringBuilder obj, GenericObjectOutput out) throws IOException{ out.writeUTF(obj.toString()); }
+ });
+ register(StringBuffer.class, new Builder<StringBuffer>(){
+ @Override
+ public Class<StringBuffer> getType(){ return StringBuffer.class; }
+ @Override
+ public StringBuffer parseFrom(GenericObjectInput in) throws IOException{ return new StringBuffer(in.readUTF()); }
+ @Override
+ public void writeTo(StringBuffer obj, GenericObjectOutput out) throws IOException{ out.writeUTF(obj.toString()); }
+ });
+
+ // java.util
+ register(ArrayList.class, new Builder<ArrayList>(){
+ @Override
+ public Class<ArrayList> getType(){ return ArrayList.class; }
+ @Override
+ public void writeTo(ArrayList obj, GenericObjectOutput out) throws IOException
+ {
+ if( obj == null )
+ {
+ out.write0(OBJECT_NULL);
+ }
+ else
+ {
+ out.write0(OBJECT_VALUES);
+ out.writeUInt(obj.size());
+ for( Object item : obj )
+ out.writeObject(item);
+ }
+ }
+ @Override
+ public ArrayList parseFrom(GenericObjectInput in) throws IOException
+ {
+ byte b = in.read0();
+ if( b == OBJECT_NULL )
+ return null;
+ if( b != OBJECT_VALUES )
+ throw new IOException("Input format error, expect OBJECT_NULL|OBJECT_VALUES, get " + b + ".");
+
+ int len = in.readUInt();
+ ArrayList ret = new ArrayList(len);
+ for(int i=0;i<len;i++)
+ ret.add(in.readObject());
+ return ret;
+ }
+ });
+ register(HashMap.class, new Builder<HashMap>(){
+ @Override
+ public Class<HashMap> getType(){ return HashMap.class; }
+ @Override
+ public void writeTo(HashMap obj, GenericObjectOutput out) throws IOException
+ {
+ if( obj == null )
+ {
+ out.write0(OBJECT_NULL);
+ }
+ else
+ {
+ out.write0(OBJECT_MAP);
+ out.writeUInt(obj.size());
+ for( Map.Entry entry : (Set<Map.Entry>)obj.entrySet() )
+ {
+ out.writeObject(entry.getKey());
+ out.writeObject(entry.getValue());
+ }
+ }
+ }
+ @Override
+ public HashMap parseFrom(GenericObjectInput in) throws IOException
+ {
+ byte b = in.read0();
+ if( b == OBJECT_NULL )
+ return null;
+ if( b != OBJECT_MAP )
+ throw new IOException("Input format error, expect OBJECT_NULL|OBJECT_MAP, get " + b + ".");
+
+ int len = in.readUInt();
+ HashMap ret = new HashMap(len);
+ for(int i=0;i<len;i++)
+ ret.put(in.readObject(), in.readObject());
+ return ret;
+ }
+ });
+ register(HashSet.class, new Builder<HashSet>(){
+ @Override
+ public Class<HashSet> getType(){ return HashSet.class; }
+ @Override
+ public void writeTo(HashSet obj, GenericObjectOutput out) throws IOException
+ {
+ if( obj == null )
+ {
+ out.write0(OBJECT_NULL);
+ }
+ else
+ {
+ out.write0(OBJECT_VALUES);
+ out.writeUInt(obj.size());
+ for( Object item : obj )
+ out.writeObject(item);
+ }
+ }
+ @Override
+ public HashSet parseFrom(GenericObjectInput in) throws IOException
+ {
+ byte b = in.read0();
+ if( b == OBJECT_NULL )
+ return null;
+ if( b != OBJECT_VALUES )
+ throw new IOException("Input format error, expect OBJECT_NULL|OBJECT_VALUES, get " + b + ".");
+
+ int len = in.readUInt();
+ HashSet ret = new HashSet(len);
+ for(int i=0;i<len;i++)
+ ret.add(in.readObject());
+ return ret;
+ }
+ });
+
+ register(Date.class, new Builder<Date>(){
+ @Override
+ public Class<Date> getType(){ return Date.class; }
+ @Override
+ public void writeTo(Date obj, GenericObjectOutput out) throws IOException
+ {
+ if( obj == null )
+ {
+ out.write0(OBJECT_NULL);
+ }
+ else
+ {
+ out.write0(OBJECT_VALUE);
+ out.writeLong(obj.getTime());
+ }
+ }
+ @Override
+ public Date parseFrom(GenericObjectInput in) throws IOException
+ {
+ byte b = in.read0();
+ if( b == OBJECT_NULL )
+ return null;
+ if( b != OBJECT_VALUE )
+ throw new IOException("Input format error, expect OBJECT_NULL|OBJECT_VALUE, get " + b + ".");
+
+ return new Date(in.readLong());
+ }
+ });
+
+ // java.sql
+ register(java.sql.Date.class, new Builder<java.sql.Date>(){
+ @Override
+ public Class<java.sql.Date> getType(){ return java.sql.Date.class; }
+ @Override
+ public void writeTo(java.sql.Date obj, GenericObjectOutput out) throws IOException
+ {
+ if( obj == null )
+ {
+ out.write0(OBJECT_NULL);
+ }
+ else
+ {
+ out.write0(OBJECT_VALUE);
+ out.writeLong(obj.getTime());
+ }
+ }
+ @Override
+ public java.sql.Date parseFrom(GenericObjectInput in) throws IOException
+ {
+ byte b = in.read0();
+ if( b == OBJECT_NULL )
+ return null;
+ if( b != OBJECT_VALUE )
+ throw new IOException("Input format error, expect OBJECT_NULL|OBJECT_VALUE, get " + b + ".");
+
+ return new java.sql.Date(in.readLong());
+ }
+ });
+ register(java.sql.Timestamp.class, new Builder<java.sql.Timestamp>(){
+ @Override
+ public Class<java.sql.Timestamp> getType(){ return java.sql.Timestamp.class; }
+ @Override
+ public void writeTo(java.sql.Timestamp obj, GenericObjectOutput out) throws IOException
+ {
+ if( obj == null )
+ {
+ out.write0(OBJECT_NULL);
+ }
+ else
+ {
+ out.write0(OBJECT_VALUE);
+ out.writeLong(obj.getTime());
+ }
+ }
+ @Override
+ public java.sql.Timestamp parseFrom(GenericObjectInput in) throws IOException
+ {
+ byte b = in.read0();
+ if( b == OBJECT_NULL )
+ return null;
+ if( b != OBJECT_VALUE )
+ throw new IOException("Input format error, expect OBJECT_NULL|OBJECT_VALUE, get " + b + ".");
+
+ return new java.sql.Timestamp(in.readLong());
+ }
+ });
+ register(java.sql.Time.class, new Builder<java.sql.Time>(){
+ @Override
+ public Class<java.sql.Time> getType(){ return java.sql.Time.class; }
+ @Override
+ public void writeTo(java.sql.Time obj, GenericObjectOutput out) throws IOException
+ {
+ if( obj == null )
+ {
+ out.write0(OBJECT_NULL);
+ }
+ else
+ {
+ out.write0(OBJECT_VALUE);
+ out.writeLong(obj.getTime());
+ }
+ }
+ @Override
+ public java.sql.Time parseFrom(GenericObjectInput in) throws IOException
+ {
+ byte b = in.read0();
+ if( b == OBJECT_NULL )
+ return null;
+ if( b != OBJECT_VALUE )
+ throw new IOException("Input format error, expect OBJECT_NULL|OBJECT_VALUE, get " + b + ".");
+
+ return new java.sql.Time(in.readLong());
+ }
+ });
+ }
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/dubbo/DubboSerialization.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/dubbo/DubboSerialization.java
new file mode 100644
index 0000000..b111122
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/dubbo/DubboSerialization.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.serialize.support.dubbo;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.serialize.ObjectInput;
+import com.alibaba.dubbo.common.serialize.ObjectOutput;
+import com.alibaba.dubbo.common.serialize.Serialization;
+
+/**
+ * @author ding.lid
+ */
+@Extension("dubbo")
+public class DubboSerialization implements Serialization {
+
+ public byte getContentTypeId() {
+ return 1;
+ }
+
+ public String getContentType() {
+ return "x-application/dubbo";
+ }
+
+ public ObjectOutput serialize(URL url, OutputStream out) throws IOException {
+ return new GenericObjectOutput(out);
+ }
+
+ public ObjectInput deserialize(URL url, InputStream is) throws IOException {
+ return new GenericObjectInput(is);
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/dubbo/GenericDataFlags.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/dubbo/GenericDataFlags.java
new file mode 100644
index 0000000..efab960
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/dubbo/GenericDataFlags.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.serialize.support.dubbo;
+
+/**
+ * Constants.
+ *
+ * @author qian.lei
+ */
+
+public interface GenericDataFlags
+{
+ // prefix three bits
+ byte VARINT = 0, OBJECT = (byte)0x80;
+
+ // varint tag
+ byte VARINT8 = VARINT, VARINT16 = VARINT | 1, VARINT24 = VARINT | 2, VARINT32 = VARINT | 3;
+
+ byte VARINT40 = VARINT | 4, VARINT48 = VARINT | 5, VARINT56 = VARINT | 6, VARINT64 = VARINT | 7;
+
+ // varint contants
+ byte VARINT_NF = VARINT | 10, VARINT_NE = VARINT | 11, VARINT_ND = VARINT | 12;
+
+ byte VARINT_NC = VARINT | 13, VARINT_NB = VARINT | 14, VARINT_NA = VARINT | 15, VARINT_N9 = VARINT | 16;
+
+ byte VARINT_N8 = VARINT | 17, VARINT_N7 = VARINT | 18, VARINT_N6 = VARINT | 19, VARINT_N5 = VARINT | 20;
+
+ byte VARINT_N4 = VARINT | 21, VARINT_N3 = VARINT | 22, VARINT_N2 = VARINT | 23, VARINT_N1 = VARINT | 24;
+
+ byte VARINT_0 = VARINT | 25, VARINT_1 = VARINT | 26, VARINT_2 = VARINT | 27, VARINT_3 = VARINT | 28;
+
+ byte VARINT_4 = VARINT | 29, VARINT_5 = VARINT | 30, VARINT_6 = VARINT | 31, VARINT_7 = VARINT | 32;
+
+ byte VARINT_8 = VARINT | 33, VARINT_9 = VARINT | 34, VARINT_A = VARINT | 35, VARINT_B = VARINT | 36;
+
+ byte VARINT_C = VARINT | 37, VARINT_D = VARINT | 38, VARINT_E = VARINT | 39, VARINT_F = VARINT | 40;
+
+ byte VARINT_10 = VARINT | 41, VARINT_11 = VARINT | 42, VARINT_12 = VARINT | 43, VARINT_13 = VARINT | 44;
+
+ byte VARINT_14 = VARINT | 45, VARINT_15 = VARINT | 46, VARINT_16 = VARINT | 47, VARINT_17 = VARINT | 48;
+
+ byte VARINT_18 = VARINT | 49, VARINT_19 = VARINT | 50, VARINT_1A = VARINT | 51, VARINT_1B = VARINT | 52;
+
+ byte VARINT_1C = VARINT | 53, VARINT_1D = VARINT | 54, VARINT_1E = VARINT | 55, VARINT_1F = VARINT | 56;
+
+ // object tag
+ byte OBJECT_REF = OBJECT | 1, OBJECT_STREAM = OBJECT | 2, OBJECT_BYTES = OBJECT | 3;
+
+ byte OBJECT_VALUE = OBJECT | 4, OBJECT_VALUES = OBJECT | 5, OBJECT_MAP = OBJECT | 6;
+
+ byte OBJECT_DESC = OBJECT | 10, OBJECT_DESC_ID = OBJECT | 11;
+
+ // object constants
+ byte OBJECT_NULL = OBJECT | 20, OBJECT_DUMMY = OBJECT | 21;
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/dubbo/GenericDataInput.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/dubbo/GenericDataInput.java
new file mode 100644
index 0000000..a9dc954
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/dubbo/GenericDataInput.java
@@ -0,0 +1,395 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.serialize.support.dubbo;
+
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.UTFDataFormatException;
+
+import com.alibaba.dubbo.common.serialize.DataInput;
+
+/**
+ * Default DataInput impl.
+ * Not thread-safe.
+ *
+ * @author qian.lei
+ */
+
+public class GenericDataInput implements DataInput, GenericDataFlags
+{
+ private static final String EMPTY_STRING = "";
+
+ private static final byte[] EMPTY_BYTES = {};
+
+ private final InputStream mInput;
+
+ private final byte[] mBuffer;
+
+ private int mRead = 0;
+
+ private int mPosition = 0;
+
+ public GenericDataInput(InputStream is)
+ {
+ this(is, 1024);
+ }
+
+ public GenericDataInput(InputStream is, int buffSize)
+ {
+ mInput = is;
+ mBuffer = new byte[buffSize];
+ }
+
+ public boolean readBool() throws IOException
+ {
+ byte b = read0();
+
+ switch( b )
+ {
+ case VARINT_0: return false;
+ case VARINT_1: return true;
+ default:
+ throw new IOException("Tag error, expect BYTE_TRUE|BYTE_FALSE, but get " + b);
+ }
+ }
+
+ public byte readByte() throws IOException
+ {
+ byte b = read0();
+
+ switch( b )
+ {
+ case VARINT8:
+ return read0();
+ case VARINT_0: return 0; case VARINT_1: return 1; case VARINT_2: return 2; case VARINT_3: return 3;
+ case VARINT_4: return 4; case VARINT_5: return 5; case VARINT_6: return 6; case VARINT_7: return 7;
+ case VARINT_8: return 8; case VARINT_9: return 9; case VARINT_A: return 10; case VARINT_B: return 11;
+ case VARINT_C: return 12; case VARINT_D: return 13; case VARINT_E: return 14; case VARINT_F: return 15;
+ case VARINT_10: return 16; case VARINT_11: return 17; case VARINT_12: return 18; case VARINT_13: return 19;
+ case VARINT_14: return 20; case VARINT_15: return 21; case VARINT_16: return 22; case VARINT_17: return 23;
+ case VARINT_18: return 24; case VARINT_19: return 25; case VARINT_1A: return 26; case VARINT_1B: return 27;
+ case VARINT_1C: return 28; case VARINT_1D: return 29; case VARINT_1E: return 30; case VARINT_1F: return 31;
+ default:
+ throw new IOException("Tag error, expect VARINT, but get " + b);
+ }
+ }
+
+ public short readShort() throws IOException
+ {
+ return (short)readVarint32();
+ }
+
+ public int readInt() throws IOException
+ {
+ return readVarint32();
+ }
+
+ public long readLong() throws IOException
+ {
+ return readVarint64();
+ }
+
+ public float readFloat() throws IOException
+ {
+ return Float.intBitsToFloat(readVarint32());
+ }
+
+ public double readDouble() throws IOException
+ {
+ return Double.longBitsToDouble(readVarint64());
+ }
+
+ public String readUTF() throws IOException
+ {
+ byte b = read0();
+
+ switch( b )
+ {
+ case OBJECT_BYTES:
+ int len = readUInt();
+ StringBuilder sb = new StringBuilder();
+
+ for(int i=0;i<len;i++)
+ {
+ byte b1 = read0();
+ if( (b1 & 0x80) == 0 )
+ {
+ sb.append((char)b1);
+ }
+ else if( (b1 & 0xE0) == 0xC0 )
+ {
+ byte b2 = read0();
+ sb.append((char)(((b1 & 0x1F) << 6) | (b2 & 0x3F)));
+ }
+ else if( (b1 & 0xF0) == 0xE0 )
+ {
+ byte b2 = read0(), b3 = read0();
+ sb.append((char)(((b1 & 0x0F) << 12) | ((b2 & 0x3F) << 6) | (b3 & 0x3F)));
+ }
+ else
+ throw new UTFDataFormatException("Bad utf-8 encoding at " + b1);
+ }
+ return sb.toString();
+ case OBJECT_NULL: return null;
+ case OBJECT_DUMMY: return EMPTY_STRING;
+ default:
+ throw new IOException("Tag error, expect BYTES|BYTES_NULL|BYTES_EMPTY, but get " + b);
+ }
+ }
+
+ public byte[] readBytes() throws IOException
+ {
+ byte b = read0();
+
+ switch( b )
+ {
+ case OBJECT_BYTES: return read0(readUInt());
+ case OBJECT_NULL: return null;
+ case OBJECT_DUMMY: return EMPTY_BYTES;
+ default:
+ throw new IOException("Tag error, expect BYTES|BYTES_NULL|BYTES_EMPTY, but get " + b);
+ }
+ }
+
+ public int readUInt() throws IOException
+ {
+ byte tmp = read0();
+ if( tmp < 0 )
+ return tmp & 0x7f;
+
+ int ret = tmp & 0x7f;
+ if( ( tmp = read0() ) < 0 )
+ {
+ ret |= ( tmp & 0x7f ) << 7;
+ }
+ else
+ {
+ ret |= tmp << 7;
+ if( ( tmp = read0() ) < 0 )
+ {
+ ret |= ( tmp & 0x7f ) << 14;
+ }
+ else
+ {
+ ret |= tmp << 14;
+ if( ( tmp = read0() ) < 0 )
+ {
+ ret |= ( tmp & 0x7f ) << 21;
+ }
+ else
+ {
+ ret |= tmp << 21;
+ ret |= ( read0() & 0x7f ) << 28;
+ }
+ }
+ }
+ return ret;
+ }
+
+ protected byte read0() throws IOException
+ {
+ if( mPosition == mRead )
+ fillBuffer();
+
+ return mBuffer[mPosition++];
+ }
+
+ protected byte[] read0(int len) throws IOException
+ {
+ int rem = mRead - mPosition;
+ byte[] ret = new byte[len];
+ if( len <= rem )
+ {
+ System.arraycopy(mBuffer, mPosition, ret, 0, len);
+ mPosition += len;
+ }
+ else
+ {
+ System.arraycopy(mBuffer, mPosition, ret, 0, rem);
+ mPosition = mRead;
+
+ len -= rem;
+ int read, pos = rem;
+
+ while( len > 0 )
+ {
+ read = mInput.read(ret, pos, len);
+ if( read == -1 )
+ throw new EOFException();
+ pos += read;
+ len -= read;
+ }
+ }
+ return ret;
+ }
+
+ private int readVarint32() throws IOException
+ {
+ byte b = read0();
+
+ switch( b )
+ {
+ case VARINT8:
+ return read0();
+ case VARINT16:
+ {
+ byte b1 = read0(), b2 = read0();
+ return (short)( ( b1 & 0xff ) | ( ( b2 & 0xff ) << 8 ) );
+ }
+ case VARINT24:
+ {
+ byte b1 = read0(), b2 = read0(), b3 = read0();
+ int ret = ( b1 & 0xff ) | ( ( b2 & 0xff ) << 8 ) | ( ( b3 & 0xff ) << 16 );
+ if( b3 < 0 )
+ return ret | 0xff000000;
+ return ret;
+ }
+ case VARINT32:
+ {
+ byte b1 = read0(), b2 = read0(), b3 = read0(), b4 = read0();
+ return ( ( b1 & 0xff ) |
+ ( ( b2 & 0xff ) << 8 ) |
+ ( ( b3 & 0xff ) << 16 ) |
+ ( ( b4 & 0xff ) << 24 ) );
+ }
+ case VARINT_NF: return -15; case VARINT_NE: return -14; case VARINT_ND: return -13;
+ case VARINT_NC: return -12; case VARINT_NB: return -11; case VARINT_NA: return -10; case VARINT_N9: return -9;
+ case VARINT_N8: return -8; case VARINT_N7: return -7; case VARINT_N6: return -6; case VARINT_N5: return -5;
+ case VARINT_N4: return -4; case VARINT_N3: return -3; case VARINT_N2: return -2; case VARINT_N1: return -1;
+ case VARINT_0: return 0; case VARINT_1: return 1; case VARINT_2: return 2; case VARINT_3: return 3;
+ case VARINT_4: return 4; case VARINT_5: return 5; case VARINT_6: return 6; case VARINT_7: return 7;
+ case VARINT_8: return 8; case VARINT_9: return 9; case VARINT_A: return 10; case VARINT_B: return 11;
+ case VARINT_C: return 12; case VARINT_D: return 13; case VARINT_E: return 14; case VARINT_F: return 15;
+ case VARINT_10: return 16; case VARINT_11: return 17; case VARINT_12: return 18; case VARINT_13: return 19;
+ case VARINT_14: return 20; case VARINT_15: return 21; case VARINT_16: return 22; case VARINT_17: return 23;
+ case VARINT_18: return 24; case VARINT_19: return 25; case VARINT_1A: return 26; case VARINT_1B: return 27;
+ case VARINT_1C: return 28; case VARINT_1D: return 29; case VARINT_1E: return 30; case VARINT_1F: return 31;
+ default:
+ throw new IOException("Tag error, expect VARINT, but get " + b);
+ }
+ }
+
+ private long readVarint64() throws IOException
+ {
+ byte b = read0();
+
+ switch( b )
+ {
+ case VARINT8:
+ return read0();
+ case VARINT16:
+ {
+ byte b1 = read0(), b2 = read0();
+ return (short)( ( b1 & 0xff ) | ( ( b2 & 0xff ) << 8 ) );
+ }
+ case VARINT24:
+ {
+ byte b1 = read0(), b2 = read0(), b3 = read0();
+ int ret = ( b1 & 0xff ) | ( ( b2 & 0xff ) << 8 ) | ( ( b3 & 0xff ) << 16 );
+ if( b3 < 0 )
+ return ret | 0xff000000;
+ return ret;
+ }
+ case VARINT32:
+ {
+ byte b1 = read0(), b2 = read0(), b3 = read0(), b4 = read0();
+ return ( ( b1 & 0xff ) |
+ ( ( b2 & 0xff ) << 8 ) |
+ ( ( b3 & 0xff ) << 16 ) |
+ ( ( b4 & 0xff ) << 24 ) );
+ }
+ case VARINT40:
+ {
+ byte b1 = read0(), b2 = read0(), b3 = read0(), b4 = read0(), b5 = read0();
+ long ret = ( (long)b1 & 0xff ) |
+ ( ( (long)b2 & 0xff ) << 8 ) |
+ ( ( (long)b3 & 0xff ) << 16 ) |
+ ( ( (long)b4 & 0xff ) << 24 ) |
+ ( ( (long)b5 & 0xff ) << 32 );
+ if( b5 < 0 )
+ return ret | 0xffffff0000000000l;
+ return ret;
+ }
+ case VARINT48:
+ {
+ byte b1 = read0(), b2 = read0(), b3 = read0(), b4 = read0(), b5 = read0(), b6 = read0();
+ long ret = ( (long)b1 & 0xff ) |
+ ( ( (long)b2 & 0xff ) << 8 ) |
+ ( ( (long)b3 & 0xff ) << 16 ) |
+ ( ( (long)b4 & 0xff ) << 24 ) |
+ ( ( (long)b5 & 0xff ) << 32 ) |
+ ( ( (long)b6 & 0xff ) << 40 );
+ if( b6 < 0 )
+ return ret | 0xffff000000000000l;
+ return ret;
+ }
+ case VARINT56:
+ {
+ byte b1 = read0(), b2 = read0(), b3 = read0(), b4 = read0(), b5 = read0(), b6 = read0(), b7 = read0();
+ long ret = ( (long)b1 & 0xff ) |
+ ( ( (long)b2 & 0xff ) << 8 ) |
+ ( ( (long)b3 & 0xff ) << 16 ) |
+ ( ( (long)b4 & 0xff ) << 24 ) |
+ ( ( (long)b5 & 0xff ) << 32 ) |
+ ( ( (long)b6 & 0xff ) << 40 ) |
+ ( ( (long)b7 & 0xff ) << 48 );
+ if( b7 < 0 )
+ return ret | 0xff00000000000000l;
+ return ret;
+ }
+ case VARINT64:
+ {
+ byte b1 = read0(), b2 = read0(), b3 = read0(), b4 = read0();
+ byte b5 = read0(), b6 = read0(), b7 = read0(), b8 = read0();
+ return ( ( (long)b1 & 0xff ) |
+ ( ( (long)b2 & 0xff ) << 8 ) |
+ ( ( (long)b3 & 0xff ) << 16 ) |
+ ( ( (long)b4 & 0xff ) << 24 ) |
+ ( ( (long)b5 & 0xff ) << 32 ) |
+ ( ( (long)b6 & 0xff ) << 40 ) |
+ ( ( (long)b7 & 0xff ) << 48 ) |
+ ( ( (long)b8 & 0xff ) << 56 ) );
+ }
+ case VARINT_NF: return -15; case VARINT_NE: return -14; case VARINT_ND: return -13;
+ case VARINT_NC: return -12; case VARINT_NB: return -11; case VARINT_NA: return -10; case VARINT_N9: return -9;
+ case VARINT_N8: return -8; case VARINT_N7: return -7; case VARINT_N6: return -6; case VARINT_N5: return -5;
+ case VARINT_N4: return -4; case VARINT_N3: return -3; case VARINT_N2: return -2; case VARINT_N1: return -1;
+ case VARINT_0: return 0; case VARINT_1: return 1; case VARINT_2: return 2; case VARINT_3: return 3;
+ case VARINT_4: return 4; case VARINT_5: return 5; case VARINT_6: return 6; case VARINT_7: return 7;
+ case VARINT_8: return 8; case VARINT_9: return 9; case VARINT_A: return 10; case VARINT_B: return 11;
+ case VARINT_C: return 12; case VARINT_D: return 13; case VARINT_E: return 14; case VARINT_F: return 15;
+ case VARINT_10: return 16; case VARINT_11: return 17; case VARINT_12: return 18; case VARINT_13: return 19;
+ case VARINT_14: return 20; case VARINT_15: return 21; case VARINT_16: return 22; case VARINT_17: return 23;
+ case VARINT_18: return 24; case VARINT_19: return 25; case VARINT_1A: return 26; case VARINT_1B: return 27;
+ case VARINT_1C: return 28; case VARINT_1D: return 29; case VARINT_1E: return 30; case VARINT_1F: return 31;
+ default:
+ throw new IOException("Tag error, expect VARINT, but get " + b);
+ }
+ }
+
+ private void fillBuffer() throws IOException
+ {
+ mPosition = 0;
+ mRead = mInput.read(mBuffer);
+
+ if( mRead == -1 )
+ {
+ mRead = 0;
+ throw new EOFException();
+ }
+ }
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/dubbo/GenericDataOutput.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/dubbo/GenericDataOutput.java
new file mode 100644
index 0000000..b50ca1f
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/dubbo/GenericDataOutput.java
@@ -0,0 +1,344 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.serialize.support.dubbo;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+import com.alibaba.dubbo.common.serialize.DataOutput;
+
+/**
+ * Default data output impl.
+ * Not thread-safe.
+ *
+ * @author qian.lei
+ */
+
+public class GenericDataOutput implements DataOutput, GenericDataFlags
+{
+ private static final int CHAR_BUF_SIZE = 256;
+
+ private final byte[] mBuffer, mTemp = new byte[9];
+
+ private final char[] mCharBuf = new char[CHAR_BUF_SIZE];
+
+ private final OutputStream mOutput;
+
+ private final int mLimit;
+
+ private int mPosition = 0;
+
+ public GenericDataOutput(OutputStream out)
+ {
+ this(out, 1024);
+ }
+
+ public GenericDataOutput(OutputStream out, int buffSize)
+ {
+ mOutput = out;
+ mLimit = buffSize;
+ mBuffer = new byte[buffSize];
+ }
+
+ public void writeBool(boolean v) throws IOException
+ {
+ write0( v ? VARINT_1 : VARINT_0 );
+ }
+
+ public void writeByte(byte v) throws IOException
+ {
+ switch( v )
+ {
+ case 0: write0(VARINT_0); break; case 1: write0(VARINT_1); break; case 2: write0(VARINT_2); break; case 3: write0(VARINT_3); break;
+ case 4: write0(VARINT_4); break; case 5: write0(VARINT_5); break; case 6: write0(VARINT_6); break; case 7: write0(VARINT_7); break;
+ case 8: write0(VARINT_8); break; case 9: write0(VARINT_9); break; case 10: write0(VARINT_A); break; case 11: write0(VARINT_B); break;
+ case 12: write0(VARINT_C); break; case 13: write0(VARINT_D); break; case 14: write0(VARINT_E); break; case 15: write0(VARINT_F); break;
+ case 16: write0(VARINT_10); break; case 17: write0(VARINT_11); break; case 18: write0(VARINT_12); break; case 19: write0(VARINT_13); break;
+ case 20: write0(VARINT_14); break; case 21: write0(VARINT_15); break; case 22: write0(VARINT_16); break; case 23: write0(VARINT_17); break;
+ case 24: write0(VARINT_18); break; case 25: write0(VARINT_19); break; case 26: write0(VARINT_1A); break; case 27: write0(VARINT_1B); break;
+ case 28: write0(VARINT_1C); break; case 29: write0(VARINT_1D); break; case 30: write0(VARINT_1E); break; case 31: write0(VARINT_1F); break;
+ default:
+ write0(VARINT8);
+ write0(v);
+ }
+ }
+
+ public void writeShort(short v) throws IOException
+ {
+ writeVarint32(v);
+ }
+
+ public void writeInt(int v) throws IOException
+ {
+ writeVarint32(v);
+ }
+
+ public void writeLong(long v) throws IOException
+ {
+ writeVarint64(v);
+ }
+
+ public void writeFloat(float v) throws IOException
+ {
+ writeVarint32(Float.floatToRawIntBits(v));
+ }
+
+ public void writeDouble(double v) throws IOException
+ {
+ writeVarint64(Double.doubleToRawLongBits(v));
+ }
+
+ public void writeUTF(String v) throws IOException
+ {
+ if( v == null )
+ {
+ write0(OBJECT_NULL);
+ }
+ else
+ {
+ int len = v.length();
+ if( len == 0 )
+ {
+ write0(OBJECT_DUMMY);
+ }
+ else
+ {
+ write0(OBJECT_BYTES);
+ writeUInt(len);
+
+ int off = 0, limit = mLimit - 3, size;
+ char[] buf = mCharBuf;
+ do
+ {
+ size = Math.min(len-off, CHAR_BUF_SIZE);
+ v.getChars(off, off+size, buf, 0);
+
+ for(int i=0;i<size;i++)
+ {
+ char c = buf[i];
+ if( mPosition > limit )
+ {
+ if( c < 0x80 )
+ {
+ write0((byte)c);
+ }
+ else if( c < 0x800 )
+ {
+ write0((byte)(0xC0 | ((c >> 6) & 0x1F)));
+ write0((byte)(0x80 | (c & 0x3F)));
+ }
+ else
+ {
+ write0((byte)(0xE0 | ((c >> 12) & 0x0F)));
+ write0((byte)(0x80 | ((c >> 6) & 0x3F)));
+ write0((byte)(0x80 | (c & 0x3F)));
+ }
+ }
+ else
+ {
+ if( c < 0x80 )
+ {
+ mBuffer[mPosition++] = (byte)c;
+ }
+ else if( c < 0x800 )
+ {
+ mBuffer[mPosition++] = (byte)(0xC0 | ((c >> 6) & 0x1F));
+ mBuffer[mPosition++] = (byte)(0x80 | (c & 0x3F));
+ }
+ else
+ {
+ mBuffer[mPosition++] = (byte)(0xE0 | ((c >> 12) & 0x0F));
+ mBuffer[mPosition++] = (byte)(0x80 | ((c >> 6) & 0x3F));
+ mBuffer[mPosition++] = (byte)(0x80 | (c & 0x3F));
+ }
+ }
+ }
+ off += size;
+ }
+ while( off < len );
+ }
+ }
+ }
+
+ public void writeBytes(byte[] b) throws IOException
+ {
+ if( b == null )
+ write0(OBJECT_NULL);
+ else
+ writeBytes(b, 0, b.length);
+ }
+
+ public void writeBytes(byte[] b, int off, int len) throws IOException
+ {
+ if( len == 0 )
+ {
+ write0(OBJECT_DUMMY);
+ }
+ else
+ {
+ write0(OBJECT_BYTES);
+ writeUInt(len);
+ write0(b, off, len);
+ }
+ }
+
+ public void flushBuffer() throws IOException
+ {
+ if( mPosition > 0 )
+ {
+ mOutput.write(mBuffer, 0, mPosition);
+ mPosition = 0;
+ }
+ }
+
+ public void writeUInt(int v) throws IOException
+ {
+ byte tmp;
+ while( true )
+ {
+ tmp = (byte)( v & 0x7f );
+ if( ( v >>>= 7 ) == 0 )
+ {
+ write0( (byte)( tmp | 0x80 ) );
+ return;
+ }
+ else
+ {
+ write0(tmp);
+ }
+ }
+ }
+
+ protected void write0(byte b) throws IOException
+ {
+ if( mPosition == mLimit )
+ flushBuffer();
+
+ mBuffer[mPosition++] = b;
+ }
+
+ protected void write0(byte[] b, int off, int len) throws IOException
+ {
+ int rem = mLimit - mPosition;
+ if( rem > len )
+ {
+ System.arraycopy(b, off, mBuffer, mPosition, len);
+ mPosition += len;
+ }
+ else
+ {
+ System.arraycopy(b, off, mBuffer, mPosition, rem);
+ mPosition = mLimit;
+ flushBuffer();
+
+ off += rem;
+ len -= rem;
+
+ if( mLimit > len )
+ {
+ System.arraycopy(b, off, mBuffer, 0, len);
+ mPosition = len;
+ }
+ else
+ {
+ mOutput.write(b, off, len);
+ }
+ }
+ }
+
+ private void writeVarint32(int v) throws IOException
+ {
+ switch( v )
+ {
+ case -15: write0(VARINT_NF); break; case -14: write0(VARINT_NE); break; case -13: write0(VARINT_ND); break;
+ case -12: write0(VARINT_NC); break; case -11: write0(VARINT_NB); break; case -10: write0(VARINT_NA); break; case -9: write0(VARINT_N9); break;
+ case -8: write0(VARINT_N8); break; case -7: write0(VARINT_N7); break; case -6: write0(VARINT_N6); break; case -5: write0(VARINT_N5); break;
+ case -4: write0(VARINT_N4); break; case -3: write0(VARINT_N3); break; case -2: write0(VARINT_N2); break; case -1: write0(VARINT_N1); break;
+ case 0: write0(VARINT_0); break; case 1: write0(VARINT_1); break; case 2: write0(VARINT_2); break; case 3: write0(VARINT_3); break;
+ case 4: write0(VARINT_4); break; case 5: write0(VARINT_5); break; case 6: write0(VARINT_6); break; case 7: write0(VARINT_7); break;
+ case 8: write0(VARINT_8); break; case 9: write0(VARINT_9); break; case 10: write0(VARINT_A); break; case 11: write0(VARINT_B); break;
+ case 12: write0(VARINT_C); break; case 13: write0(VARINT_D); break; case 14: write0(VARINT_E); break; case 15: write0(VARINT_F); break;
+ case 16: write0(VARINT_10); break; case 17: write0(VARINT_11); break; case 18: write0(VARINT_12); break; case 19: write0(VARINT_13); break;
+ case 20: write0(VARINT_14); break; case 21: write0(VARINT_15); break; case 22: write0(VARINT_16); break; case 23: write0(VARINT_17); break;
+ case 24: write0(VARINT_18); break; case 25: write0(VARINT_19); break; case 26: write0(VARINT_1A); break; case 27: write0(VARINT_1B); break;
+ case 28: write0(VARINT_1C); break; case 29: write0(VARINT_1D); break; case 30: write0(VARINT_1E); break; case 31: write0(VARINT_1F); break;
+ default:
+ int t = v, ix = 0;
+ byte[] b = mTemp;
+
+ while( true )
+ {
+ b[++ix] = (byte)( v & 0xff );
+ if( ( v >>>= 8 ) == 0 )
+ break;
+ }
+
+ if( t > 0 )
+ {
+ // [ 0a e2 => 0a e2 00 ] [ 92 => 92 00 ]
+ if( b[ix] < 0 )
+ b[++ix] = 0;
+ }
+ else
+ {
+ // [ 01 ff ff ff => 01 ff ] [ e0 ff ff ff => e0 ]
+ while( b[ix] == (byte)0xff && b[ix-1] < 0 )
+ ix--;
+ }
+
+ b[0] = (byte)( VARINT + ix - 1 );
+ write0(b, 0, ix+1);
+ }
+ }
+
+ private void writeVarint64(long v) throws IOException
+ {
+ int i = (int)v;
+ if( v == i )
+ {
+ writeVarint32(i);
+ }
+ else
+ {
+ long t = v;
+ int ix = 0;
+ byte[] b = mTemp;
+
+ while( true )
+ {
+ b[++ix] = (byte)( v & 0xff );
+ if( ( v >>>= 8 ) == 0 )
+ break;
+ }
+
+ if( t > 0 )
+ {
+ // [ 0a e2 => 0a e2 00 ] [ 92 => 92 00 ]
+ if( b[ix] < 0 )
+ b[++ix] = 0;
+ }
+ else
+ {
+ // [ 01 ff ff ff => 01 ff ] [ e0 ff ff ff => e0 ]
+ while( b[ix] == (byte)0xff && b[ix-1] < 0 )
+ ix--;
+ }
+
+ b[0] = (byte)( VARINT + ix - 1 );
+ write0(b, 0, ix+1);
+ }
+ }
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/dubbo/GenericObjectInput.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/dubbo/GenericObjectInput.java
new file mode 100644
index 0000000..3ec6570
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/dubbo/GenericObjectInput.java
@@ -0,0 +1,236 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.serialize.support.dubbo;
+
+import java.io.IOException;
+import java.io.InputStream;
+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> c) 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..3752306
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/hessian/Hessian2ObjectInput.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.support.hessian;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+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);
+ }
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/hessian/Hessian2ObjectOutput.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/hessian/Hessian2ObjectOutput.java
new file mode 100644
index 0000000..1f3c57d
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/hessian/Hessian2ObjectOutput.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.serialize.support.hessian;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+import com.alibaba.com.caucho.hessian.io.Hessian2Output;
+import com.alibaba.dubbo.common.serialize.ObjectOutput;
+
+/**
+ * Hessian2 Object output.
+ *
+ * @author qian.lei
+ */
+
+public class Hessian2ObjectOutput implements ObjectOutput
+{
+ private final Hessian2Output mH2o;
+
+ public Hessian2ObjectOutput(OutputStream os)
+ {
+ mH2o = new Hessian2Output(os);
+ mH2o.setSerializerFactory(Hessian2SerializerFactory.SERIALIZER_FACTORY);
+ }
+
+ public void writeBool(boolean v) throws IOException
+ {
+ mH2o.writeBoolean(v);
+ }
+
+ public void writeByte(byte v) throws IOException
+ {
+ mH2o.writeInt(v);
+ }
+
+ public void writeShort(short v) throws IOException
+ {
+ mH2o.writeInt(v);
+ }
+
+ public void writeInt(int v) throws IOException
+ {
+ mH2o.writeInt(v);
+ }
+
+ public void writeLong(long v) throws IOException
+ {
+ mH2o.writeLong(v);
+ }
+
+ public void writeFloat(float v) throws IOException
+ {
+ mH2o.writeDouble(v);
+ }
+
+ public void writeDouble(double v) throws IOException
+ {
+ mH2o.writeDouble(v);
+ }
+
+ public void writeBytes(byte[] b) throws IOException
+ {
+ mH2o.writeBytes(b);
+ }
+
+ public void writeBytes(byte[] b, int off, int len) throws IOException
+ {
+ mH2o.writeBytes(b, off, len);
+ }
+
+ public void writeUTF(String v) throws IOException
+ {
+ mH2o.writeString(v);
+ }
+
+ public void writeObject(Object obj) throws IOException
+ {
+ mH2o.writeObject(obj);
+ }
+
+ public void flushBuffer() throws IOException
+ {
+ mH2o.flushBuffer();
+ }
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/hessian/Hessian2Serialization.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/hessian/Hessian2Serialization.java
new file mode 100644
index 0000000..1c8431d
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/hessian/Hessian2Serialization.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.serialize.support.hessian;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.serialize.ObjectInput;
+import com.alibaba.dubbo.common.serialize.ObjectOutput;
+import com.alibaba.dubbo.common.serialize.Serialization;
+
+/**
+ * @author ding.lid
+ */
+@Extension("hessian2")
+public class Hessian2Serialization implements Serialization {
+
+ public static final byte ID = 2;
+
+ public byte getContentTypeId() {
+ return ID;
+ }
+
+ public String getContentType() {
+ return "x-application/hessian2";
+ }
+
+ public ObjectOutput serialize(URL url, OutputStream out) throws IOException {
+ return new Hessian2ObjectOutput(out);
+ }
+
+ public ObjectInput deserialize(URL url, InputStream is) throws IOException {
+ return new Hessian2ObjectInput(is);
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/hessian/Hessian2SerializerFactory.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/hessian/Hessian2SerializerFactory.java
new file mode 100644
index 0000000..5289be0
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/hessian/Hessian2SerializerFactory.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.serialize.support.hessian;
+
+import com.alibaba.com.caucho.hessian.io.SerializerFactory;
+
+public class Hessian2SerializerFactory extends SerializerFactory {
+
+ public static final SerializerFactory SERIALIZER_FACTORY = new Hessian2SerializerFactory();
+
+ private Hessian2SerializerFactory() {
+ }
+
+ @Override
+ public ClassLoader getClassLoader() {
+ return Thread.currentThread().getContextClassLoader();
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/java/CompactedJavaSerialization.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/java/CompactedJavaSerialization.java
new file mode 100644
index 0000000..1535368
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/java/CompactedJavaSerialization.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.serialize.support.java;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.serialize.ObjectInput;
+import com.alibaba.dubbo.common.serialize.ObjectOutput;
+import com.alibaba.dubbo.common.serialize.Serialization;
+
+/**
+ * @author ding.lid
+ */
+@Extension("compactedjava")
+public class CompactedJavaSerialization implements Serialization {
+
+ public byte getContentTypeId() {
+ return 4;
+ }
+
+ public String getContentType() {
+ return "x-application/compactedjava";
+ }
+
+ public ObjectOutput serialize(URL url, OutputStream out) throws IOException {
+ return new JavaObjectOutput(out, true);
+ }
+
+ public ObjectInput deserialize(URL url, InputStream is) throws IOException {
+ return new JavaObjectInput(is, true);
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/java/CompactedObjectInputStream.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/java/CompactedObjectInputStream.java
new file mode 100644
index 0000000..34068f6
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/java/CompactedObjectInputStream.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.serialize.support.java;
+
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.ObjectInputStream;
+import java.io.ObjectStreamClass;
+import java.io.StreamCorruptedException;
+
+import com.alibaba.dubbo.common.utils.ClassHelper;
+
+/**
+ * Compacted java object input stream.
+ *
+ * @author qianlei
+ */
+
+public class CompactedObjectInputStream extends ObjectInputStream
+{
+ private ClassLoader mClassLoader;
+
+ public CompactedObjectInputStream(InputStream in) throws IOException
+ {
+ this(in, Thread.currentThread().getContextClassLoader());
+ }
+
+ public CompactedObjectInputStream(InputStream in, ClassLoader cl) throws IOException
+ {
+ super(in);
+ mClassLoader = cl == null ? ClassHelper.getClassLoader() : cl;
+ }
+
+ @Override
+ protected ObjectStreamClass readClassDescriptor() throws IOException,ClassNotFoundException
+ {
+ int type = read();
+ if( type < 0 )
+ throw new EOFException();
+ switch( type )
+ {
+ case 0:
+ return super.readClassDescriptor();
+ case 1:
+ Class<?> clazz = loadClass(readUTF());
+ return ObjectStreamClass.lookup(clazz);
+ default:
+ throw new StreamCorruptedException("Unexpected class descriptor type: " + type);
+ }
+ }
+
+ private Class<?> loadClass(String className) throws ClassNotFoundException
+ {
+ return mClassLoader.loadClass(className);
+ }
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/java/CompactedObjectOutputStream.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/java/CompactedObjectOutputStream.java
new file mode 100644
index 0000000..716cc21
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/java/CompactedObjectOutputStream.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.serialize.support.java;
+
+import java.io.IOException;
+import java.io.ObjectOutputStream;
+import java.io.ObjectStreamClass;
+import java.io.OutputStream;
+
+/**
+ * Compacted java object output stream.
+ *
+ * @author qianlei
+ */
+
+public class CompactedObjectOutputStream extends ObjectOutputStream
+{
+ public CompactedObjectOutputStream(OutputStream out) throws IOException
+ {
+ super(out);
+ }
+
+ @Override
+ protected void writeClassDescriptor(ObjectStreamClass desc) throws IOException
+ {
+ Class<?> clazz = desc.forClass();
+ if( clazz.isPrimitive() || clazz.isArray() )
+ {
+ write(0);
+ super.writeClassDescriptor(desc);
+ }
+ else
+ {
+ write(1);
+ writeUTF(desc.getName());
+ }
+ }
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/java/JavaObjectInput.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/java/JavaObjectInput.java
new file mode 100644
index 0000000..9b76905
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/java/JavaObjectInput.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.serialize.support.java;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.ObjectInputStream;
+
+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();
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/java/JavaObjectOutput.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/java/JavaObjectOutput.java
new file mode 100644
index 0000000..f12e031
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/java/JavaObjectOutput.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.serialize.support.java;
+
+import java.io.IOException;
+import java.io.ObjectOutputStream;
+import java.io.OutputStream;
+
+import com.alibaba.dubbo.common.serialize.ObjectOutput;
+
+/**
+ * Java Object output.
+ *
+ * @author qian.lei
+ */
+
+public class JavaObjectOutput implements ObjectOutput
+{
+ private final ObjectOutputStream mOut;
+
+ public JavaObjectOutput(OutputStream os) throws IOException
+ {
+ mOut = new ObjectOutputStream(os);
+ }
+
+ public JavaObjectOutput(OutputStream os, boolean compact) throws IOException
+ {
+ mOut = compact ? new CompactedObjectOutputStream(os) : new ObjectOutputStream(os);
+ }
+
+ public void writeBool(boolean v) throws IOException
+ {
+ mOut.writeBoolean(v);
+ }
+
+ public void writeByte(byte v) throws IOException
+ {
+ mOut.writeByte(v);
+ }
+
+ public void writeShort(short v) throws IOException
+ {
+ mOut.writeShort(v);
+ }
+
+ public void writeInt(int v) throws IOException
+ {
+ mOut.writeInt(v);
+ }
+
+ public void writeLong(long v) throws IOException
+ {
+ mOut.writeLong(v);
+ }
+
+ public void writeFloat(float v) throws IOException
+ {
+ mOut.writeFloat(v);
+ }
+
+ public void writeDouble(double v) throws IOException
+ {
+ mOut.writeDouble(v);
+ }
+
+ public void writeBytes(byte[] b) throws IOException
+ {
+ if( b == null )
+ mOut.writeInt(-1);
+ else
+ this.writeBytes(b, 0, b.length);
+ }
+
+ public void writeBytes(byte[] b, int off, int len) throws IOException
+ {
+ mOut.writeInt(len);
+ mOut.write(b, off, len);
+ }
+
+ public void writeUTF(String v) throws IOException
+ {
+ if( v == null )
+ {
+ mOut.writeInt(-1);
+ }
+ else
+ {
+ mOut.writeInt(v.length());
+ mOut.writeUTF(v);
+ }
+ }
+
+ public void writeObject(Object obj) throws IOException
+ {
+ if( obj == null )
+ {
+ mOut.writeByte(0);
+ }
+ else
+ {
+ mOut.writeByte(1);
+ mOut.writeObject(obj);
+ }
+ }
+
+ public void flushBuffer() throws IOException
+ {
+ mOut.flush();
+ }
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/java/JavaSerialization.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/java/JavaSerialization.java
new file mode 100644
index 0000000..58f1a1c
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/java/JavaSerialization.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.serialize.support.java;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.serialize.ObjectInput;
+import com.alibaba.dubbo.common.serialize.ObjectOutput;
+import com.alibaba.dubbo.common.serialize.Serialization;
+
+/**
+ * @author ding.lid
+ */
+@Extension("java")
+public class JavaSerialization implements Serialization {
+
+ public byte getContentTypeId() {
+ return 3;
+ }
+
+ public String getContentType() {
+ return "x-application/java";
+ }
+
+ public ObjectOutput serialize(URL url, OutputStream out) throws IOException {
+ return new JavaObjectOutput(out);
+ }
+
+ public ObjectInput deserialize(URL url, InputStream is) throws IOException {
+ return new JavaObjectInput(is);
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/json/FastJsonObjectInput.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/json/FastJsonObjectInput.java
new file mode 100644
index 0000000..79fa786
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/json/FastJsonObjectInput.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.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.util.Map;
+
+import com.alibaba.dubbo.common.serialize.ObjectInput;
+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();
+ }
+
+ @SuppressWarnings("unchecked")
+ public Object readObject() throws IOException, ClassNotFoundException {
+ String json = readLine();
+ if (json.startsWith("{")) {
+ return JSON.parseObject(json, Map.class);
+ } else {
+ json = "{\"value\":" + json + "}";
+ Map<String, Object> map = JSON.parseObject(json, Map.class);
+ return map.get("value");
+ }
+ }
+
+ public <T> T readObject(Class<T> cls) throws IOException, ClassNotFoundException {
+ return JSON.parseObject(readLine(), cls);
+ }
+
+ private String readLine() throws IOException, EOFException {
+ String line = reader.readLine();
+ if(line == null || line.trim().length() == 0) throw new EOFException();
+ return line;
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/json/FastJsonObjectOutput.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/json/FastJsonObjectOutput.java
new file mode 100644
index 0000000..ffaaf97
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/json/FastJsonObjectOutput.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.serialize.support.json;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.io.Writer;
+
+import com.alibaba.dubbo.common.serialize.ObjectOutput;
+import com.alibaba.fastjson.serializer.JSONSerializer;
+import com.alibaba.fastjson.serializer.SerializeWriter;
+import com.alibaba.fastjson.serializer.SerializerFeature;
+
+/**
+ * JsonObjectOutput
+ *
+ * @author william.liangf
+ */
+public class FastJsonObjectOutput implements ObjectOutput {
+
+ private final PrintWriter writer;
+
+ public FastJsonObjectOutput(OutputStream out) {
+ this(new OutputStreamWriter(out));
+ }
+
+ public FastJsonObjectOutput(Writer writer) {
+ this.writer = new PrintWriter(writer);
+ }
+
+ public void writeBool(boolean v) throws IOException {
+ writeObject(v);
+ }
+
+ public void writeByte(byte v) throws IOException {
+ writeObject(v);
+ }
+
+ public void writeShort(short v) throws IOException {
+ writeObject(v);
+ }
+
+ public void writeInt(int v) throws IOException {
+ writeObject(v);
+ }
+
+ public void writeLong(long v) throws IOException {
+ writeObject(v);
+ }
+
+ public void writeFloat(float v) throws IOException {
+ writeObject(v);
+ }
+
+ public void writeDouble(double v) throws IOException {
+ writeObject(v);
+ }
+
+ public void writeUTF(String v) throws IOException {
+ writeObject(v);
+ }
+
+ public void writeBytes(byte[] b) throws IOException {
+ writer.println(new String(b));
+ }
+
+ public void writeBytes(byte[] b, int off, int len) throws IOException {
+ writer.println(new String(b, off, len));
+ }
+
+ public void writeObject(Object obj) throws IOException {
+ SerializeWriter out = new SerializeWriter();
+ JSONSerializer serializer = new JSONSerializer(out);
+ serializer.config(SerializerFeature.WriteEnumUsingToString, true);
+ serializer.write(obj);
+ out.writeTo(writer);
+ writer.println();
+ writer.flush();
+ }
+
+ public void flushBuffer() throws IOException {
+ writer.flush();
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/json/FastJsonSerialization.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/json/FastJsonSerialization.java
new file mode 100644
index 0000000..5e94197
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/json/FastJsonSerialization.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.serialize.support.json;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.serialize.ObjectInput;
+import com.alibaba.dubbo.common.serialize.ObjectOutput;
+import com.alibaba.dubbo.common.serialize.Serialization;
+
+/**
+ * FastJsonSerialization
+ *
+ * @author william.liangf
+ */
+@Extension("fastjson")
+public class FastJsonSerialization implements Serialization {
+
+ public byte getContentTypeId() {
+ return 6;
+ }
+
+ public String getContentType() {
+ return "text/json";
+ }
+
+ public ObjectOutput serialize(URL url, OutputStream output) throws IOException {
+ return new FastJsonObjectOutput(output);
+ }
+
+ public ObjectInput deserialize(URL url, InputStream input) throws IOException {
+ return new FastJsonObjectInput(input);
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/json/JsonObjectInput.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/json/JsonObjectInput.java
new file mode 100644
index 0000000..6fc2154
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/json/JsonObjectInput.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.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.util.Map;
+
+import com.alibaba.dubbo.common.json.JSON;
+import com.alibaba.dubbo.common.json.ParseException;
+import com.alibaba.dubbo.common.serialize.ObjectInput;
+
+/**
+ * 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());
+ }
+ }
+
+ public <T> T readObject(Class<T> cls) throws IOException, ClassNotFoundException {
+ try {
+ return JSON.parse(readLine(), cls);
+ } catch (ParseException e) {
+ throw new IOException(e.getMessage());
+ }
+ }
+
+ private String readLine() throws IOException, EOFException {
+ String line = reader.readLine();
+ if(line == null || line.trim().length() == 0) throw new EOFException();
+ return line;
+ }
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/json/JsonObjectOutput.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/json/JsonObjectOutput.java
new file mode 100644
index 0000000..53ebc2f
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/json/JsonObjectOutput.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.serialize.support.json;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.io.Writer;
+
+import com.alibaba.dubbo.common.json.JSON;
+import com.alibaba.dubbo.common.serialize.ObjectOutput;
+
+/**
+ * JsonObjectOutput
+ *
+ * @author william.liangf
+ */
+public class JsonObjectOutput implements ObjectOutput {
+
+ private final PrintWriter writer;
+
+ private final boolean writeClass;
+
+ public JsonObjectOutput(OutputStream out) {
+ this(new OutputStreamWriter(out), false);
+ }
+
+ public JsonObjectOutput(Writer writer) {
+ this(writer, false);
+ }
+
+ public JsonObjectOutput(OutputStream out, boolean writeClass) {
+ this(new OutputStreamWriter(out), writeClass);
+ }
+
+ public JsonObjectOutput(Writer writer, boolean writeClass) {
+ this.writer = new PrintWriter(writer);
+ this.writeClass = writeClass;
+ }
+
+ public void writeBool(boolean v) throws IOException {
+ writeObject(v);
+ }
+
+ public void writeByte(byte v) throws IOException {
+ writeObject(v);
+ }
+
+ public void writeShort(short v) throws IOException {
+ writeObject(v);
+ }
+
+ public void writeInt(int v) throws IOException {
+ writeObject(v);
+ }
+
+ public void writeLong(long v) throws IOException {
+ writeObject(v);
+ }
+
+ public void writeFloat(float v) throws IOException {
+ writeObject(v);
+ }
+
+ public void writeDouble(double v) throws IOException {
+ writeObject(v);
+ }
+
+ public void writeUTF(String v) throws IOException {
+ writeObject(v);
+ }
+
+ public void writeBytes(byte[] b) throws IOException {
+ writer.println(new String(b));
+ }
+
+ public void writeBytes(byte[] b, int off, int len) throws IOException {
+ writer.println(new String(b, off, len));
+ }
+
+ public void writeObject(Object obj) throws IOException {
+ JSON.json(obj, writer, writeClass);
+ writer.println();
+ writer.flush();
+ }
+
+ public void flushBuffer() throws IOException {
+ writer.flush();
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/json/JsonSerialization.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/json/JsonSerialization.java
new file mode 100644
index 0000000..590e9df
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/serialize/support/json/JsonSerialization.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.serialize.support.json;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.serialize.ObjectInput;
+import com.alibaba.dubbo.common.serialize.ObjectOutput;
+import com.alibaba.dubbo.common.serialize.Serialization;
+
+/**
+ * JsonSerialization
+ *
+ * @author william.liangf
+ */
+@Extension("json")
+public class JsonSerialization implements Serialization {
+
+ public byte getContentTypeId() {
+ return 5;
+ }
+
+ public String getContentType() {
+ return "text/json";
+ }
+
+ public ObjectOutput serialize(URL url, OutputStream output) throws IOException {
+ return new JsonObjectOutput(output, url.getParameter("with.class", true));
+ }
+
+ public ObjectInput deserialize(URL url, InputStream input) throws IOException {
+ return new JsonObjectInput(input);
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/status/Status.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/status/Status.java
new file mode 100644
index 0000000..a4472f9
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/status/Status.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.status;
+
+/**
+ * Status
+ *
+ * @author william.liangf
+ */
+public class Status {
+
+ /**
+ * Level
+ */
+ public static enum Level {
+ OK, WARN, ERROR, UNKNOWN
+ }
+
+ private final Level level;
+
+ private final String message;
+
+ public Status(Level level){
+ this(level, null);
+ }
+
+ /**
+ *
+ * @param level
+ * @param message
+ */
+ public Status(Level level, String message){
+ this.level = level;
+ this.message = message;
+ }
+
+ public Level getLevel() {
+ return level;
+ }
+
+ public String getMessage() {
+ return message;
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/status/StatusChecker.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/status/StatusChecker.java
new file mode 100644
index 0000000..c97bad6
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/status/StatusChecker.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.status;
+
+import com.alibaba.dubbo.common.Extension;
+
+/**
+ * StatusChecker
+ *
+ * @author william.liangf
+ */
+@Extension
+public interface StatusChecker {
+
+ /**
+ * check status
+ *
+ * @return status
+ */
+ Status check();
+
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/status/support/LoadStatusChecker.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/status/support/LoadStatusChecker.java
new file mode 100644
index 0000000..dc08083
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/status/support/LoadStatusChecker.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.status.support;
+
+import java.lang.management.ManagementFactory;
+import java.lang.management.OperatingSystemMXBean;
+import java.lang.reflect.Method;
+
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.common.status.Status;
+import com.alibaba.dubbo.common.status.StatusChecker;
+
+/**
+ * Load Status
+ *
+ * @author william.liangf
+ */
+@Extension("load")
+public class LoadStatusChecker implements StatusChecker {
+
+ public Status check() {
+ OperatingSystemMXBean operatingSystemMXBean = ManagementFactory.getOperatingSystemMXBean();
+ double load;
+ try {
+ Method method = OperatingSystemMXBean.class.getMethod("getSystemLoadAverage", new Class<?>[0]);
+ load = (Double)method.invoke(operatingSystemMXBean, new Object[0]);
+ } catch (Throwable e) {
+ load = -1;
+ }
+ int cpu = operatingSystemMXBean.getAvailableProcessors();
+ return new Status(load < 0 ? Status.Level.UNKNOWN : (load < cpu ? Status.Level.OK : Status.Level.WARN), (load < 0 ? "" : "load:" + load + ",") + "cpu:" + cpu);
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/status/support/MemoryStatusChecker.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/status/support/MemoryStatusChecker.java
new file mode 100644
index 0000000..947b210
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/status/support/MemoryStatusChecker.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.status.support;
+
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.common.status.Status;
+import com.alibaba.dubbo.common.status.StatusChecker;
+
+/**
+ * MemoryStatus
+ *
+ * @author william.liangf
+ */
+@Extension("memory")
+public class MemoryStatusChecker implements StatusChecker {
+
+ public Status check() {
+ Runtime runtime = Runtime.getRuntime();
+ long freeMemory = runtime.freeMemory();
+ long totalMemory = runtime.totalMemory();
+ long maxMemory = runtime.maxMemory();
+ boolean ok = (maxMemory - (totalMemory - freeMemory) > 2048); // 剩余空间小于2M报警
+ String msg = "max:" + (maxMemory / 1024 / 1024) + "M,total:"
+ + (totalMemory / 1024 / 1024) + "M,used:" + ((totalMemory / 1024 / 1024) - (freeMemory / 1024 / 1024)) + "M";
+ 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..4223644
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/threadpool/ThreadPool.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.threadpool;
+
+import java.util.concurrent.Executor;
+
+import com.alibaba.dubbo.common.Adaptive;
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.common.URL;
+
+/**
+ * ThreadPool
+ *
+ * @author william.liangf
+ */
+@Extension("fixed")
+public interface ThreadPool {
+
+ /**
+ * 线程池
+ *
+ * @param url
+ * @return
+ */
+ @Adaptive({Constants.THREADPOOL_KEY})
+ Executor getExecutor(URL url);
+
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/threadpool/support/AbortPolicyWithReport.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/threadpool/support/AbortPolicyWithReport.java
new file mode 100644
index 0000000..4296e5e
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/threadpool/support/AbortPolicyWithReport.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.threadpool.support;
+
+import java.util.concurrent.RejectedExecutionException;
+import java.util.concurrent.ThreadPoolExecutor;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.logger.Logger;
+import com.alibaba.dubbo.common.logger.LoggerFactory;
+
+/**
+ * Abort Policy.
+ * Log warn info when abort.
+ *
+ * @author ding.lid
+ */
+public class AbortPolicyWithReport extends ThreadPoolExecutor.AbortPolicy {
+
+ protected static final Logger logger = LoggerFactory.getLogger(AbortPolicyWithReport.class);
+
+ private final String threadName;
+
+ private final URL url;
+
+ public AbortPolicyWithReport(String threadName, URL url) {
+ this.threadName = threadName;
+ this.url = url;
+ }
+
+ @Override
+ public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
+ String msg = String.format("Thread pool is EXHAUSTED!" +
+ " Thread Name: %s, Pool Size: %d (active: %d, core: %d, max: %d, largest: %d), Task: %d (completed: %d), Executor status:(isShutdown:%s, isTerminated:%s, isTerminating:%s), in %s!" ,
+ threadName, e.getPoolSize(), e.getActiveCount(), e.getCorePoolSize(), e.getMaximumPoolSize(), e.getLargestPoolSize(),
+ e.getTaskCount(), e.getCompletedTaskCount(), e.isShutdown(), e.isTerminated(), e.isTerminating(), url.toIdentityString());
+ logger.warn(msg);
+ throw new RejectedExecutionException(msg);
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/threadpool/support/cached/CachedThreadPool.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/threadpool/support/cached/CachedThreadPool.java
new file mode 100644
index 0000000..6f8fe45
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/threadpool/support/cached/CachedThreadPool.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.threadpool.support.cached;
+
+import java.util.concurrent.Executor;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.SynchronousQueue;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.threadpool.ThreadPool;
+import com.alibaba.dubbo.common.threadpool.support.AbortPolicyWithReport;
+import com.alibaba.dubbo.common.utils.NamedThreadFactory;
+
+/**
+ * CachedThreadPool
+ *
+ * @see java.util.concurrent.Executors#newCachedThreadPool()
+ * @author william.liangf
+ */
+@Extension("cached")
+public class CachedThreadPool implements ThreadPool {
+
+ public Executor getExecutor(URL url) {
+ String threadName = url.getParameter(Constants.THREAD_NAME_KEY, Constants.DEFAULT_THREAD_NAME);
+ int threads = url.getParameter(Constants.THREADS_KEY, Integer.MAX_VALUE);
+ int queues = url.getParameter(Constants.QUEUES_KEY, Constants.DEFAULT_QUEUES);
+ int keepalive = url.getParameter(Constants.THREAD_ALIVE_KEY, Constants.DEFAULT_THREAD_ALIVE);
+ return new ThreadPoolExecutor(0, threads, keepalive, TimeUnit.MILLISECONDS,
+ queues <= 0 ? new SynchronousQueue<Runnable>() : new LinkedBlockingQueue<Runnable>(queues),
+ new NamedThreadFactory(threadName, true), new AbortPolicyWithReport(threadName, url));
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/threadpool/support/fixed/FixedThreadPool.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/threadpool/support/fixed/FixedThreadPool.java
new file mode 100644
index 0000000..24a74b3
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/threadpool/support/fixed/FixedThreadPool.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.threadpool.support.fixed;
+
+import java.util.concurrent.Executor;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.SynchronousQueue;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.threadpool.ThreadPool;
+import com.alibaba.dubbo.common.threadpool.support.AbortPolicyWithReport;
+import com.alibaba.dubbo.common.utils.NamedThreadFactory;
+
+/**
+ * FixedThreadPool
+ *
+ * @see java.util.concurrent.Executors#newFixedThreadPool(int)
+ * @author william.liangf
+ */
+@Extension("fixed")
+public class FixedThreadPool implements ThreadPool {
+
+ public Executor getExecutor(URL url) {
+ String threadName = url.getParameter(Constants.THREAD_NAME_KEY, Constants.DEFAULT_THREAD_NAME);
+ int threads = url.getParameter(Constants.THREADS_KEY, Constants.DEFAULT_THREADS);
+ int queues = url.getParameter(Constants.QUEUES_KEY, Constants.DEFAULT_QUEUES);
+ return new ThreadPoolExecutor(threads, threads, Constants.DEFAULT_THREAD_ALIVE, TimeUnit.MILLISECONDS,
+ queues <= 0 ? new SynchronousQueue<Runnable>() : new LinkedBlockingQueue<Runnable>(queues),
+ new NamedThreadFactory(threadName, true), new AbortPolicyWithReport(threadName, url));
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/utils/AtomicPositiveInteger.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/utils/AtomicPositiveInteger.java
new file mode 100644
index 0000000..b1ade56
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/utils/AtomicPositiveInteger.java
@@ -0,0 +1,186 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.utils;
+
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * AtomicPositiveInteger
+ *
+ * @author william.liangf
+ * @author ding.lid
+ */
+public class AtomicPositiveInteger extends Number {
+
+ private static final long serialVersionUID = -3038533876489105940L;
+
+ private final AtomicInteger i;
+
+ public AtomicPositiveInteger() {
+ i = new AtomicInteger();
+ }
+
+ public AtomicPositiveInteger(int initialValue) {
+ i = new AtomicInteger(initialValue);
+ }
+
+ public final int getAndIncrement() {
+ for (;;) {
+ int current = i.get();
+ int next = (current >= Integer.MAX_VALUE ? 0 : current + 1);
+ if (i.compareAndSet(current, next)) {
+ return current;
+ }
+ }
+ }
+
+ public final int getAndDecrement() {
+ for (;;) {
+ int current = i.get();
+ int next = (current <= 0 ? Integer.MAX_VALUE : current - 1);
+ if (i.compareAndSet(current, next)) {
+ return current;
+ }
+ }
+ }
+
+ public final int incrementAndGet() {
+ for (;;) {
+ int current = i.get();
+ int next = (current >= Integer.MAX_VALUE ? 0 : current + 1);
+ if (i.compareAndSet(current, next)) {
+ return next;
+ }
+ }
+ }
+
+ public final int decrementAndGet() {
+ for (;;) {
+ int current = i.get();
+ int next = (current <= 0 ? Integer.MAX_VALUE : current - 1);
+ if (i.compareAndSet(current, next)) {
+ return next;
+ }
+ }
+ }
+
+ public final int get() {
+ return i.get();
+ }
+
+ public final void set(int newValue) {
+ if (newValue < 0) {
+ throw new IllegalArgumentException("new value " + newValue + " < 0");
+ }
+ i.set(newValue);
+ }
+
+ public final int getAndSet(int newValue) {
+ if (newValue < 0) {
+ throw new IllegalArgumentException("new value " + newValue + " < 0");
+ }
+ return i.getAndSet(newValue);
+ }
+
+ public final int getAndAdd(int delta) {
+ if (delta < 0) {
+ throw new IllegalArgumentException("delta " + delta + " < 0");
+ }
+ for (;;) {
+ int current = i.get();
+ int next = (current >= Integer.MAX_VALUE - delta + 1 ? delta - 1 : current + delta);
+ if (i.compareAndSet(current, next)) {
+ return current;
+ }
+ }
+ }
+
+ public final int addAndGet(int delta) {
+ if (delta < 0) {
+ throw new IllegalArgumentException("delta " + delta + " < 0");
+ }
+ for (;;) {
+ int current = i.get();
+ int next = (current >= Integer.MAX_VALUE - delta + 1 ? delta - 1 : current + delta);
+ if (i.compareAndSet(current, next)) {
+ return next;
+ }
+ }
+ }
+
+ public final boolean compareAndSet(int expect, int update) {
+ if (update < 0) {
+ throw new IllegalArgumentException("update value " + update + " < 0");
+ }
+ return i.compareAndSet(expect, update);
+ }
+
+ public final boolean weakCompareAndSet(int expect, int update) {
+ if (update < 0) {
+ throw new IllegalArgumentException("update value " + update + " < 0");
+ }
+ return i.weakCompareAndSet(expect, update);
+ }
+
+ public byte byteValue() {
+ return i.byteValue();
+ }
+
+ public short shortValue() {
+ return i.shortValue();
+ }
+
+ public int intValue() {
+ return i.intValue();
+ }
+
+ public long longValue() {
+ return i.longValue();
+ }
+
+ public float floatValue() {
+ return i.floatValue();
+ }
+
+ public double doubleValue() {
+ return i.doubleValue();
+ }
+
+ public String toString() {
+ return i.toString();
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((i == null) ? 0 : i.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) return true;
+ if (obj == null) return false;
+ if (getClass() != obj.getClass()) return false;
+ AtomicPositiveInteger other = (AtomicPositiveInteger) obj;
+ if (i == null) {
+ if (other.i != null) return false;
+ } else if (!i.equals(other.i)) return false;
+ return true;
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/utils/ClassHelper.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/utils/ClassHelper.java
new file mode 100644
index 0000000..0c66edd
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/utils/ClassHelper.java
@@ -0,0 +1,191 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.utils;
+
+import java.lang.reflect.Array;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+public class ClassHelper {
+
+ /**
+ * get class loader
+ *
+ * @param cls
+ * @return class loader
+ */
+ public static ClassLoader getClassLoader(Class<?> cls) {
+ ClassLoader cl = null;
+ try {
+ cl = Thread.currentThread().getContextClassLoader();
+ } catch (Throwable ex) {
+ // Cannot access thread context ClassLoader - falling back to system class loader...
+ }
+ if (cl == null) {
+ // No thread context class loader -> use class loader of this class.
+ cl = cls.getClassLoader();
+ }
+ return cl;
+ }
+
+ /**
+ * Return the default ClassLoader to use: typically the thread context
+ * ClassLoader, if available; the ClassLoader that loaded the ClassUtils
+ * class will be used as fallback.
+ * <p>
+ * Call this method if you intend to use the thread context ClassLoader in a
+ * scenario where you absolutely need a non-null ClassLoader reference: for
+ * example, for class path resource loading (but not necessarily for
+ * <code>Class.forName</code>, which accepts a <code>null</code> ClassLoader
+ * reference as well).
+ *
+ * @return the default ClassLoader (never <code>null</code>)
+ * @see java.lang.Thread#getContextClassLoader()
+ */
+ public static ClassLoader getClassLoader() {
+ return getClassLoader(ClassHelper.class);
+ }
+
+ /**
+ * Same as <code>Class.forName()</code>, except that it works for primitive
+ * types.
+ */
+ public static Class<?> forName(String name) throws ClassNotFoundException {
+ return forName(name, getClassLoader());
+ }
+
+ /**
+ * Replacement for <code>Class.forName()</code> that also returns Class
+ * instances for primitives (like "int") and array class names (like
+ * "String[]").
+ *
+ * @param name the name of the Class
+ * @param classLoader the class loader to use (may be <code>null</code>,
+ * which indicates the default class loader)
+ * @return Class instance for the supplied name
+ * @throws ClassNotFoundException if the class was not found
+ * @throws LinkageError if the class file could not be loaded
+ * @see Class#forName(String, boolean, ClassLoader)
+ */
+ public static Class<?> forName(String name, ClassLoader classLoader)
+ throws ClassNotFoundException, LinkageError {
+
+ Class<?> clazz = resolvePrimitiveClassName(name);
+ if (clazz != null) {
+ return clazz;
+ }
+
+ // "java.lang.String[]" style arrays
+ if (name.endsWith(ARRAY_SUFFIX)) {
+ String elementClassName = name.substring(0, name.length() - ARRAY_SUFFIX.length());
+ Class<?> elementClass = forName(elementClassName, classLoader);
+ return Array.newInstance(elementClass, 0).getClass();
+ }
+
+ // "[Ljava.lang.String;" style arrays
+ int internalArrayMarker = name.indexOf(INTERNAL_ARRAY_PREFIX);
+ if (internalArrayMarker != -1 && name.endsWith(";")) {
+ String elementClassName = null;
+ if (internalArrayMarker == 0) {
+ elementClassName = name
+ .substring(INTERNAL_ARRAY_PREFIX.length(), name.length() - 1);
+ } else if (name.startsWith("[")) {
+ elementClassName = name.substring(1);
+ }
+ Class<?> elementClass = forName(elementClassName, classLoader);
+ return Array.newInstance(elementClass, 0).getClass();
+ }
+
+ ClassLoader classLoaderToUse = classLoader;
+ if (classLoaderToUse == null) {
+ classLoaderToUse = getClassLoader();
+ }
+ return classLoaderToUse.loadClass(name);
+ }
+
+ /**
+ * Resolve the given class name as primitive class, if appropriate,
+ * according to the JVM's naming rules for primitive classes.
+ * <p>
+ * Also supports the JVM's internal class names for primitive arrays. Does
+ * <i>not</i> support the "[]" suffix notation for primitive arrays; this is
+ * only supported by {@link #forName}.
+ *
+ * @param name the name of the potentially primitive class
+ * @return the primitive class, or <code>null</code> if the name does not
+ * denote a primitive class or primitive array class
+ */
+ public static Class<?> resolvePrimitiveClassName(String name) {
+ Class<?> result = null;
+ // Most class names will be quite long, considering that they
+ // SHOULD sit in a package, so a length check is worthwhile.
+ if (name != null && name.length() <= 8) {
+ // Could be a primitive - likely.
+ result = (Class<?>) primitiveTypeNameMap.get(name);
+ }
+ return result;
+ }
+
+ /** Suffix for array class names: "[]" */
+ public static final String ARRAY_SUFFIX = "[]";
+ /** Prefix for internal array class names: "[L" */
+ private static final String INTERNAL_ARRAY_PREFIX = "[L";
+
+ /**
+ * Map with primitive type name as key and corresponding primitive type as
+ * value, for example: "int" -> "int.class".
+ */
+ private static final Map<String,Class<?>> primitiveTypeNameMap = new HashMap<String, Class<?>>(16);
+
+ /**
+ * Map with primitive wrapper type as key and corresponding primitive type
+ * as value, for example: Integer.class -> int.class.
+ */
+ private static final Map<Class<?>,Class<?>> primitiveWrapperTypeMap = new HashMap<Class<?>, Class<?>>(8);
+
+ static {
+ primitiveWrapperTypeMap.put(Boolean.class, boolean.class);
+ primitiveWrapperTypeMap.put(Byte.class, byte.class);
+ primitiveWrapperTypeMap.put(Character.class, char.class);
+ primitiveWrapperTypeMap.put(Double.class, double.class);
+ primitiveWrapperTypeMap.put(Float.class, float.class);
+ primitiveWrapperTypeMap.put(Integer.class, int.class);
+ primitiveWrapperTypeMap.put(Long.class, long.class);
+ primitiveWrapperTypeMap.put(Short.class, short.class);
+
+ Set<Class<?>> primitiveTypeNames = new HashSet<Class<?>>(16);
+ primitiveTypeNames.addAll(primitiveWrapperTypeMap.values());
+ primitiveTypeNames.addAll(Arrays
+ .asList(new Class<?>[] { boolean[].class, byte[].class, char[].class, double[].class,
+ float[].class, int[].class, long[].class, short[].class }));
+ for (Iterator<Class<?>> it = primitiveTypeNames.iterator(); it.hasNext();) {
+ Class<?> primitiveClass = (Class<?>) it.next();
+ primitiveTypeNameMap.put(primitiveClass.getName(), primitiveClass);
+ }
+ }
+
+ public static String toShortString(Object obj){
+ if(obj == null){
+ return "null";
+ }
+ return obj.getClass().getSimpleName() + "@" + System.identityHashCode(obj);
+
+ }
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/utils/CollectionUtils.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/utils/CollectionUtils.java
new file mode 100644
index 0000000..d3f9234
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/utils/CollectionUtils.java
@@ -0,0 +1,188 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.utils;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class CollectionUtils {
+
+ @SuppressWarnings({ "unchecked", "rawtypes" })
+ public static <T> List<T> sort(List<T> list) {
+ if (list != null && list.size() > 0) {
+ Collections.sort((List)list);
+ }
+ return list;
+ }
+
+ private static final Comparator<String> SIMPLE_NAME_COMPARATOR = new Comparator<String>() {
+ public int compare(String s1, String s2) {
+ if (s1 == null && s2 == null) {
+ return 0;
+ }
+ if (s1 == null) {
+ return -1;
+ }
+ if (s2 == null) {
+ return 1;
+ }
+ int i1 = s1.lastIndexOf('.');
+ if (i1 >= 0) {
+ s1 = s1.substring(i1 + 1);
+ }
+ int i2 = s2.lastIndexOf('.');
+ if (i2 >= 0) {
+ s2 = s2.substring(i2 + 1);
+ }
+ return s1.compareToIgnoreCase(s2);
+ }
+ };
+
+ public static List<String> sortSimpleName(List<String> list) {
+ if (list != null && list.size() > 0) {
+ Collections.sort(list, SIMPLE_NAME_COMPARATOR);
+ }
+ return list;
+ }
+
+ public static Map<String, Map<String, String>> splitAll(Map<String, List<String>> list, String separator) {
+ if (list == null) {
+ return null;
+ }
+ Map<String, Map<String, String>> result = new HashMap<String, Map<String, String>>();
+ for (Map.Entry<String, List<String>> entry : list.entrySet()) {
+ result.put(entry.getKey(), split(entry.getValue(), separator));
+ }
+ return result;
+ }
+
+ public static Map<String, List<String>> joinAll(Map<String, Map<String, String>> map, String separator) {
+ if (map == null) {
+ return null;
+ }
+ Map<String, List<String>> result = new HashMap<String, List<String>>();
+ for (Map.Entry<String, Map<String, String>> entry : map.entrySet()) {
+ result.put(entry.getKey(), join(entry.getValue(), separator));
+ }
+ return result;
+ }
+
+ public static Map<String, String> split(List<String> list, String separator) {
+ if (list == null) {
+ return null;
+ }
+ Map<String, String> map = new HashMap<String, String>();
+ if (list == null || list.size() == 0) {
+ return map;
+ }
+ for (String item : list) {
+ int index = item.indexOf(separator);
+ if (index == -1) {
+ map.put(item, "");
+ } else {
+ map.put(item.substring(0, index), item.substring(index + 1));
+ }
+ }
+ return map;
+ }
+
+ public static List<String> join(Map<String, String> map, String separator) {
+ if (map == null) {
+ return null;
+ }
+ List<String> list = new ArrayList<String>();
+ if (map == null || map.size() == 0) {
+ return list;
+ }
+ for (Map.Entry<String, String> entry : map.entrySet()) {
+ String key = entry.getKey();
+ String value = entry.getValue();
+ if (value == null || value.length() == 0) {
+ list.add(key);
+ } else {
+ list.add(key + separator + value);
+ }
+ }
+ return list;
+ }
+
+ public static boolean mapEquals(Map<?, ?> map1, Map<?, ?> map2) {
+ if (map1 == null && map2 == null) {
+ return true;
+ }
+ if (map1 == null || map2 == null) {
+ return false;
+ }
+ if (map1.size() != map2.size()) {
+ return false;
+ }
+ for (Map.Entry<?, ?> entry : map1.entrySet()) {
+ Object key = entry.getKey();
+ Object value1 = entry.getValue();
+ Object value2 = map2.get(key);
+ if (! objectEquals(value1, value2)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private static boolean objectEquals(Object obj1, Object obj2) {
+ if (obj1 == null && obj2 == null) {
+ return true;
+ }
+ if (obj1 == null || obj2 == null) {
+ return false;
+ }
+ return obj1.equals(obj2);
+ }
+
+ public static Map<String, String> toStringMap(String... pairs) {
+ Map<String, String> parameters = new HashMap<String, String>();
+ if (pairs.length > 0) {
+ if (pairs.length % 2 != 0) {
+ throw new IllegalArgumentException("pairs must be even.");
+ }
+ for (int i = 0; i < pairs.length; i = i + 2) {
+ parameters.put(pairs[i], pairs[i + 1]);
+ }
+ }
+ return parameters;
+ }
+
+ @SuppressWarnings("unchecked")
+ public static <K, V> Map<K, V> toMap(Object ... pairs) {
+ Map<K, V> ret = new HashMap<K, V>();
+ if (pairs == null || pairs.length == 0) return ret;
+
+ if (pairs.length % 2 != 0) {
+ throw new IllegalArgumentException("Map pairs can not be odd number.");
+ }
+ int len = pairs.length / 2;
+ for (int i = 0; i < len; i ++) {
+ ret.put((K) pairs[2 * i], (V) pairs[2 * i + 1]);
+ }
+ return ret;
+ }
+
+ private CollectionUtils() {
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/utils/CompatibleTypeUtils.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/utils/CompatibleTypeUtils.java
new file mode 100644
index 0000000..70f1c6c
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/utils/CompatibleTypeUtils.java
@@ -0,0 +1,173 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.utils;
+
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * @author ding.lid
+ */
+public class CompatibleTypeUtils {
+ private CompatibleTypeUtils() {
+ }
+
+ private static final List<Class<?>> INETGER_LIKE_TYPES;
+ private static final List<Class<?>> FLOAT_LIKE_TYPES;
+ static {
+ List<Class<?>> list = new ArrayList<Class<?>>();
+ list.add(byte.class);
+ list.add(Byte.class);
+ list.add(short.class);
+ list.add(Short.class);
+ list.add(int.class);
+ list.add(Integer.class);
+ list.add(long.class);
+ list.add(Long.class);
+
+ INETGER_LIKE_TYPES = Collections.unmodifiableList(list);
+
+ list = new ArrayList<Class<?>>();
+ list.add(float.class);
+ list.add(Float.class);
+ list.add(double.class);
+ list.add(Double.class);
+
+ FLOAT_LIKE_TYPES = Collections.unmodifiableList(list);
+ };
+
+ public static boolean isIntegerLikeType(Class<?> clazz) {
+ return INETGER_LIKE_TYPES.contains(clazz);
+ }
+
+ public static boolean isFloatLikeType(Class<?> clazz) {
+ return FLOAT_LIKE_TYPES.contains(clazz);
+ }
+
+ public static Object convertIntegerLikeType(Object value, Class<?> clazz) {
+ if(value == null || clazz == null || value.getClass().equals(clazz)) return value;
+
+ Class<?> valueClass = value.getClass();
+ if (!INETGER_LIKE_TYPES.contains(valueClass)) {
+ String msg = String.format("Input value(type: %s) is not a integer like type!", valueClass);
+ throw new IllegalArgumentException(msg);
+ }
+ if (!INETGER_LIKE_TYPES.contains(clazz)) {
+ String msg = String.format("Destination type(%s) is not a integer like type!", clazz);
+ throw new IllegalArgumentException(msg);
+ }
+
+ Number n = (Number) value;
+ if (clazz.equals(INETGER_LIKE_TYPES.get(0))
+ || clazz.equals(INETGER_LIKE_TYPES.get(1))) {
+ if(n.longValue() > Byte.MAX_VALUE || n.longValue() < Byte.MIN_VALUE ) {
+ throw new IllegalStateException("Overflow/Underflow when convert value to compatible type byte!");
+ }
+ return n.byteValue();
+ } else if (clazz.equals(INETGER_LIKE_TYPES.get(2))
+ || clazz.equals(INETGER_LIKE_TYPES.get(3))) {
+ if(n.longValue() > Short.MAX_VALUE || n.longValue() < Short.MIN_VALUE ) {
+ throw new IllegalStateException("Overflow/Underflow when convert value to compatible type short!");
+ }
+ return n.shortValue();
+ } else if (clazz.equals(INETGER_LIKE_TYPES.get(4))
+ || clazz.equals(INETGER_LIKE_TYPES.get(5))) {
+ if(n.longValue() > Integer.MAX_VALUE || n.longValue() < Integer.MIN_VALUE ) {
+ throw new IllegalStateException("Overflow/Underflow when convert value to compatible type int!");
+ }
+ return n.intValue();
+ }
+ return n.longValue();
+ }
+
+ public static Object convertFloatLikeType(Object value, Class<?> clazz) {
+ if(value ==null || clazz == null || value.getClass().equals(clazz)) return value;
+
+ Class<?> valueClass = value.getClass();
+ if (!FLOAT_LIKE_TYPES.contains(valueClass)) {
+ String msg = String.format("Input value(type: %s) is not a float like type!", valueClass);
+ throw new IllegalArgumentException(msg);
+ }
+ if (!FLOAT_LIKE_TYPES.contains(clazz)) {
+ String msg = String.format("Destination type(%s) is not a float like type!", clazz);
+ throw new IllegalArgumentException(msg);
+ }
+
+ Number n = (Number) value;
+ if (clazz.equals(FLOAT_LIKE_TYPES.get(0))
+ || clazz.equals(FLOAT_LIKE_TYPES.get(1))) {
+ return n.floatValue();
+ }
+
+ return n.doubleValue();
+ }
+
+ public static boolean needCompatibleTypeConvert(Object value, Class<?> destinationType) {
+ return value != null && destinationType != null && ! value.getClass().equals(destinationType);
+ }
+
+ public static boolean isCharType(Class<?> clazz) {
+ return char.class.equals(clazz) || Character.class.equals(clazz);
+ }
+
+ public static Character covert2Char(String value) {
+ String s = (String) value;
+ if(s.length() != 1) {
+ String msg = String.format("CAN NOT convert String(%s) to char!" +
+ " when convert String to char, the String MUST only 1 char.", s);
+ throw new IllegalArgumentException(msg);
+ }
+ return s.charAt(0);
+ }
+
+ 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<?> destinationType) {
+ if(! needCompatibleTypeConvert(value, destinationType)) {
+ return value;
+ }
+ if (isCharType(destinationType) && value instanceof String) {
+ return CompatibleTypeUtils.covert2Char((String) value);
+ } else if(destinationType.isEnum() && value instanceof String) {
+ return Enum.valueOf((Class<Enum>)destinationType, (String) value);
+ } else if(destinationType == Date.class && value instanceof String) {
+ 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(CompatibleTypeUtils.isIntegerLikeType(destinationType)) {
+ return CompatibleTypeUtils.convertIntegerLikeType(value, destinationType);
+ } else if(CompatibleTypeUtils.isFloatLikeType(destinationType)) {
+ return CompatibleTypeUtils.convertFloatLikeType(value, destinationType);
+ }
+ 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..8e689d4
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/utils/ConfigUtils.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.common.utils;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.ExtensionLoader;
+
+public class ConfigUtils {
+
+ public static boolean isNotEmpty(String value) {
+ return ! isEmpty(value);
+ }
+
+ public static boolean isEmpty(String value) {
+ return value == null || value.length() == 0
+ || "null".equalsIgnoreCase(value)
+ || "false".equalsIgnoreCase(value)
+ || "N/A".equalsIgnoreCase(value);
+ }
+
+ public static boolean isDefault(String value) {
+ return "true".equalsIgnoreCase(value)
+ || "default".equalsIgnoreCase(value);
+ }
+
+ public static List<String> mergeValues(Class<?> type, String cfg, List<String> def) {
+ List<String> defaults = new ArrayList<String>();
+ if (def != null) {
+ for (String name : def) {
+ if (ExtensionLoader.getExtensionLoader(type).hasExtension(name)) {
+ defaults.add(name);
+ }
+ }
+ }
+ List<String> names = new ArrayList<String>();
+ String[] configs = cfg == null ? new String[0] : Constants.COMMA_SPLIT_PATTERN.split(cfg);
+ for (String config : configs) {
+ if(config != null && config.length() > 0) {
+ String[] fs = Constants.COMMA_SPLIT_PATTERN.split(config);
+ names.addAll(Arrays.asList(fs));
+ }
+ }
+ if (! names.contains(Constants.REMOVE_VALUE_PREFIX + Constants.DEFAULT_KEY)) {
+ int i = names.indexOf(Constants.DEFAULT_KEY);
+ if (i > 0) {
+ names.addAll(i, defaults);
+ } else {
+ names.addAll(defaults);
+ }
+ names.remove(Constants.DEFAULT_KEY);
+ }
+ 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 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..f997f8f
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/utils/DubboAppender.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.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 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..b6110f8
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/utils/ExecutorUtil.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.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 ,timeout);
+ }
+ }
+ 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 ,timeout);
+ }
+ }
+
+ private static void newThreadToCloseExecutor(final ExecutorService es, final int timeout) {
+ if (!isShutdown(es)) {
+ shutdownExecutor.execute(new Runnable() {
+ public void run() {
+ try {
+ es.shutdownNow();
+ } catch (Throwable e) {
+ logger.warn(e.getMessage(), e);
+ }
+ try {
+ es.awaitTermination(timeout, TimeUnit.MILLISECONDS);
+ } catch (InterruptedException ex) {
+ Thread.currentThread().interrupt();
+ }
+ }
+ });
+ }
+ }
+}
\ 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..881ed0f
--- /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 file file.
+ * @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 file file.
+ * @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..83f065f
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/utils/LogUtil.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.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 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 <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 100644
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..f8a7400
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/utils/NetUtils.java
@@ -0,0 +1,237 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.util.Enumeration;
+import java.util.Map;
+import java.util.Random;
+import java.util.regex.Pattern;
+
+import com.alibaba.dubbo.common.logger.Logger;
+import com.alibaba.dubbo.common.logger.LoggerFactory;
+
+/**
+ * IP and Port Helper for RPC,
+ *
+ * @author shawn.qianx
+ */
+
+public class NetUtils {
+
+ private static final Logger logger = LoggerFactory.getLogger(NetUtils.class);
+
+ public static final String LOCALHOST = "127.0.0.1";
+
+ public static final String ANYHOST = "0.0.0.0";
+
+ private static final int RND_PORT_START = 30000;
+
+ private static final int RND_PORT_RANGE = 10000;
+
+ private static final Random RANDOM = new Random(System.currentTimeMillis());
+
+ public static int getRandomPort() {
+ return RND_PORT_START + RANDOM.nextInt(RND_PORT_RANGE);
+ }
+
+ public static int getAvailablePort() {
+ ServerSocket ss = null;
+ try {
+ ss = new ServerSocket();
+ ss.bind(null);
+ return ss.getLocalPort();
+ } catch (IOException e) {
+ return getRandomPort();
+ } finally {
+ if (ss != null) {
+ try {
+ ss.close();
+ } catch (IOException e) {
+ }
+ }
+ }
+ }
+
+ private static final int MIN_PORT = 0;
+
+ private static final int MAX_PORT = 65535;
+
+ public static boolean isInvalidPort(int port){
+ return port > MIN_PORT || port <= MAX_PORT;
+ }
+
+ private static final Pattern ADDRESS_PATTERN = Pattern.compile("^\\d{1,3}(\\.\\d{1,3}){3}\\:\\d{1,5}$");
+
+ public static boolean isValidAddress(String address){
+ return ADDRESS_PATTERN.matcher(address).matches();
+ }
+
+ private static final Pattern LOCAL_IP_PATTERN = Pattern.compile("127(\\.\\d{1,3}){3}$");
+ public static boolean 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 ? null : address.getHostAddress();
+ }
+
+ public static String filterLocalHost(String host) {
+ if (NetUtils.isInvalidLocalHost(host)) {
+ return NetUtils.getLocalHost();
+ }
+ return host;
+ }
+
+ private static volatile InetAddress LOCAL_ADDRESS = null;
+
+ /**
+ * 遍历本地网卡,返回第一个合理的IP。
+ *
+ * @return 本地网卡IP
+ */
+ public static InetAddress getLocalAddress() {
+ if (LOCAL_ADDRESS != null)
+ return LOCAL_ADDRESS;
+ InetAddress localAddress = getLocalAddress0();
+ LOCAL_ADDRESS = localAddress;
+ return localAddress;
+ }
+
+ 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;
+ }
+
+ 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..65ff913
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/utils/PojoUtils.java
@@ -0,0 +1,452 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.InvocationHandler;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Proxy;
+import java.lang.reflect.Type;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * PojoUtils. Travel object deeply, and convert complex type to simple type.
+ * <p>
+ * Type below will be remained:
+ * <ul>
+ * <li> Primitive Type, also include <b>String</b>, <b>Number</b>(Integer, Long), <b>Date</b>
+ * <li> Array of Primitive Type
+ * <li> Collection, eg: List, Map, Set etc.
+ * </ul>
+ * <p>
+ * Other type will be covert to a map which contains the attributes and value pair of object.
+ *
+ * @author william.liangf
+ * @author ding.lid
+ */
+public class PojoUtils {
+
+ public static Object[] generalize(Object[] objs) {
+ Object[] dests = new Object[objs.length];
+ for (int i = 0; i < objs.length; i ++) {
+ dests[i] = generalize(objs[i]);
+ }
+ return dests;
+ }
+
+ public static Object[] realize(Object[] objs, Class<?>[] types) {
+ if (objs.length != types.length)
+ throw new IllegalArgumentException("args.length != types.length");
+ Object[] dests = new Object[objs.length];
+ for (int i = 0; i < objs.length; i ++) {
+ dests[i] = realize(objs[i], types[i]);
+ }
+ return dests;
+ }
+
+ public static Object generalize(Object pojo) {
+ return generalize(pojo, new HashMap<Integer, Object>());
+ }
+
+ @SuppressWarnings("unchecked")
+ private static Object generalize(Object pojo, Map<Integer, Object> history) {
+ if (pojo == null) {
+ return null;
+ }
+
+ if (pojo instanceof Enum<?>) {
+ return ((Enum<?>)pojo).name();
+ }
+ if (pojo.getClass().isArray()
+ && Enum.class.isAssignableFrom(
+ pojo.getClass().getComponentType())) {
+ int len = Array.getLength(pojo);
+ String[] values = new String[len];
+ for (int i = 0; i < len; i ++) {
+ values[i] = ((Enum<?>)Array.get(pojo, i)).name();
+ }
+ return values;
+ }
+
+ if (isBase(pojo.getClass())) {
+ return pojo;
+ }
+
+ Integer id = System.identityHashCode(pojo);
+ if (history.containsKey(id)) {
+ return history.get(id);
+ }
+ history.put(id, pojo);
+
+ if (pojo.getClass().isArray()) {
+ int len = Array.getLength(pojo);
+ Object[] dest = new Object[len];
+ for (int i = 0; i < len; i ++) {
+ Object obj = Array.get(pojo, i);
+ dest[i] = generalize(obj, history);
+ }
+ return dest;
+ }
+ if (pojo instanceof Collection<?>) {
+ Collection<Object> src = (Collection<Object>)pojo;
+ int len = src.size();
+ Collection<Object> dest = (pojo instanceof List<?>) ? new ArrayList<Object>(len) : new HashSet<Object>(len);
+ for (Object obj : src) {
+ dest.add(generalize(obj, history));
+ }
+ return dest;
+ }
+ if (pojo instanceof Map<?, ?>) {
+ Map<Object, Object> src = (Map<Object, Object>)pojo;
+ Map<Object, Object> tmp = new HashMap<Object, Object>(src.size());
+ tmp.putAll(src);
+ for (Map.Entry<Object, Object> obj : tmp.entrySet()) {
+ src.put(generalize(obj.getKey(), history), generalize(obj.getValue(), history));
+ }
+ return src;
+ }
+ Map<String, Object> map = new HashMap<String, Object>();
+ history.put(id, map);
+ map.put("class", pojo.getClass().getName());
+ for (Method method : pojo.getClass().getMethods()) {
+ if (Modifier.isPublic(method.getModifiers())
+ && method.getDeclaringClass() != Object.class
+ && method.getParameterTypes().length == 0) {
+ String name = method.getName();
+ try {
+ if (name.startsWith("get")) {
+ map.put(name.substring(3, 4).toLowerCase() + name.substring(4), generalize(method
+ .invoke(pojo, new Object[0]), history));
+ } else if (name.startsWith("is")) {
+ map.put(name.substring(2, 3).toLowerCase() + name.substring(3), generalize(method
+ .invoke(pojo, new Object[0]), history));
+ }
+ } catch (Exception e) {
+ throw new RuntimeException(e.getMessage(), e);
+ }
+ }
+ }
+ return map;
+ }
+
+ public static Object realize(Object pojo, Class<?> type) {
+ return realize(pojo, type, new HashMap<Integer, Object>());
+ }
+
+ public static Object realize(Object pojo, Class<?> type, Type genericType) {
+ return realize(pojo, type, genericType, new HashMap<Integer, Object>());
+ }
+
+ private static class PojoInvocationHandler implements InvocationHandler {
+
+ private Map<Object, Object> map;
+
+ public PojoInvocationHandler(Map<Object, Object> map) {
+ this.map = map;
+ }
+
+ @SuppressWarnings("unchecked")
+ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+ if (method.getDeclaringClass() == Object.class) {
+ return method.invoke(map, args);
+ }
+ String methodName = method.getName();
+ Object value = null;
+ if (methodName.length() > 3 && methodName.startsWith("get")) {
+ value = map.get(methodName.substring(3, 4).toLowerCase() + methodName.substring(4));
+ } else if (methodName.length() > 2 && methodName.startsWith("is")) {
+ value = map.get(methodName.substring(2, 3).toLowerCase() + methodName.substring(3));
+ } else {
+ value = map.get(methodName.substring(0, 1).toLowerCase() + methodName.substring(1));
+ }
+ if (value instanceof Map<?,?> && ! Map.class.isAssignableFrom(method.getReturnType())) {
+ value = realize((Map<String, Object>)value, method.getReturnType(), new HashMap<Integer, Object>());
+ }
+ return value;
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ private static Collection<Object> createCollection(Class<?> type, int len) {
+ if (type.isAssignableFrom(ArrayList.class)) {
+ return new ArrayList<Object>(len);
+ }
+ if (type.isAssignableFrom(HashSet.class)) {
+ return new HashSet<Object>(len);
+ }
+ if (! type.isInterface() && ! Modifier.isAbstract(type.getModifiers())) {
+ try {
+ return (Collection<Object>) type.newInstance();
+ } catch (Exception e) {
+ // ignore
+ }
+ }
+ return new ArrayList<Object>();
+ }
+
+ private static Object realize(Object pojo, Class<?> type, final Map<Integer, Object> history) {
+ return realize(pojo, type, null , history);
+ }
+
+ @SuppressWarnings({ "unchecked", "rawtypes" })
+ private static Object realize(Object pojo, Class<?> type, Type genericType, final Map<Integer, Object> history) {
+ if (pojo == null) {
+ return null;
+ }
+
+ if (type != null && type.isEnum()
+ && pojo.getClass() == String.class) {
+ return Enum.valueOf((Class<Enum>)type, (String)pojo);
+ }
+
+ if (isBase(pojo.getClass())
+ && ! (type != null && type.isArray()
+ && type.getComponentType().isEnum()
+ && pojo.getClass() == String[].class)) {
+ return CompatibleTypeUtils.compatibleTypeConvert(pojo, type);
+ }
+
+ Integer id = System.identityHashCode(pojo);
+ if (history.containsKey(id)) {
+ return history.get(id);
+ }
+ history.put(id, pojo);
+
+ if (pojo.getClass().isArray()) {
+ if (Collection.class.isAssignableFrom(type)) {
+ Class<?> ctype = pojo.getClass().getComponentType();
+ int len = Array.getLength(pojo);
+ Collection dest = createCollection(type, len);
+ for (int i = 0; i < len; i ++) {
+ Object obj = Array.get(pojo, i);
+ Object value = realize(obj, ctype, history);
+ dest.add(value);
+ }
+ return dest;
+ } else {
+ Class<?> ctype = (type != null && type.isArray() ? type.getComponentType() : pojo.getClass().getComponentType());
+ int len = Array.getLength(pojo);
+ Object dest = Array.newInstance(ctype, len);
+ for (int i = 0; i < len; i ++) {
+ Object obj = Array.get(pojo, i);
+ Object value = realize(obj, ctype, history);
+ Array.set(dest, i, value);
+ }
+ return dest;
+ }
+ }
+
+ if (pojo instanceof Collection<?>) {
+ if (type.isArray()) {
+ Class<?> ctype = type.getComponentType();
+ Collection<Object> src = (Collection<Object>)pojo;
+ int len = src.size();
+ Object dest = Array.newInstance(ctype, len);
+ int i = 0;
+ for (Object obj : src) {
+ Object value = realize(obj, ctype, history);
+ Array.set(dest, i, value);
+ i ++;
+ }
+ return dest;
+ } else {
+ Collection<Object> src = (Collection<Object>)pojo;
+ int len = src.size();
+ Collection<Object> dest = createCollection(type, len);
+ for (Object obj : src) {
+ Type keyType = getGenericClassByIndex(genericType, 0);
+ Class<?> keyClazz = obj.getClass() ;
+ if ( keyType instanceof Class){
+ keyClazz = (Class<?>)keyType;
+ }
+ Object value = realize(obj, keyClazz, keyType, history);
+ dest.add(value);
+ }
+ return dest;
+ }
+ }
+
+ if (pojo instanceof Map<?, ?> && type != null) {
+ Object className = ((Map<Object, Object>)pojo).get("class");
+ if (className instanceof String && ! Map.class.isAssignableFrom(type)) {
+ try {
+ type = ClassHelper.forName((String)className);
+ } catch (ClassNotFoundException e) {
+ // ignore
+ }
+ }
+ Map<Object, Object> map ;
+ // 返回值类型不是方法签名类型的子集 并且 不是接口类型
+ if (! type.isInterface()
+ && ! type.isAssignableFrom(pojo.getClass())){
+ try {
+ map = (Map<Object,Object>)type.newInstance();
+ } catch (Exception e) {
+ //ignore error
+ map = (Map<Object, Object>)pojo;
+ }
+ }else {
+ map = (Map<Object, Object>)pojo;
+ }
+
+ if (Map.class.isAssignableFrom(type) || type == Object.class) {
+ final Map<Object, Object> tmp = new HashMap<Object, Object>(map.size());
+ tmp.putAll(map);
+ for (Map.Entry<Object, Object> entry : tmp.entrySet()) {
+ Type keyType = getGenericClassByIndex(genericType, 0);
+ Type valueType = getGenericClassByIndex(genericType, 1);
+ Class<?> keyClazz = entry.getKey().getClass();
+ if ( keyType instanceof Class){
+ keyClazz = (Class<?>)keyType;
+ }
+ Class<?> valueClazz = entry.getValue().getClass() ;
+ if ( valueType instanceof Class){
+ valueClazz = (Class<?>)valueType;
+ }
+
+ Object key = realize(entry.getKey(), keyClazz, keyType, history);
+ Object value = realize(entry.getValue(), valueClazz, valueType, history);
+ map.put(key, value);
+ }
+ return map;
+ } else if (type.isInterface()) {
+ Object dest = Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), new Class<?>[]{type}, new PojoInvocationHandler(map));
+ history.put(id, dest);
+ return dest;
+ } else {
+ Object dest = newInstance(type);
+ history.put(id, dest);
+ for (Map.Entry<Object, Object> entry : map.entrySet()) {
+ Object key = entry.getKey();
+ if (key instanceof String) {
+ String name = (String) key;
+ Object value = entry.getValue();
+ if (value != null) {
+ Method method = getSetterMethod(dest.getClass(), name, value.getClass());
+ if (method != null) {
+ if (! method.isAccessible())
+ method.setAccessible(true);
+ Type ptype = method.getGenericParameterTypes()[0];
+ value = realize(value, method.getParameterTypes()[0], ptype, history);
+ try {
+ method.invoke(dest, value);
+ } catch (Exception e) {
+ throw new RuntimeException("Failed to set pojo " + dest.getClass().getSimpleName() + " property " + name + " value " + value + ", cause: " + e.getMessage(), e);
+ }
+ }
+ }
+ }
+ }
+ return dest;
+ }
+ }
+ return pojo;
+ }
+
+ /**
+ * 获取范型的类型
+ * @param genericType
+ * @param index
+ * @return List<Person> 返回Person.class ,Map<String,Person> index=0 返回String.class index=1 返回Person.class
+ */
+ private static Type getGenericClassByIndex(Type genericType, int index){
+ Type clazz = null ;
+ //范型参数转换
+ if (genericType instanceof ParameterizedType){
+ ParameterizedType t = (ParameterizedType)genericType;
+ Type[] types = t.getActualTypeArguments();
+ clazz = types[index];
+ }
+ return clazz;
+ }
+
+ private static Object newInstance(Class<?> cls) {
+ try {
+ return cls.newInstance();
+ } catch (Throwable t) {
+ try {
+ Constructor<?>[] constructors = cls.getConstructors();
+ if (constructors != null && constructors.length > 0) {
+ throw new RuntimeException("Illegal constructor: " + cls.getName());
+ }
+ Constructor<?> constructor = constructors[0];
+ if (constructor.getParameterTypes().length > 0) {
+ for (Constructor<?> c : constructors) {
+ if (c.getParameterTypes().length <
+ constructor.getParameterTypes().length) {
+ constructor = c;
+ if (constructor.getParameterTypes().length == 0) {
+ break;
+ }
+ }
+ }
+ }
+ return constructor.newInstance(new Object[constructor.getParameterTypes().length]);
+ } catch (InstantiationException e) {
+ throw new RuntimeException(e.getMessage(), e);
+ } catch (IllegalAccessException e) {
+ throw new RuntimeException(e.getMessage(), e);
+ } catch (InvocationTargetException e) {
+ throw new RuntimeException(e.getMessage(), e);
+ }
+ }
+ }
+
+ private static Method getSetterMethod(Class<?> cls, String property, Class<?> valueCls) {
+ String name = "set" + property.substring(0, 1).toUpperCase() + property.substring(1);
+ try {
+ return cls.getMethod(name, valueCls);
+ } catch (NoSuchMethodException e) {
+ for (Method method : cls.getMethods()) {
+ if (Modifier.isPublic(method.getModifiers())
+ && method.getDeclaringClass() != Object.class
+ && method.getParameterTypes().length == 1
+ && method.getName().equals(name)) {
+ return method;
+ }
+ }
+ }
+ return null;
+ }
+
+ public static boolean isPojo(Class<?> cls) {
+ return ! isBase(cls)
+ && ! Collection.class.isAssignableFrom(cls)
+ && ! Map.class.isAssignableFrom(cls);
+ }
+
+ private static boolean isBase(Class<?> cls) {
+ if (cls.isArray()) {
+ return isPrimitive(cls.getComponentType());
+ }
+ return isPrimitive(cls);
+ }
+
+ private static boolean isPrimitive(Class<?> cls) {
+ return cls.isPrimitive() || cls == String.class || cls == Boolean.class || cls == Character.class
+ || Number.class.isAssignableFrom(cls) || Date.class.isAssignableFrom(cls);
+ }
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/utils/Pool.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/utils/Pool.java
new file mode 100644
index 0000000..01dd5a8
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/utils/Pool.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.utils;
+
+/**
+ * Pool.
+ *
+ * @author qian.lei
+ */
+
+public interface Pool<T>
+{
+ /**
+ * clear.
+ */
+ void clear() throws Exception;
+
+ /**
+ * close pool.
+ *
+ * @throws Exception.
+ */
+ void close() throws Exception;
+
+ /**
+ * borrow.
+ *
+ * @return object.
+ */
+ T borrowObject() throws Exception;
+
+ /**
+ * borrow.
+ *
+ * @param timeout timeout.
+ * @return object.
+ */
+ T borrowObject(long timeout) throws Exception;
+
+ /**
+ * return object.
+ *
+ * @param obj object.
+ */
+ void returnObject(T obj) throws Exception;
+
+ /**
+ * get factory.
+ *
+ * @return Factory instance.
+ */
+ Factory<T> getFactory();
+
+ /**
+ * get idle number.
+ *
+ * @return idle number.
+ */
+ int getIdleNum();
+
+ /**
+ * get active number.
+ *
+ * @return active number.
+ */
+ int getActiveNum();
+
+ /**
+ * pool factory.
+ */
+ public interface Factory<T>
+ {
+ /**
+ * make object.
+ *
+ * @return object.
+ * @throws Exception.
+ */
+ T makeObject() throws Exception;
+
+ /**
+ * destroy object.
+ *
+ * @param obj object.
+ * @throws Exception.
+ */
+ void destroyObject(T obj) throws Exception;
+
+ /**
+ * activate object.
+ *
+ * @param obj object.
+ */
+ void activateObject(T obj);
+
+ /**
+ * passivate object.
+ *
+ * @param obj object.
+ */
+ void passivateObject(T obj);
+
+// /**
+// * validate object.
+// *
+// * @param obj object.
+// * @return valid or not.
+// */
+// boolean validateObject(T obj);
+ }
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/utils/Reference.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/utils/Reference.java
new file mode 100644
index 0000000..6e4c7fd
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/utils/Reference.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.utils;
+
+/**
+ * TODO Comment of Reference
+ *
+ * @author william.liangf
+ */
+public class Reference<T> {
+
+ private T value;
+
+ public void set(T value) {
+ this.value = value;
+ }
+
+ public T get() {
+ return value;
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/utils/ReflectUtils.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/utils/ReflectUtils.java
new file mode 100644
index 0000000..84ac7d4
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/utils/ReflectUtils.java
@@ -0,0 +1,790 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.Constructor;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.net.URL;
+import java.security.CodeSource;
+import java.security.ProtectionDomain;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import javassist.CtClass;
+import javassist.CtConstructor;
+import javassist.CtMethod;
+import javassist.NotFoundException;
+
+/**
+ * ReflectUtils
+ *
+ * @author qian.lei
+ */
+public final class ReflectUtils {
+
+ /**
+ * void(V).
+ */
+ public static final char JVM_VOID = 'V';
+
+ /**
+ * boolean(Z).
+ */
+ public static final char JVM_BOOLEAN = 'Z';
+
+ /**
+ * byte(B).
+ */
+ public static final char JVM_BYTE = 'B';
+
+ /**
+ * char(C).
+ */
+ public static final char JVM_CHAR = 'C';
+
+ /**
+ * double(D).
+ */
+ public static final char JVM_DOUBLE = 'D';
+
+ /**
+ * float(F).
+ */
+ public static final char JVM_FLOAT = 'F';
+
+ /**
+ * int(I).
+ */
+ public static final char JVM_INT = 'I';
+
+ /**
+ * long(J).
+ */
+ public static final char JVM_LONG = 'J';
+
+ /**
+ * short(S).
+ */
+ public static final char JVM_SHORT = 'S';
+
+ public static final Class<?>[] EMPTY_CLASS_ARRAY = new Class<?>[0];
+
+ public static final String JAVA_IDENT_REGEX = "(?:[_$a-zA-Z][_$a-zA-Z0-9]*)";
+
+ public static final String JAVA_NAME_REGEX = "(?:" + JAVA_IDENT_REGEX + "(?:\\." + JAVA_IDENT_REGEX + ")*)";
+
+ public static final String CLASS_DESC = "(?:L" + JAVA_IDENT_REGEX + "(?:\\/" + JAVA_IDENT_REGEX + ")*;)";
+
+ public static final String ARRAY_DESC = "(?:\\[+(?:(?:[VZBCDFIJS])|" + CLASS_DESC + "))";
+
+ public static final String DESC_REGEX = "(?:(?:[VZBCDFIJS])|" + CLASS_DESC + "|" + ARRAY_DESC + ")";
+
+ public static final Pattern DESC_PATTERN = Pattern.compile(DESC_REGEX);
+
+ public static final String METHOD_DESC_REGEX = "(?:("+JAVA_IDENT_REGEX+")?\\(("+DESC_REGEX+"*)\\)("+DESC_REGEX+")?)";
+
+ public static final Pattern METHOD_DESC_PATTERN = Pattern.compile(METHOD_DESC_REGEX);
+
+ public static final Pattern GETTER_METHOD_DESC_PATTERN = Pattern.compile("get([A-Z][_a-zA-Z0-9]*)\\(\\)(" + DESC_REGEX + ")");
+
+ public static final Pattern SETTER_METHOD_DESC_PATTERN = Pattern.compile("set([A-Z][_a-zA-Z0-9]*)\\((" + DESC_REGEX + ")\\)V");
+
+ public static final Pattern IS_HAS_CAN_METHOD_DESC_PATTERN = Pattern.compile("(?:is|has|can)([A-Z][_a-zA-Z0-9]*)\\(\\)Z");
+
+ private static final ConcurrentMap<String, Class<?>> DESC_CLASS_CACHE = new ConcurrentHashMap<String, Class<?>>();
+
+ /**
+ * is compatible.
+ *
+ * @param c class.
+ * @param o instance.
+ * @return compatible or not.
+ */
+ public static boolean isCompatible(Class<?> c, Object o)
+ {
+ boolean pt = c.isPrimitive();
+ if( o == null )
+ return !pt;
+
+ if( pt )
+ {
+ if( c == int.class )
+ c = Integer.class;
+ else if( c == boolean.class )
+ c = Boolean.class;
+ else if( c == long.class )
+ c = Long.class;
+ else if( c == float.class )
+ c = Float.class;
+ else if( c == double.class )
+ c = Double.class;
+ else if( c == char.class )
+ c = Character.class;
+ else if( c == byte.class )
+ c = Byte.class;
+ else if( c == short.class )
+ c = Short.class;
+ }
+ if( c == o.getClass() )
+ return true;
+ return c.isInstance(o);
+ }
+
+ /**
+ * is compatible.
+ *
+ * @param cs class array.
+ * @param os object array.
+ * @return compatible or not.
+ */
+ public static boolean isCompatible(Class<?>[] cs, Object[] os)
+ {
+ int len = cs.length;
+ if( len != os.length ) return false;
+ if( len == 0 ) return true;
+ for(int i=0;i<len;i++)
+ if( !isCompatible(cs[i], os[i]) ) return false;
+ return true;
+ }
+
+ public static String getCodeBase(Class<?> cls) {
+ if (cls == null)
+ return null;
+ ProtectionDomain domain = cls.getProtectionDomain();
+ if (domain == null)
+ return null;
+ CodeSource source = domain.getCodeSource();
+ if (source == null)
+ return null;
+ URL location = source.getLocation();
+ if (location == null)
+ return null;
+ return location.getFile();
+ }
+
+ /**
+ * get name.
+ * java.lang.Object[][].class => "java.lang.Object[][]"
+ *
+ * @param c class.
+ * @return name.
+ */
+ public static String getName(Class<?> c)
+ {
+ if( c.isArray() )
+ {
+ StringBuilder sb = new StringBuilder();
+ do
+ {
+ sb.append("[]");
+ c = c.getComponentType();
+ }
+ while( c.isArray() );
+
+ return c.getName() + sb.toString();
+ }
+ return c.getName();
+ }
+
+ /**
+ * get method name.
+ * "void do(int)", "void do()", "int do(java.lang.String,boolean)"
+ *
+ * @param m method.
+ * @return name.
+ */
+ public static String getName(final Method m)
+ {
+ StringBuilder ret = new StringBuilder();
+ ret.append(getName(m.getReturnType())).append(' ');
+ ret.append(m.getName()).append('(');
+ Class<?>[] parameterTypes = m.getParameterTypes();
+ for(int i=0;i<parameterTypes.length;i++)
+ {
+ if( i > 0 )
+ ret.append(',');
+ ret.append(getName(parameterTypes[i]));
+ }
+ ret.append(')');
+ return ret.toString();
+ }
+
+ public static String getSignature(String methodName, Class<?>[] parameterTypes) {
+ StringBuilder sb = new StringBuilder(methodName);
+ sb.append("(");
+ if (parameterTypes != null && parameterTypes.length > 0) {
+ boolean first = true;
+ for (Class<?> type : parameterTypes) {
+ if (first) {
+ first = false;
+ } else {
+ sb.append(",");
+ }
+ sb.append(type.getName());
+ }
+ }
+ sb.append(")");
+ return sb.toString();
+ }
+
+ /**
+ * get constructor name.
+ * "()", "(java.lang.String,int)"
+ *
+ * @param c constructor.
+ * @return name.
+ */
+ public static String getName(final Constructor<?> c)
+ {
+ StringBuilder ret = new StringBuilder("(");
+ Class<?>[] parameterTypes = c.getParameterTypes();
+ for(int i=0;i<parameterTypes.length;i++)
+ {
+ if( i > 0 )
+ ret.append(',');
+ ret.append(getName(parameterTypes[i]));
+ }
+ ret.append(')');
+ return ret.toString();
+ }
+
+ /**
+ * get class desc.
+ * boolean[].class => "[Z"
+ * Object.class => "Ljava/lang/Object;"
+ *
+ * @param c class.
+ * @return desc.
+ * @throws NotFoundException
+ */
+ public static String getDesc(Class<?> c)
+ {
+ StringBuilder ret = new StringBuilder();
+
+ while( c.isArray() )
+ {
+ ret.append('[');
+ c = c.getComponentType();
+ }
+
+ if( c.isPrimitive() )
+ {
+ String t = c.getName();
+ if( "void".equals(t) ) ret.append(JVM_VOID);
+ else if( "boolean".equals(t) ) ret.append(JVM_BOOLEAN);
+ else if( "byte".equals(t) ) ret.append(JVM_BYTE);
+ else if( "char".equals(t) ) ret.append(JVM_CHAR);
+ else if( "double".equals(t) ) ret.append(JVM_DOUBLE);
+ else if( "float".equals(t) ) ret.append(JVM_FLOAT);
+ else if( "int".equals(t) ) ret.append(JVM_INT);
+ else if( "long".equals(t) ) ret.append(JVM_LONG);
+ else if( "short".equals(t) ) ret.append(JVM_SHORT);
+ }
+ else
+ {
+ ret.append('L');
+ ret.append(c.getName().replace('.', '/'));
+ ret.append(';');
+ }
+ return ret.toString();
+ }
+
+ /**
+ * get class array desc.
+ * [int.class, boolean[].class, Object.class] => "I[ZLjava/lang/Object;"
+ *
+ * @param cs class array.
+ * @return desc.
+ * @throws NotFoundException
+ */
+ public static String getDesc(final Class<?>[] cs)
+ {
+ if( cs.length == 0 )
+ return "";
+
+ StringBuilder sb = new StringBuilder(64);
+ for( Class<?> c : cs )
+ sb.append(getDesc(c));
+ return sb.toString();
+ }
+
+ /**
+ * get method desc.
+ * int do(int arg1) => "do(I)I"
+ * void do(String arg1,boolean arg2) => "do(Ljava/lang/String;Z)V"
+ *
+ * @param m method.
+ * @return desc.
+ */
+ public static String getDesc(final Method m)
+ {
+ StringBuilder ret = new StringBuilder(m.getName()).append('(');
+ Class<?>[] parameterTypes = m.getParameterTypes();
+ for(int i=0;i<parameterTypes.length;i++)
+ ret.append(getDesc(parameterTypes[i]));
+ ret.append(')').append(getDesc(m.getReturnType()));
+ return ret.toString();
+ }
+
+ /**
+ * get constructor desc.
+ * "()V", "(Ljava/lang/String;I)V"
+ *
+ * @param c constructor.
+ * @return desc
+ */
+ public static String getDesc(final Constructor<?> c)
+ {
+ StringBuilder ret = new StringBuilder("(");
+ Class<?>[] parameterTypes = c.getParameterTypes();
+ for(int i=0;i<parameterTypes.length;i++)
+ ret.append(getDesc(parameterTypes[i]));
+ ret.append(')').append('V');
+ return ret.toString();
+ }
+
+ /**
+ * get method desc.
+ * "(I)I", "()V", "(Ljava/lang/String;Z)V"
+ *
+ * @param m method.
+ * @return desc.
+ */
+ public static String getDescWithoutMethodName(Method m)
+ {
+ StringBuilder ret = new StringBuilder();
+ ret.append('(');
+ Class<?>[] parameterTypes = m.getParameterTypes();
+ for(int i=0;i<parameterTypes.length;i++)
+ ret.append(getDesc(parameterTypes[i]));
+ ret.append(')').append(getDesc(m.getReturnType()));
+ return ret.toString();
+ }
+
+ /**
+ * get class desc.
+ * Object.class => "Ljava/lang/Object;"
+ * boolean[].class => "[Z"
+ *
+ * @param c class.
+ * @return desc.
+ * @throws NotFoundException
+ */
+ public static String getDesc(final CtClass c) throws NotFoundException
+ {
+ StringBuilder ret = new StringBuilder();
+ if( c.isArray() )
+ {
+ ret.append('[');
+ ret.append(getDesc(c.getComponentType()));
+ }
+ else if( c.isPrimitive() )
+ {
+ String t = c.getName();
+ if( "void".equals(t) ) ret.append(JVM_VOID);
+ else if( "boolean".equals(t) ) ret.append(JVM_BOOLEAN);
+ else if( "byte".equals(t) ) ret.append(JVM_BYTE);
+ else if( "char".equals(t) ) ret.append(JVM_CHAR);
+ else if( "double".equals(t) ) ret.append(JVM_DOUBLE);
+ else if( "float".equals(t) ) ret.append(JVM_FLOAT);
+ else if( "int".equals(t) ) ret.append(JVM_INT);
+ else if( "long".equals(t) ) ret.append(JVM_LONG);
+ else if( "short".equals(t) ) ret.append(JVM_SHORT);
+ }
+ else
+ {
+ ret.append('L');
+ ret.append(c.getName().replace('.','/'));
+ ret.append(';');
+ }
+ return ret.toString();
+ }
+
+ /**
+ * get method desc.
+ * "do(I)I", "do()V", "do(Ljava/lang/String;Z)V"
+ *
+ * @param m method.
+ * @return desc.
+ */
+ public static String getDesc(final CtMethod m) throws NotFoundException
+ {
+ StringBuilder ret = new StringBuilder(m.getName()).append('(');
+ CtClass[] parameterTypes = m.getParameterTypes();
+ for(int i=0;i<parameterTypes.length;i++)
+ ret.append(getDesc(parameterTypes[i]));
+ ret.append(')').append(getDesc(m.getReturnType()));
+ return ret.toString();
+ }
+
+ /**
+ * get constructor desc.
+ * "()V", "(Ljava/lang/String;I)V"
+ *
+ * @param c constructor.
+ * @return desc
+ */
+ public static String getDesc(final CtConstructor c) throws NotFoundException
+ {
+ StringBuilder ret = new StringBuilder("(");
+ CtClass[] parameterTypes = c.getParameterTypes();
+ for(int i=0;i<parameterTypes.length;i++)
+ ret.append(getDesc(parameterTypes[i]));
+ ret.append(')').append('V');
+ return ret.toString();
+ }
+
+ /**
+ * get method desc.
+ * "(I)I", "()V", "(Ljava/lang/String;Z)V".
+ *
+ * @param m method.
+ * @return desc.
+ */
+ public static String getDescWithoutMethodName(final CtMethod m) throws NotFoundException
+ {
+ StringBuilder ret = new StringBuilder();
+ ret.append('(');
+ CtClass[] parameterTypes = m.getParameterTypes();
+ for(int i=0;i<parameterTypes.length;i++)
+ ret.append(getDesc(parameterTypes[i]));
+ ret.append(')').append(getDesc(m.getReturnType()));
+ return ret.toString();
+ }
+
+ /**
+ * name to desc.
+ * java.util.Map[][] => "[[Ljava/util/Map;"
+ *
+ * @param name name.
+ * @return desc.
+ */
+ public static String name2desc(String name)
+ {
+ StringBuilder sb = new StringBuilder();
+ int c = 0,index = name.indexOf('[');
+ if( index > 0 )
+ {
+ c = ( name.length() - index ) / 2;
+ name = name.substring(0,index);
+ }
+ while( c-- > 0 ) sb.append("[");
+ if( "void".equals(name) ) sb.append(JVM_VOID);
+ else if( "boolean".equals(name) ) sb.append(JVM_BOOLEAN);
+ else if( "byte".equals(name) ) sb.append(JVM_BYTE);
+ else if( "char".equals(name) ) sb.append(JVM_CHAR);
+ else if( "double".equals(name) ) sb.append(JVM_DOUBLE);
+ else if( "float".equals(name) ) sb.append(JVM_FLOAT);
+ else if( "int".equals(name) ) sb.append(JVM_INT);
+ else if( "long".equals(name) ) sb.append(JVM_LONG);
+ else if( "short".equals(name) ) sb.append(JVM_SHORT);
+ else sb.append('L').append(name.replace('.', '/')).append(';');
+ return sb.toString();
+ }
+
+ /**
+ * desc to name.
+ * "[[I" => "int[][]"
+ *
+ * @param desc desc.
+ * @return name.
+ */
+ public static String desc2name(String desc)
+ {
+ StringBuilder sb = new StringBuilder();
+ int c = desc.lastIndexOf('[') + 1;
+ if( desc.length() == c+1 )
+ {
+ switch( desc.charAt(c) )
+ {
+ case JVM_VOID: { sb.append("void"); break; }
+ case JVM_BOOLEAN: { sb.append("boolean"); break; }
+ case JVM_BYTE: { sb.append("byte"); break; }
+ case JVM_CHAR: { sb.append("char"); break; }
+ case JVM_DOUBLE: { sb.append("double"); break; }
+ case JVM_FLOAT: { sb.append("float"); break; }
+ case JVM_INT: { sb.append("int"); break; }
+ case JVM_LONG: { sb.append("long"); break; }
+ case JVM_SHORT: { sb.append("short"); break; }
+ default:
+ throw new RuntimeException();
+ }
+ }
+ else
+ {
+ sb.append(desc.substring(c+1, desc.length()-1).replace('/','.'));
+ }
+ while( c-- > 0 ) sb.append("[]");
+ return sb.toString();
+ }
+
+ public static Class<?> forName(String name) {
+ try {
+ return name2class(name);
+ } catch (ClassNotFoundException e) {
+ throw new IllegalStateException("Not found class " + name + ", cause: " + e.getMessage(), e);
+ }
+ }
+
+ /**
+ * name to class.
+ * "boolean" => boolean.class
+ * "java.util.Map[][]" => java.util.Map[][].class
+ *
+ * @param name name.
+ * @return Class instance.
+ */
+ public static Class<?> name2class(String name) throws ClassNotFoundException
+ {
+ return name2class(ClassHelper.getClassLoader(), name);
+ }
+
+ /**
+ * name to class.
+ * "boolean" => boolean.class
+ * "java.util.Map[][]" => java.util.Map[][].class
+ *
+ * @param cl ClassLoader instance.
+ * @param name name.
+ * @return Class instance.
+ */
+ private static Class<?> name2class(ClassLoader cl, String name) throws ClassNotFoundException
+ {
+ int c = 0, index = name.indexOf('[');
+ if( index > 0 )
+ {
+ c = ( name.length() - index ) / 2;
+ name = name.substring(0, index);
+ }
+ if( c > 0 )
+ {
+ StringBuilder sb = new StringBuilder();
+ while( c-- > 0 )
+ sb.append("[");
+
+ if( "void".equals(name) ) sb.append(JVM_VOID);
+ else if( "boolean".equals(name) ) sb.append(JVM_BOOLEAN);
+ else if( "byte".equals(name) ) sb.append(JVM_BYTE);
+ else if( "char".equals(name) ) sb.append(JVM_CHAR);
+ else if( "double".equals(name) ) sb.append(JVM_DOUBLE);
+ else if( "float".equals(name) ) sb.append(JVM_FLOAT);
+ else if( "int".equals(name) ) sb.append(JVM_INT);
+ else if( "long".equals(name) ) sb.append(JVM_LONG);
+ else if( "short".equals(name) ) sb.append(JVM_SHORT);
+ else sb.append('L').append(name).append(';'); // "java.lang.Object" ==> "Ljava.lang.Object;"
+ name = sb.toString();
+ }
+ else
+ {
+ if( "void".equals(name) ) return void.class;
+ else if( "boolean".equals(name) ) return boolean.class;
+ else if( "byte".equals(name) ) return byte.class;
+ else if( "char".equals(name) ) return char.class;
+ else if( "double".equals(name) ) return double.class;
+ else if( "float".equals(name) ) return float.class;
+ else if( "int".equals(name) ) return int.class;
+ else if( "long".equals(name) ) return long.class;
+ else if( "short".equals(name) ) return short.class;
+ }
+
+ if( cl == null )
+ cl = ClassHelper.getClassLoader();
+ return Class.forName(name, true, cl);
+ }
+
+ /**
+ * desc to class.
+ * "[Z" => boolean[].class
+ * "[[Ljava/util/Map;" => java.util.Map[][].class
+ *
+ * @param desc desc.
+ * @return Class instance.
+ * @throws ClassNotFoundException
+ */
+ public static Class<?> desc2class(String desc) throws ClassNotFoundException
+ {
+ return desc2class(ClassHelper.getClassLoader(), desc);
+ }
+
+ /**
+ * desc to class.
+ * "[Z" => boolean[].class
+ * "[[Ljava/util/Map;" => java.util.Map[][].class
+ *
+ * @param cl ClassLoader instance.
+ * @param desc desc.
+ * @return Class instance.
+ * @throws ClassNotFoundException
+ */
+ private static Class<?> desc2class(ClassLoader cl, String desc) throws ClassNotFoundException
+ {
+ switch( desc.charAt(0) )
+ {
+ case JVM_VOID: return void.class;
+ case JVM_BOOLEAN: return boolean.class;
+ case JVM_BYTE: return byte.class;
+ case JVM_CHAR: return char.class;
+ case JVM_DOUBLE: return double.class;
+ case JVM_FLOAT: return float.class;
+ case JVM_INT: return int.class;
+ case JVM_LONG: return long.class;
+ case JVM_SHORT: return short.class;
+ case 'L':
+ desc = desc.substring(1, desc.length()-1).replace('/', '.'); // "Ljava/lang/Object;" ==> "java.lang.Object"
+ break;
+ case '[':
+ desc = desc.replace('/', '.'); // "[[Ljava/lang/Object;" ==> "[[Ljava.lang.Object;"
+ break;
+ default:
+ throw new ClassNotFoundException("Class not found: " + desc);
+ }
+
+ if( cl == null )
+ cl = ClassHelper.getClassLoader();
+ Class<?> clazz = DESC_CLASS_CACHE.get(desc);
+ if(clazz==null){
+ clazz = Class.forName(desc, true, cl);
+ }
+ 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 methodSignature 方法签名,形如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;
+ }
+
+ private ReflectUtils(){}
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/utils/Service.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/utils/Service.java
new file mode 100644
index 0000000..09aefea
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/utils/Service.java
@@ -0,0 +1,300 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.Iterator;
+import java.util.List;
+import java.util.NoSuchElementException;
+import java.util.Set;
+import java.util.TreeSet;
+
+/**
+ * Copy form openjdk sun.misc.Service.
+ * http://java.sun.com/j2se/1.4.2/docs/guide/jar/jar.html#Service%20Provider
+ *
+ * @author william.liangf
+ * @author qian.lei
+ * @author ding.lid
+ */
+public final class Service {
+
+ private static final String prefix = "META-INF/services/";
+
+ private Service() {
+ }
+
+ private static void fail(Class<?> service, String msg, Throwable cause) {
+ IllegalStateException sce = new IllegalStateException(service.getName() + ": "
+ + msg);
+ sce.initCause(cause);
+ throw sce;
+ }
+
+ private static void fail(Class<?> service, String msg) {
+ throw new IllegalStateException(service.getName() + ": " + msg);
+ }
+
+ private static void fail(Class<?> service, URL u, int line, String msg) {
+ fail(service, u + ":" + line + ": " + msg);
+ }
+
+ /**
+ * Parse a single line from the given configuration file, adding the name on
+ * the line to both the names list and the returned set iff the name is not
+ * already a member of the returned set.
+ */
+ private static int parseLine(Class<?> service, URL u, BufferedReader r, int lc,
+ List<String> names, Set<String> returned) throws IOException {
+ String ln = r.readLine();
+ if (ln == null) {
+ return -1;
+ }
+ int ci = ln.indexOf('#');
+ if (ci >= 0)
+ ln = ln.substring(0, ci);
+ ln = ln.trim();
+ int n = ln.length();
+ if (n != 0) {
+ if ((ln.indexOf(' ') >= 0) || (ln.indexOf('\t') >= 0))
+ fail(service, u, lc, "Illegal configuration-file syntax");
+ int cp = ln.codePointAt(0);
+ if (!Character.isJavaIdentifierStart(cp))
+ fail(service, u, lc, "Illegal provider-class name: " + ln);
+ for (int i = Character.charCount(cp); i < n; i += Character.charCount(cp)) {
+ cp = ln.codePointAt(i);
+ if (!Character.isJavaIdentifierPart(cp) && (cp != '.'))
+ fail(service, u, lc, "Illegal provider-class name: " + ln);
+ }
+ if (!returned.contains(ln)) {
+ names.add(ln);
+ returned.add(ln);
+ }
+ }
+ return lc + 1;
+ }
+
+ /**
+ * Parse the content of the given URL as a provider-configuration file.
+ *
+ * @param service The service class for which providers are being sought;
+ * used to construct error detail strings
+ * @param url The URL naming the configuration file to be parsed
+ * @param returned A Set containing the names of provider classes that have
+ * already been returned. This set will be updated to contain the
+ * names that will be yielded from the returned <tt>Iterator</tt>
+ * .
+ * @return A (possibly empty) <tt>Iterator</tt> that will yield the
+ * provider-class names in the given configuration file that are not
+ * yet members of the returned set
+ * @throws ServiceConfigurationError If an I/O error occurs while reading
+ * from the given URL, or if a configuration-file format error
+ * is detected
+ */
+ private static Iterator<String> parse(Class<?> service, URL u, Set<String> returned) {
+ InputStream in = null;
+ BufferedReader r = null;
+ ArrayList<String> names = new ArrayList<String>();
+ try {
+ in = u.openStream();
+ r = new BufferedReader(new InputStreamReader(in, "utf-8"));
+ int lc = 1;
+ while ((lc = parseLine(service, u, r, lc, names, returned)) >= 0)
+ ;
+ } catch (IOException x) {
+ fail(service, ": " + x);
+ } finally {
+ try {
+ if (r != null)
+ r.close();
+ if (in != null)
+ in.close();
+ } catch (IOException y) {
+ fail(service, ": " + y);
+ }
+ }
+ return names.iterator();
+ }
+
+ /**
+ * Private inner class implementing fully-lazy provider lookup
+ */
+ private static class LazyIterator<T> implements Iterator<Class<? extends T>> {
+
+ Class<T> service;
+ ClassLoader loader;
+ Enumeration<URL> configs = null;
+ Iterator<String> pending = null;
+ Set<String> returned = new TreeSet<String>();
+ String nextName = null;
+
+ private LazyIterator(Class<T> service, ClassLoader loader) {
+ this.service = service;
+ this.loader = loader;
+ }
+
+ public boolean hasNext() {
+ if (nextName != null) {
+ return true;
+ }
+ if (configs == null) {
+ try {
+ String fullName = prefix + service.getName();
+ if (loader == null)
+ configs = ClassLoader.getSystemResources(fullName);
+ else
+ configs = loader.getResources(fullName);
+ } catch (IOException x) {
+ fail(service, ": " + x);
+ }
+ }
+ while ((pending == null) || !pending.hasNext()) {
+ if (!configs.hasMoreElements()) {
+ return false;
+ }
+ pending = parse(service, (URL) configs.nextElement(), returned);
+ }
+ nextName = (String) pending.next();
+ return true;
+ }
+
+ @SuppressWarnings("unchecked")
+ public Class<? extends T> next() {
+ if (!hasNext()) {
+ throw new NoSuchElementException();
+ }
+ String cn = nextName;
+ nextName = null;
+ try {
+ return (Class<? extends T>) Class.forName(cn, true, loader);
+ } catch (ClassNotFoundException x) {
+ fail(service, "Provider " + cn + " not found");
+ } catch (Exception x) {
+ fail(service, "Provider " + cn + " could not be instantiated: " + x, x);
+ }
+ return null; /* This cannot happen */
+ }
+
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+ }
+
+ /**
+ * Locates and incrementally instantiates the available providers of a given
+ * service using the given class loader.
+ * <p>
+ * This method transforms the name of the given service class into a
+ * provider-configuration filename as described above and then uses the
+ * <tt>getResources</tt> method of the given class loader to find all
+ * available files with that name. These files are then read and parsed to
+ * produce a list of provider-class names. The iterator that is returned
+ * uses the given class loader to lookup and then instantiate each element
+ * of the list.
+ * <p>
+ * Because it is possible for extensions to be installed into a running Java
+ * virtual machine, this method may return different results each time it is
+ * invoked.
+ * <p>
+ *
+ * @param service The service's abstract service class
+ * @param loader The class loader to be used to load provider-configuration
+ * files and instantiate provider classes, or <tt>null</tt> if
+ * the system class loader (or, failing that the bootstrap class
+ * loader) is to be used
+ * @return An <tt>Iterator</tt> that yields provider objects for the given
+ * service, in some arbitrary order. The iterator will throw a
+ * <tt>ServiceConfigurationError</tt> if a provider-configuration
+ * file violates the specified format or if a provider class cannot
+ * be found and instantiated.
+ * @throws ServiceConfigurationError If a provider-configuration file
+ * violates the specified format or names a provider class that
+ * cannot be found and instantiated
+ * @see #providers(java.lang.Class<?>)
+ * @see #installedProviders(java.lang.Class<?>)
+ */
+ public static <T> Iterator<Class<? extends T>> providers(Class<T> service, ClassLoader loader) {
+ return new LazyIterator<T>(service, loader);
+ }
+
+ /**
+ * Locates and incrementally instantiates the available providers of a given
+ * service using the context class loader. This convenience method is
+ * equivalent to
+ *
+ * <pre>
+ * ClassLoader cl = Thread.currentThread().getContextClassLoader();
+ * return Service.providers(service, cl);
+ * </pre>
+ *
+ * @param service The service's abstract service class
+ * @return An <tt>Iterator</tt> that yields provider objects for the given
+ * service, in some arbitrary order. The iterator will throw a
+ * <tt>ServiceConfigurationError</tt> if a provider-configuration
+ * file violates the specified format or if a provider class cannot
+ * be found and instantiated.
+ * @throws ServiceConfigurationError If a provider-configuration file
+ * violates the specified format or names a provider class that
+ * cannot be found and instantiated
+ * @see #providers(java.lang.Class<?>, java.lang.ClassLoader)
+ */
+ public static <T> Iterator<Class<? extends T>> providers(Class<T> service) {
+ ClassLoader cl = Thread.currentThread().getContextClassLoader();
+ return Service.providers(service, cl);
+ }
+
+ /**
+ * Locates and incrementally instantiates the available providers of a given
+ * service using the extension class loader. This convenience method simply
+ * locates the extension class loader, call it <tt>extClassLoader</tt>, and
+ * then does
+ *
+ * <pre>
+ * return Service.providers(service, extClassLoader);
+ * </pre>
+ *
+ * If the extension class loader cannot be found then the system class
+ * loader is used; if there is no system class loader then the bootstrap
+ * class loader is used.
+ *
+ * @param service The service's abstract service class
+ * @return An <tt>Iterator</tt> that yields provider objects for the given
+ * service, in some arbitrary order. The iterator will throw a
+ * <tt>ServiceConfigurationError</tt> if a provider-configuration
+ * file violates the specified format or if a provider class cannot
+ * be found and instantiated.
+ * @throws ServiceConfigurationError If a provider-configuration file
+ * violates the specified format or names a provider class that
+ * cannot be found and instantiated
+ * @see #providers(java.lang.Class<?>, java.lang.ClassLoader)
+ */
+ public static <T> Iterator<Class<? extends T>> installedProviders(Class<T> service) {
+ ClassLoader cl = ClassLoader.getSystemClassLoader();
+ ClassLoader prev = null;
+ while (cl != null) {
+ prev = cl;
+ cl = cl.getParent();
+ }
+ return Service.providers(service, prev);
+ }
+
+}
\ 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..13d6347
--- /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
+ */
+ 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..eca8cdd
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/utils/StringUtils.java
@@ -0,0 +1,385 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.utils;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.io.UnsafeStringWriter;
+
+/**
+ * StringUtils
+ *
+ * @author qian.lei
+ */
+
+public final class StringUtils
+{
+ public static final String[] EMPTY_STRING_ARRAY = new String[0];
+
+ private static final Pattern KVP_PATTERN = Pattern.compile("([_.a-zA-Z0-9][-_.a-zA-Z0-9]*)[=](.*)"); //key value pair pattern.
+
+ private static final Pattern INT_PATTERN = Pattern.compile("^\\d+$");
+
+ public static boolean isBlank(String str)
+ {
+ if( str == null || str.length() == 0 )
+ return true;
+ return false;
+ }
+
+ /**
+ * is empty string.
+ *
+ * @param str source string.
+ * @return is empty.
+ */
+ public static boolean isEmpty(String str)
+ {
+ if( str == null || str.length() == 0 )
+ return true;
+ return false;
+ }
+
+ /**
+ * is not empty string.
+ *
+ * @param str source string.
+ * @return is not empty.
+ */
+ public static boolean isNotEmpty(String str)
+ {
+ return str != null && str.length() > 0;
+ }
+
+ /**
+ *
+ * @param s1
+ * @param s2
+ * @return
+ */
+ 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
+ */
+ public static boolean isInteger(String str) {
+ if (str == null || str.length() == 0)
+ return false;
+ return INT_PATTERN.matcher(str).matches();
+ }
+
+ public static int parseInteger(String str) {
+ if (! isInteger(str))
+ return 0;
+ return Integer.parseInt(str);
+ }
+
+ /**
+ * Returns true if s is a legal Java identifier.<p>
+ * <a href="http://www.exampledepot.com/egs/java.lang/IsJavaId.html">more info.</a>
+ */
+ public static boolean isJavaIdentifier(String s) {
+ if (s.length() == 0 || !Character.isJavaIdentifierStart(s.charAt(0))) {
+ return false;
+ }
+ for (int i=1; i<s.length(); i++) {
+ if (!Character.isJavaIdentifierPart(s.charAt(i))) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ *
+ * @param values
+ * @param value
+ * @return
+ */
+ public static boolean isContains(String[] values, String value) {
+ if (value != null && value.length() > 0 && values != null && values.length > 0) {
+ for (String v : values) {
+ if (value.equals(v)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ *
+ * @param e
+ * @return
+ */
+ 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
+ */
+ public static String toString(String msg, Throwable e) {
+ UnsafeStringWriter w = new UnsafeStringWriter();
+ w.write(msg + "\n");
+ PrintWriter p = new PrintWriter(w);
+ try {
+ e.printStackTrace(p);
+ return w.toString();
+ } finally {
+ p.close();
+ }
+ }
+
+ /**
+ * translat.
+ *
+ * @param src source string.
+ * @param from src char table.
+ * @param to target char table.
+ * @return String.
+ */
+ public static String translat(String src, String from, String to)
+ {
+ if( isEmpty(src) ) return src;
+ StringBuilder sb = null;
+ int ix;
+ char c;
+ for(int i=0,len=src.length();i<len;i++)
+ {
+ c = src.charAt(i);
+ ix = from.indexOf(c);
+ if( ix == -1 )
+ {
+ if( sb != null )
+ sb.append(c);
+ }
+ else
+ {
+ if( sb == null )
+ {
+ sb = new StringBuilder(len);
+ sb.append(src, 0, i);
+ }
+ if( ix < to.length() )
+ sb.append(to.charAt(ix));
+ }
+ }
+ return sb == null ? src : sb.toString();
+ }
+
+ /**
+ * split.
+ *
+ * @param ch char.
+ * @return string array.
+ */
+ public static String[] split(String str, char ch)
+ {
+ List<String> list = null;
+ char c;
+ int ix = 0,len=str.length();
+ for(int i=0;i<len;i++)
+ {
+ c = str.charAt(i);
+ if( c == ch )
+ {
+ if( list == null )
+ list = new ArrayList<String>();
+ list.add(str.substring(ix, i));
+ ix = i + 1;
+ }
+ }
+ if( ix > 0 )
+ list.add(str.substring(ix));
+ return list == null ? EMPTY_STRING_ARRAY : (String[])list.toArray(EMPTY_STRING_ARRAY);
+ }
+
+ /**
+ * join string.
+ *
+ * @param array String array.
+ * @return String.
+ */
+ public static String join(String[] array)
+ {
+ if( array.length == 0 ) return "";
+ StringBuilder sb = new StringBuilder();
+ for( String s : array )
+ sb.append(s);
+ return sb.toString();
+ }
+
+ /**
+ * join string like javascript.
+ *
+ * @param array String array.
+ * @param split split
+ * @return String.
+ */
+ public static String join(String[] array, char split)
+ {
+ if( array.length == 0 ) return "";
+ StringBuilder sb = new StringBuilder();
+ for(int i=0;i<array.length;i++)
+ {
+ if( i > 0 )
+ sb.append(split);
+ sb.append(array[i]);
+ }
+ return sb.toString();
+ }
+
+ /**
+ * join string like javascript.
+ *
+ * @param array String array.
+ * @param split split
+ * @return String.
+ */
+ public static String join(String[] array, String split)
+ {
+ if( array.length == 0 ) return "";
+ StringBuilder sb = new StringBuilder();
+ for(int i=0;i<array.length;i++)
+ {
+ if( i > 0 )
+ sb.append(split);
+ sb.append(array[i]);
+ }
+ return sb.toString();
+ }
+
+ public static String join(Collection<String> coll, String split) {
+ if(coll.isEmpty()) return "";
+
+ StringBuilder sb = new StringBuilder();
+ boolean isFirst = true;
+ for(String s : coll) {
+ if(isFirst) isFirst = false; else sb.append(split);
+ sb.append(s);
+ }
+ return sb.toString();
+ }
+
+ /**
+ * parse key-value pair.
+ *
+ * @param str string.
+ * @param itemSeparator item separator.
+ * @return key-value map;
+ */
+ private static Map<String, String> parseKeyValuePair(String str, String itemSeparator)
+ {
+ String[] tmp = str.split(itemSeparator);
+ Map<String, String> map = new HashMap<String, String>(tmp.length);
+ for(int i=0;i<tmp.length;i++)
+ {
+ Matcher matcher = KVP_PATTERN.matcher(tmp[i]);
+ if( matcher.matches() == false )
+ continue;
+ map.put(matcher.group(1), matcher.group(2));
+ }
+ return map;
+ }
+
+ public static String getQueryStringValue(String qs, String key) {
+ Map<String, String> map = StringUtils.parseQueryString(qs);
+ return map.get(key);
+ }
+
+ /**
+ * parse query string to Parameters.
+ *
+ * @param qs query string.
+ * @return Parameters instance.
+ */
+ public static Map<String, String> parseQueryString(String qs)
+ {
+ if( qs == null || qs.length() == 0 )
+ return new HashMap<String, String>();
+ return parseKeyValuePair(qs, "\\&");
+ }
+
+ public static String getServiceKey(Map<String, String> ps) {
+ StringBuilder buf = new StringBuilder();
+ String group = ps.get(Constants.GROUP_KEY);
+ if (group != null && group.length()>0){
+ buf.append(group).append("/");
+ }
+ buf.append(ps.get(Constants.INTERFACE_KEY));
+ String version = ps.get(Constants.VERSION_KEY);
+ if (version!= null && version.length()>0){
+ buf.append(":").append(version);
+ }
+ return buf.toString();
+ }
+
+ public static String toQueryString(Map<String, String> ps) {
+ StringBuilder buf = new StringBuilder();
+ if (ps != null && ps.size() > 0) {
+ for (Map.Entry<String, String> entry : new TreeMap<String, String>(ps).entrySet()) {
+ String key = entry.getKey();
+ String value = entry.getValue();
+ if (key != null && key.length() > 0
+ && value != null && value.length() > 0) {
+ if (buf.length() > 0) {
+ buf.append("&");
+ }
+ buf.append(key);
+ buf.append("=");
+ buf.append(value);
+ }
+ }
+ }
+ return buf.toString();
+ }
+
+ private StringUtils(){}
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/utils/UrlUtils.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/utils/UrlUtils.java
new file mode 100644
index 0000000..a96d341
--- /dev/null
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/utils/UrlUtils.java
@@ -0,0 +1,331 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.utils;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.utils.NetUtils;
+import com.alibaba.dubbo.common.utils.StringUtils;
+
+public class UrlUtils {
+
+ public static URL parseURL(String address, Map<String, String> defaults) {
+ if (address == null || address.length() == 0) {
+ return null;
+ }
+ String url;
+ if (address.indexOf("://") >= 0) {
+ url = address;
+ } else {
+ String[] addresses = Constants.COMMA_SPLIT_PATTERN.split(address);
+ url = addresses[0];
+ if (addresses.length > 1) {
+ StringBuilder backup = new StringBuilder();
+ for (int i = 1; i < addresses.length; i++) {
+ if (i > 1) {
+ backup.append(",");
+ }
+ backup.append(NetUtils.filterLocalHost(addresses[i]));
+ }
+ url += "?" + Constants.BACKUP_KEY + "=" + backup.toString();
+ }
+ }
+ String defaultProtocol = defaults == null ? null : defaults.get("protocol");
+ if (defaultProtocol == null || defaultProtocol.length() == 0) {
+ defaultProtocol = "dubbo";
+ }
+ String defaultUsername = defaults == null ? null : defaults.get("username");
+ String defaultPassword = defaults == null ? null : defaults.get("password");
+ int defaultPort = StringUtils.parseInteger(defaults == null ? null : defaults.get("port"));
+ String defaultPath = defaults == null ? null : defaults.get("path");
+ Map<String, String> defaultParameters = defaults == null ? null : new HashMap<String, String>(defaults);
+ if (defaultParameters != null) {
+ defaultParameters.remove("protocol");
+ defaultParameters.remove("username");
+ defaultParameters.remove("password");
+ defaultParameters.remove("host");
+ defaultParameters.remove("port");
+ defaultParameters.remove("path");
+ }
+ URL u = URL.valueOf(url);
+ boolean changed = false;
+ String protocol = u.getProtocol();
+ String username = u.getUsername();
+ String password = u.getPassword();
+ String host = u.getHost();
+ int port = u.getPort();
+ String path = u.getPath();
+ Map<String, String> parameters = new HashMap<String, String>(u.getParameters());
+ if ((protocol == null || protocol.length() == 0) && defaultProtocol != null && defaultProtocol.length() > 0) {
+ changed = true;
+ protocol = defaultProtocol;
+ }
+ if ((username == null || username.length() == 0) && defaultUsername != null && defaultUsername.length() > 0) {
+ changed = true;
+ username = defaultUsername;
+ }
+ if ((password == null || password.length() == 0) && defaultPassword != null && defaultPassword.length() > 0) {
+ changed = true;
+ password = defaultPassword;
+ }
+ if (port <= 0) {
+ if (defaultPort > 0) {
+ changed = true;
+ port = defaultPort;
+ } else {
+ changed = true;
+ port = 9090;
+ }
+ }
+ if (path == null || path.length() == 0) {
+ if (defaultPath != null && defaultPath.length() > 0) {
+ changed = true;
+ path = defaultPath;
+ }
+ }
+ if (defaultParameters != null && defaultParameters.size() > 0) {
+ for (Map.Entry<String, String> entry : defaultParameters.entrySet()) {
+ String key = entry.getKey();
+ String defaultValue = entry.getValue();
+ if (defaultValue != null && defaultValue.length() > 0) {
+ String value = parameters.get(key);
+ if (value == null || value.length() == 0) {
+ changed = true;
+ parameters.put(key, defaultValue);
+ }
+ }
+ }
+ }
+ if (changed) {
+ u = new URL(protocol, username, password, host, port, path, parameters);
+ }
+ return u;
+ }
+
+ public static List<URL> parseURLs(String address, Map<String, String> defaults) {
+ if (address == null || address.length() == 0) {
+ return null;
+ }
+ String[] addresses = Constants.REGISTRY_SPLIT_PATTERN.split(address);
+ if (addresses == null || addresses.length == 0) {
+ return null; //here won't be empty
+ }
+ List<URL> registries = new ArrayList<URL>();
+ for (String addr : addresses) {
+ registries.add(parseURL(addr, defaults));
+ }
+ return registries;
+ }
+
+ public static Map<String, Map<String, String>> convertRegister(Map<String, Map<String, String>> register) {
+ Map<String, Map<String, String>> newRegister = new HashMap<String, Map<String, String>>();
+ for (Map.Entry<String, Map<String, String>> entry : register.entrySet()) {
+ String serviceName = entry.getKey();
+ Map<String, String> serviceUrls = entry.getValue();
+ if (!serviceName.contains(":") && !serviceName.contains("/")) {
+ for (Map.Entry<String, String> entry2 : serviceUrls.entrySet()) {
+ String serviceUrl = entry2.getKey();
+ String serviceQuery = entry2.getValue();
+ Map<String, String> params = StringUtils.parseQueryString(serviceQuery);
+ String group = params.get("group");
+ String version = params.get("version");
+ params.remove("group");
+ params.remove("version");
+ String name = serviceName;
+ if (group != null && group.length() > 0) {
+ name = group + "/" + name;
+ }
+ if (version != null && version.length() > 0) {
+ name = name + ":" + version;
+ }
+ Map<String, String> newUrls = newRegister.get(name);
+ if (newUrls == null) {
+ newUrls = new HashMap<String, String>();
+ newRegister.put(name, newUrls);
+ }
+ newUrls.put(serviceUrl, StringUtils.toQueryString(params));
+ }
+ } else {
+ newRegister.put(serviceName, serviceUrls);
+ }
+ }
+ return newRegister;
+ }
+
+ public static Map<String, String> convertSubscribe(Map<String, String> subscribe) {
+ Map<String, String> newSubscribe = new HashMap<String, String>();
+ for (Map.Entry<String, String> entry : subscribe.entrySet()) {
+ String serviceName = entry.getKey();
+ String serviceQuery = entry.getValue();
+ if (!serviceName.contains(":") && !serviceName.contains("/")) {
+ Map<String, String> params = StringUtils.parseQueryString(serviceQuery);
+ String group = params.get("group");
+ String version = params.get("version");
+ params.remove("group");
+ params.remove("version");
+ String name = serviceName;
+ if (group != null && group.length() > 0) {
+ name = group + "/" + name;
+ }
+ if (version != null && version.length() > 0) {
+ name = name + ":" + version;
+ }
+ newSubscribe.put(name, StringUtils.toQueryString(params));
+ } else {
+ newSubscribe.put(serviceName, serviceQuery);
+ }
+ }
+ return newSubscribe;
+ }
+
+ public static Map<String, Map<String, String>> revertRegister(Map<String, Map<String, String>> register) {
+ Map<String, Map<String, String>> newRegister = new HashMap<String, Map<String, String>>();
+ for (Map.Entry<String, Map<String, String>> entry : register.entrySet()) {
+ String serviceName = entry.getKey();
+ Map<String, String> serviceUrls = entry.getValue();
+ if (serviceName.contains(":") || serviceName.contains("/")) {
+ for (Map.Entry<String, String> entry2 : serviceUrls.entrySet()) {
+ String serviceUrl = entry2.getKey();
+ String serviceQuery = entry2.getValue();
+ Map<String, String> params = StringUtils.parseQueryString(serviceQuery);
+ String name = serviceName;
+ int i = name.indexOf('/');
+ if (i >= 0) {
+ params.put("group", name.substring(0, i));
+ name = name.substring(i + 1);
+ }
+ i = name.lastIndexOf(':');
+ if (i >= 0) {
+ params.put("version", name.substring(i + 1));
+ name = name.substring(0, i);
+ }
+ Map<String, String> newUrls = newRegister.get(name);
+ if (newUrls == null) {
+ newUrls = new HashMap<String, String>();
+ newRegister.put(name, newUrls);
+ }
+ newUrls.put(serviceUrl, StringUtils.toQueryString(params));
+ }
+ } else {
+ newRegister.put(serviceName, serviceUrls);
+ }
+ }
+ return newRegister;
+ }
+
+ public static Map<String, String> revertSubscribe(Map<String, String> subscribe) {
+ Map<String, String> newSubscribe = new HashMap<String, String>();
+ for (Map.Entry<String, String> entry : subscribe.entrySet()) {
+ String serviceName = entry.getKey();
+ String serviceQuery = entry.getValue();
+ if (serviceName.contains(":") || serviceName.contains("/")) {
+ Map<String, String> params = StringUtils.parseQueryString(serviceQuery);
+ String name = serviceName;
+ int i = name.indexOf('/');
+ if (i >= 0) {
+ params.put("group", name.substring(0, i));
+ name = name.substring(i + 1);
+ }
+ i = name.lastIndexOf(':');
+ if (i >= 0) {
+ params.put("version", name.substring(i + 1));
+ name = name.substring(0, i);
+ }
+ newSubscribe.put(name, StringUtils.toQueryString(params));
+ } else {
+ newSubscribe.put(serviceName, serviceQuery);
+ }
+ }
+ return newSubscribe;
+ }
+
+ public static Map<String, Map<String, String>> revertNotify(Map<String, Map<String, String>> notify) {
+ if (notify != null && notify.size() > 0) {
+ Map<String, Map<String, String>> newNotify = new HashMap<String, Map<String, String>>();
+ for (Map.Entry<String, Map<String, String>> entry : notify.entrySet()) {
+ String serviceName = entry.getKey();
+ Map<String, String> serviceUrls = entry.getValue();
+ if (!serviceName.contains(":") && !serviceName.contains("/")) {
+ if (serviceUrls != null && serviceUrls.size() > 0) {
+ for (Map.Entry<String, String> entry2 : serviceUrls.entrySet()) {
+ String url = entry2.getKey();
+ String query = entry2.getValue();
+ Map<String, String> params = StringUtils.parseQueryString(query);
+ String group = params.get("group");
+ String version = params.get("version");
+ // params.remove("group");
+ // params.remove("version");
+ String name = serviceName;
+ if (group != null && group.length() > 0) {
+ name = group + "/" + name;
+ }
+ if (version != null && version.length() > 0) {
+ name = name + ":" + version;
+ }
+ Map<String, String> newUrls = newNotify.get(name);
+ if (newUrls == null) {
+ newUrls = new HashMap<String, String>();
+ newNotify.put(name, newUrls);
+ }
+ newUrls.put(url, StringUtils.toQueryString(params));
+ }
+ }
+ } else {
+ newNotify.put(serviceName, serviceUrls);
+ }
+ }
+ return newNotify;
+ }
+ return notify;
+ }
+
+ //compatible for dubbo-2.0.0
+ public static List<String> revertForbid(List<String> forbid, Set<String> subscribed) {
+ if (forbid != null && forbid.size() > 0) {
+ List<String> newForbid = new ArrayList<String>();
+ for (String serviceName : forbid) {
+ if (!serviceName.contains(":") && !serviceName.contains("/")) {
+ for (String name : subscribed) {
+ if (name.contains(serviceName)) {
+ newForbid.add(name);
+ break;
+ }
+ }
+ } else {
+ newForbid.add(serviceName);
+ }
+ }
+ return newForbid;
+ }
+ return forbid;
+ }
+
+ public static boolean isMatch(URL consumerUrl, URL providerUrl) {
+ String consumerGroup = consumerUrl.getParameter(Constants.GROUP_KEY);
+ String consumerVersion = consumerUrl.getParameter(Constants.VERSION_KEY);
+ String providerGroup = providerUrl.getParameter(Constants.GROUP_KEY);
+ String providerVersion = providerUrl.getParameter(Constants.VERSION_KEY);
+ return (Constants.ANY_VALUE.equals(consumerGroup) || StringUtils.isEquals(consumerGroup, providerGroup))
+ && (Constants.ANY_VALUE.equals(consumerVersion) || StringUtils.isEquals(consumerVersion, providerVersion));
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-common/src/main/resources/META-INF/services/com.alibaba.dubbo.common.serialize.Serialization b/dubbo-common/src/main/resources/META-INF/services/com.alibaba.dubbo.common.serialize.Serialization
new file mode 100644
index 0000000..09bd8e9
--- /dev/null
+++ b/dubbo-common/src/main/resources/META-INF/services/com.alibaba.dubbo.common.serialize.Serialization
@@ -0,0 +1,6 @@
+com.alibaba.dubbo.common.serialize.support.dubbo.DubboSerialization
+com.alibaba.dubbo.common.serialize.support.hessian.Hessian2Serialization
+com.alibaba.dubbo.common.serialize.support.java.JavaSerialization
+com.alibaba.dubbo.common.serialize.support.java.CompactedJavaSerialization
+com.alibaba.dubbo.common.serialize.support.json.JsonSerialization
+com.alibaba.dubbo.common.serialize.support.json.FastJsonSerialization
\ No newline at end of file
diff --git a/dubbo-common/src/main/resources/META-INF/services/com.alibaba.dubbo.common.status.StatusChecker b/dubbo-common/src/main/resources/META-INF/services/com.alibaba.dubbo.common.status.StatusChecker
new file mode 100644
index 0000000..3d14c96
--- /dev/null
+++ b/dubbo-common/src/main/resources/META-INF/services/com.alibaba.dubbo.common.status.StatusChecker
@@ -0,0 +1,2 @@
+com.alibaba.dubbo.common.status.support.MemoryStatusChecker
+com.alibaba.dubbo.common.status.support.LoadStatusChecker
\ No newline at end of file
diff --git a/dubbo-common/src/main/resources/META-INF/services/com.alibaba.dubbo.common.threadpool.ThreadPool b/dubbo-common/src/main/resources/META-INF/services/com.alibaba.dubbo.common.threadpool.ThreadPool
new file mode 100644
index 0000000..91dae06
--- /dev/null
+++ b/dubbo-common/src/main/resources/META-INF/services/com.alibaba.dubbo.common.threadpool.ThreadPool
@@ -0,0 +1,2 @@
+com.alibaba.dubbo.common.threadpool.support.fixed.FixedThreadPool
+com.alibaba.dubbo.common.threadpool.support.cached.CachedThreadPool
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/MixinTest.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/MixinTest.java
new file mode 100644
index 0000000..15a824f
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/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;
+
+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/ProxyTest.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/ProxyTest.java
new file mode 100644
index 0000000..069323d
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/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;
+
+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/URLTest.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/URLTest.java
new file mode 100644
index 0000000..a1fb2b8
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/URLTest.java
@@ -0,0 +1,536 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.*;
+
+import static org.hamcrest.CoreMatchers.*;
+
+import java.io.File;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.junit.Test;
+
+import com.alibaba.dubbo.common.utils.CollectionUtils;
+
+/**
+ * @author ding.lid
+ * @author william.liangf
+ */
+public class URLTest {
+
+ @Test
+ public void test_valueOf_noProtocolAndHost() throws Exception {
+ URL url = URL.valueOf("/context/path?version=1.0.0&application=morgan");
+ assertNull(url.getProtocol());
+ assertNull(url.getUsername());
+ assertNull(url.getPassword());
+ assertNull(url.getHost());
+ assertEquals(0, url.getPort());
+ assertEquals("context/path", url.getPath());
+ assertEquals(2, url.getParameters().size());
+ assertEquals("1.0.0", url.getParameter("version"));
+ assertEquals("morgan", url.getParameter("application"));
+
+
+ url = URL.valueOf("context/path?version=1.0.0&application=morgan");
+ // ^^^^^^^ Caution , parse as host
+ assertNull(url.getProtocol());
+ assertNull(url.getUsername());
+ assertNull(url.getPassword());
+ assertEquals("context", url.getHost());
+ assertEquals(0, url.getPort());
+ assertEquals("path", url.getPath());
+ assertEquals(2, url.getParameters().size());
+ assertEquals("1.0.0", url.getParameter("version"));
+ assertEquals("morgan", url.getParameter("application"));
+ }
+
+ @Test
+ public void test_valueOf_noProtocol() throws Exception {
+ URL url = URL.valueOf("10.20.130.230");
+ assertNull(url.getProtocol());
+ assertNull(url.getUsername());
+ assertNull(url.getPassword());
+ assertEquals("10.20.130.230", url.getHost());
+ assertEquals(0, url.getPort());
+ assertEquals(null, url.getPath());
+ assertEquals(0, url.getParameters().size());
+
+ url = URL.valueOf("10.20.130.230:20880");
+ assertNull(url.getProtocol());
+ assertNull(url.getUsername());
+ assertNull(url.getPassword());
+ assertEquals("10.20.130.230", url.getHost());
+ assertEquals(20880, url.getPort());
+ assertEquals(null, url.getPath());
+ assertEquals(0, url.getParameters().size());
+
+ url = URL.valueOf("10.20.130.230/context/path");
+ assertNull(url.getProtocol());
+ assertNull(url.getUsername());
+ assertNull(url.getPassword());
+ assertEquals("10.20.130.230", url.getHost());
+ assertEquals(0, url.getPort());
+ assertEquals("context/path", url.getPath());
+ assertEquals(0, url.getParameters().size());
+
+ url = URL.valueOf("10.20.130.230:20880/context/path");
+ assertNull(url.getProtocol());
+ assertNull(url.getUsername());
+ assertNull(url.getPassword());
+ assertEquals("10.20.130.230", url.getHost());
+ assertEquals(20880, url.getPort());
+ assertEquals("context/path", url.getPath());
+ assertEquals(0, url.getParameters().size());
+
+ url = URL.valueOf("admin:hello1234@10.20.130.230:20880/context/path?version=1.0.0&application=morgan");
+ assertNull(url.getProtocol());
+ assertEquals("admin", url.getUsername());
+ assertEquals("hello1234", url.getPassword());
+ assertEquals("10.20.130.230", url.getHost());
+ assertEquals(20880, url.getPort());
+ assertEquals("context/path", url.getPath());
+ assertEquals(2, url.getParameters().size());
+ assertEquals("1.0.0", url.getParameter("version"));
+ assertEquals("morgan", url.getParameter("application"));
+ }
+
+ @Test
+ public void test_valueOf_noHost() throws Exception {
+ URL url = URL.valueOf("file:///home/user1/router.js");
+ assertEquals("file", url.getProtocol());
+ assertNull(url.getUsername());
+ assertNull(url.getPassword());
+ assertNull(url.getHost());
+ assertEquals(0, url.getPort());
+ assertEquals("home/user1/router.js", url.getPath());
+ assertEquals(0, url.getParameters().size());
+
+ // Caution!!
+ url = URL.valueOf("file://home/user1/router.js");
+ // ^^ only tow slash!
+ assertEquals("file", url.getProtocol());
+ assertNull(url.getUsername());
+ assertNull(url.getPassword());
+ assertEquals("home", url.getHost());
+ assertEquals(0, url.getPort());
+ assertEquals("user1/router.js", url.getPath());
+ assertEquals(0, url.getParameters().size());
+
+
+ url = URL.valueOf("file:/home/user1/router.js");
+ assertEquals("file", url.getProtocol());
+ assertNull(url.getUsername());
+ assertNull(url.getPassword());
+ assertNull(url.getHost());
+ assertEquals(0, url.getPort());
+ assertEquals("home/user1/router.js", url.getPath());
+ assertEquals(0, url.getParameters().size());
+
+ url = URL.valueOf("file:///d:/home/user1/router.js");
+ assertEquals("file", url.getProtocol());
+ assertNull(url.getUsername());
+ assertNull(url.getPassword());
+ assertNull(url.getHost());
+ assertEquals(0, url.getPort());
+ assertEquals("d:/home/user1/router.js", url.getPath());
+ assertEquals(0, url.getParameters().size());
+
+ url = URL.valueOf("file:///home/user1/router.js?p1=v1&p2=v2");
+ assertEquals("file", url.getProtocol());
+ assertNull(url.getUsername());
+ assertNull(url.getPassword());
+ assertNull(url.getHost());
+ assertEquals(0, url.getPort());
+ assertEquals("home/user1/router.js", url.getPath());
+ assertEquals(2, url.getParameters().size());
+ Map<String, String> params = new HashMap<String, String>();
+ params.put("p1", "v1");
+ params.put("p2", "v2");
+ assertEquals(params, url.getParameters());
+
+ url = URL.valueOf("file:/home/user1/router.js?p1=v1&p2=v2");
+ assertEquals("file", url.getProtocol());
+ assertNull(url.getUsername());
+ assertNull(url.getPassword());
+ assertNull(url.getHost());
+ assertEquals(0, url.getPort());
+ assertEquals("home/user1/router.js", url.getPath());
+ assertEquals(2, url.getParameters().size());
+ params = new HashMap<String, String>();
+ params.put("p1", "v1");
+ params.put("p2", "v2");
+ assertEquals(params, url.getParameters());
+ }
+
+ @Test
+ public void test_valueOf_WithProtocolHost() throws Exception {
+ URL url = URL.valueOf("dubbo://10.20.130.230");
+ assertEquals("dubbo", url.getProtocol());
+ assertNull(url.getUsername());
+ assertNull(url.getPassword());
+ assertEquals("10.20.130.230", url.getHost());
+ assertEquals(0, url.getPort());
+ assertEquals(null, url.getPath());
+ assertEquals(0, url.getParameters().size());
+
+ url = URL.valueOf("dubbo://10.20.130.230:20880/context/path");
+ assertEquals("dubbo", url.getProtocol());
+ assertNull(url.getUsername());
+ assertNull(url.getPassword());
+ assertEquals("10.20.130.230", url.getHost());
+ assertEquals(20880, url.getPort());
+ assertEquals("context/path", url.getPath());
+ assertEquals(0, url.getParameters().size());
+
+ url = URL.valueOf("dubbo://admin:hello1234@10.20.130.230:20880");
+ assertEquals("dubbo", url.getProtocol());
+ assertEquals("admin", url.getUsername());
+ assertEquals("hello1234", url.getPassword());
+ assertEquals("10.20.130.230", url.getHost());
+ assertEquals(20880, url.getPort());
+ assertEquals(null, url.getPath());
+ assertEquals(0, url.getParameters().size());
+
+ url = URL.valueOf("dubbo://admin:hello1234@10.20.130.230:20880?version=1.0.0");
+ assertEquals("dubbo", url.getProtocol());
+ assertEquals("admin", url.getUsername());
+ assertEquals("hello1234", url.getPassword());
+ assertEquals("10.20.130.230", url.getHost());
+ assertEquals(20880, url.getPort());
+ assertEquals(null, url.getPath());
+ assertEquals(1, url.getParameters().size());
+ assertEquals("1.0.0", url.getParameter("version"));
+
+ url = URL.valueOf("dubbo://admin:hello1234@10.20.130.230:20880/context/path?version=1.0.0&application=morgan");
+ assertEquals("dubbo", url.getProtocol());
+ assertEquals("admin", url.getUsername());
+ assertEquals("hello1234", url.getPassword());
+ assertEquals("10.20.130.230", url.getHost());
+ assertEquals(20880, url.getPort());
+ assertEquals("context/path", url.getPath());
+ assertEquals(2, url.getParameters().size());
+ assertEquals("1.0.0", url.getParameter("version"));
+ assertEquals("morgan", url.getParameter("application"));
+
+ url = URL.valueOf("dubbo://admin:hello1234@10.20.130.230:20880/context/path?version=1.0.0&application=morgan&noValue");
+ assertEquals("dubbo", url.getProtocol());
+ assertEquals("admin", url.getUsername());
+ assertEquals("hello1234", url.getPassword());
+ assertEquals("10.20.130.230", url.getHost());
+ assertEquals(20880, url.getPort());
+ assertEquals("context/path", url.getPath());
+ assertEquals(3, url.getParameters().size());
+ assertEquals("1.0.0", url.getParameter("version"));
+ assertEquals("morgan", url.getParameter("application"));
+ assertEquals("noValue", url.getParameter("noValue"));
+ }
+
+ @Test
+ public void test_valueOf_Exception_noProtocol() throws Exception {
+ try {
+ URL.valueOf("://1.2.3.4:8080/path");
+ fail();
+ } catch (IllegalStateException expected) {
+ assertEquals("url missing protocol: \"://1.2.3.4:8080/path\"", expected.getMessage());
+ }
+ }
+
+ @Test
+ public void test_getAddress() throws Exception {
+ URL url1 = URL.valueOf("dubbo://admin:hello1234@10.20.130.230:20880/context/path?version=1.0.0&application=morgan");
+ assertEquals("10.20.130.230:20880", url1.getAddress());
+ }
+
+ @Test
+ public void test_getAbsolutePath() throws Exception {
+ URL url = new URL("p1", "1.2.2.2", 33);
+ assertEquals(null, url.getAbsolutePath());
+
+ url = new URL("file", null, 90, "/home/user1/route.js");
+ assertEquals("/home/user1/route.js", url.getAbsolutePath());
+ }
+
+ @Test
+ public void test_equals() throws Exception {
+ URL url1 = URL.valueOf("dubbo://admin:hello1234@10.20.130.230:20880/context/path?version=1.0.0&application=morgan");
+
+ Map<String, String> params = new HashMap<String, String>();
+ params.put("version", "1.0.0");
+ params.put("application", "morgan");
+ URL url2 = new URL("dubbo", "10.20.130.230", 20880, "context/path", params);
+
+ assertEquals(url1, url2);
+ }
+
+ @Test
+ public void test_toString() throws Exception {
+ URL url1 = URL.valueOf("dubbo://admin:hello1234@10.20.130.230:20880/context/path?version=1.0.0&application=morgan");
+ assertThat(url1.toString(), anyOf(
+ equalTo("dubbo://10.20.130.230:20880/context/path?version=1.0.0&application=morgan"),
+ equalTo("dubbo://10.20.130.230:20880/context/path?application=morgan&version=1.0.0"))
+ );
+ }
+
+ @Test
+ public void test_toFullString() throws Exception {
+ URL url1 = URL.valueOf("dubbo://admin:hello1234@10.20.130.230:20880/context/path?version=1.0.0&application=morgan");
+ assertThat(url1.toFullString(), anyOf(
+ equalTo("dubbo://admin:hello1234@10.20.130.230:20880/context/path?version=1.0.0&application=morgan"),
+ equalTo("dubbo://admin:hello1234@10.20.130.230:20880/context/path?application=morgan&version=1.0.0"))
+ );
+ }
+
+ @Test
+ public void test_set_methods() throws Exception {
+ URL url = URL.valueOf("dubbo://admin:hello1234@10.20.130.230:20880/context/path?version=1.0.0&application=morgan");
+
+ url = url.setHost("host");
+
+ assertEquals("dubbo", url.getProtocol());
+ assertEquals("admin", url.getUsername());
+ assertEquals("hello1234", url.getPassword());
+ assertEquals("host", url.getHost());
+ assertEquals(20880, url.getPort());
+ assertEquals("context/path", url.getPath());
+ assertEquals(2, url.getParameters().size());
+ assertEquals("1.0.0", url.getParameter("version"));
+ assertEquals("morgan", url.getParameter("application"));
+
+ url = url.setPort(1);
+
+ assertEquals("dubbo", url.getProtocol());
+ assertEquals("admin", url.getUsername());
+ assertEquals("hello1234", url.getPassword());
+ assertEquals("host", url.getHost());
+ assertEquals(1, url.getPort());
+ assertEquals("context/path", url.getPath());
+ assertEquals(2, url.getParameters().size());
+ assertEquals("1.0.0", url.getParameter("version"));
+ assertEquals("morgan", url.getParameter("application"));
+
+ url = url.setPath("path");
+
+ assertEquals("dubbo", url.getProtocol());
+ assertEquals("admin", url.getUsername());
+ assertEquals("hello1234", url.getPassword());
+ assertEquals("host", url.getHost());
+ assertEquals(1, url.getPort());
+ assertEquals("path", url.getPath());
+ assertEquals(2, url.getParameters().size());
+ assertEquals("1.0.0", url.getParameter("version"));
+ assertEquals("morgan", url.getParameter("application"));
+
+ url = url.setProtocol("protocol");
+
+ assertEquals("protocol", url.getProtocol());
+ assertEquals("admin", url.getUsername());
+ assertEquals("hello1234", url.getPassword());
+ assertEquals("host", url.getHost());
+ assertEquals(1, url.getPort());
+ assertEquals("path", url.getPath());
+ assertEquals(2, url.getParameters().size());
+ assertEquals("1.0.0", url.getParameter("version"));
+ assertEquals("morgan", url.getParameter("application"));
+
+ url = url.setUsername("username");
+
+ assertEquals("protocol", url.getProtocol());
+ assertEquals("username", url.getUsername());
+ assertEquals("hello1234", url.getPassword());
+ assertEquals("host", url.getHost());
+ assertEquals(1, url.getPort());
+ assertEquals("path", url.getPath());
+ assertEquals(2, url.getParameters().size());
+ assertEquals("1.0.0", url.getParameter("version"));
+ assertEquals("morgan", url.getParameter("application"));
+
+ url = url.setPassword("password");
+
+ assertEquals("protocol", url.getProtocol());
+ assertEquals("username", url.getUsername());
+ assertEquals("password", url.getPassword());
+ assertEquals("host", url.getHost());
+ assertEquals(1, url.getPort());
+ assertEquals("path", url.getPath());
+ assertEquals(2, url.getParameters().size());
+ assertEquals("1.0.0", url.getParameter("version"));
+ assertEquals("morgan", url.getParameter("application"));
+ }
+
+ @Test
+ public void test_removeParameters() throws Exception {
+ URL url = URL.valueOf("dubbo://admin:hello1234@10.20.130.230:20880/context/path?version=1.0.0&application=morgan&k1=v1&k2=v2");
+
+ url = url.removeParameter("version");
+ assertEquals("dubbo", url.getProtocol());
+ assertEquals("admin", url.getUsername());
+ assertEquals("hello1234", url.getPassword());
+ assertEquals("10.20.130.230", url.getHost());
+ assertEquals(20880, url.getPort());
+ assertEquals("context/path", url.getPath());
+ assertEquals(3, url.getParameters().size());
+ assertEquals("morgan", url.getParameter("application"));
+ assertEquals("v1", url.getParameter("k1"));
+ assertEquals("v2", url.getParameter("k2"));
+
+ url = URL.valueOf("dubbo://admin:hello1234@10.20.130.230:20880/context/path?version=1.0.0&application=morgan&k1=v1&k2=v2");
+ url = url.removeParameters("version", "application");
+ assertEquals("dubbo", url.getProtocol());
+ assertEquals("admin", url.getUsername());
+ assertEquals("hello1234", url.getPassword());
+ assertEquals("10.20.130.230", url.getHost());
+ assertEquals(20880, url.getPort());
+ assertEquals("context/path", url.getPath());
+ assertEquals(2, url.getParameters().size());
+ assertEquals("v1", url.getParameter("k1"));
+ assertEquals("v2", url.getParameter("k2"));
+
+ url = URL.valueOf("dubbo://admin:hello1234@10.20.130.230:20880/context/path?version=1.0.0&application=morgan&k1=v1&k2=v2");
+ url = url.removeParameters(Arrays.asList("version", "application"));
+ assertEquals("dubbo", url.getProtocol());
+ assertEquals("admin", url.getUsername());
+ assertEquals("hello1234", url.getPassword());
+ assertEquals("10.20.130.230", url.getHost());
+ assertEquals(20880, url.getPort());
+ assertEquals("context/path", url.getPath());
+ assertEquals(2, url.getParameters().size());
+ assertEquals("v1", url.getParameter("k1"));
+ assertEquals("v2", url.getParameter("k2"));
+ }
+
+ @Test
+ public void test_addParameters() throws Exception {
+ URL url = URL.valueOf("dubbo://admin:hello1234@10.20.130.230:20880/context/path?application=morgan");
+ url = url.addParameters(CollectionUtils.toStringMap("k1", "v1", "k2", "v2"));
+
+ assertEquals("dubbo", url.getProtocol());
+ assertEquals("admin", url.getUsername());
+ assertEquals("hello1234", url.getPassword());
+ assertEquals("10.20.130.230", url.getHost());
+ assertEquals(20880, url.getPort());
+ assertEquals("context/path", url.getPath());
+ assertEquals(3, url.getParameters().size());
+ assertEquals("morgan", url.getParameter("application"));
+ assertEquals("v1", url.getParameter("k1"));
+ assertEquals("v2", url.getParameter("k2"));
+
+ url = URL.valueOf("dubbo://admin:hello1234@10.20.130.230:20880/context/path?application=morgan");
+ url = url.addParameters("k1", "v1", "k2", "v2", "application", "xxx");
+
+ assertEquals("dubbo", url.getProtocol());
+ assertEquals("admin", url.getUsername());
+ assertEquals("hello1234", url.getPassword());
+ assertEquals("10.20.130.230", url.getHost());
+ assertEquals(20880, url.getPort());
+ assertEquals("context/path", url.getPath());
+ assertEquals(3, url.getParameters().size());
+ assertEquals("xxx", url.getParameter("application"));
+ assertEquals("v1", url.getParameter("k1"));
+ assertEquals("v2", url.getParameter("k2"));
+
+ url = URL.valueOf("dubbo://admin:hello1234@10.20.130.230:20880/context/path?application=morgan");
+ url = url.addParametersIfAbsent(CollectionUtils.toStringMap("k1", "v1", "k2", "v2", "application", "xxx"));
+
+ assertEquals("dubbo", url.getProtocol());
+ assertEquals("admin", url.getUsername());
+ assertEquals("hello1234", url.getPassword());
+ assertEquals("10.20.130.230", url.getHost());
+ assertEquals(20880, url.getPort());
+ assertEquals("context/path", url.getPath());
+ assertEquals(3, url.getParameters().size());
+ assertEquals("morgan", url.getParameter("application"));
+ assertEquals("v1", url.getParameter("k1"));
+ assertEquals("v2", url.getParameter("k2"));
+
+ url = URL.valueOf("dubbo://admin:hello1234@10.20.130.230:20880/context/path?application=morgan");
+ url = url.addParameter("k1", "v1");
+
+ assertEquals("dubbo", url.getProtocol());
+ assertEquals("admin", url.getUsername());
+ assertEquals("hello1234", url.getPassword());
+ assertEquals("10.20.130.230", url.getHost());
+ assertEquals(20880, url.getPort());
+ assertEquals("context/path", url.getPath());
+ assertEquals(2, url.getParameters().size());
+ assertEquals("morgan", url.getParameter("application"));
+ assertEquals("v1", url.getParameter("k1"));
+
+ url = URL.valueOf("dubbo://admin:hello1234@10.20.130.230:20880/context/path?application=morgan");
+ url = url.addParameter("application", "xxx");
+
+ assertEquals("dubbo", url.getProtocol());
+ assertEquals("admin", url.getUsername());
+ assertEquals("hello1234", url.getPassword());
+ assertEquals("10.20.130.230", url.getHost());
+ assertEquals(20880, url.getPort());
+ assertEquals("context/path", url.getPath());
+ assertEquals(1, url.getParameters().size());
+ assertEquals("xxx", url.getParameter("application"));
+
+ url = URL.valueOf("dubbo://admin:hello1234@10.20.130.230:20880/context/path?application=morgan");
+ url = url.addParameterIfAbsent("application", "xxx");
+
+ assertEquals("dubbo", url.getProtocol());
+ assertEquals("admin", url.getUsername());
+ assertEquals("hello1234", url.getPassword());
+ assertEquals("10.20.130.230", url.getHost());
+ assertEquals(20880, url.getPort());
+ assertEquals("context/path", url.getPath());
+ assertEquals(1, url.getParameters().size());
+ assertEquals("morgan", url.getParameter("application"));
+ }
+
+ @Test
+ public void test_windowAbsolutePathBeginWithSlashIsValid() throws Exception {
+ final String osProperty = System.getProperties().getProperty("os.name");
+ if(!osProperty.toLowerCase().contains("windows")) return;
+
+ System.out.println("Test Windows valid path string.");
+
+ File f0 = new File("C:/Windows");
+ File f1 = new File("/C:/Windows");
+
+ File f2 = new File("C:\\Windows");
+ File f3 = new File("/C:\\Windows");
+ File f4 = new File("\\C:\\Windows");
+
+ assertEquals(f0, f1);
+ assertEquals(f0, f2);
+ assertEquals(f0, f3);
+ assertEquals(f0, f4);
+ }
+
+ @Test
+ public void test_javaNetUrl() throws Exception {
+ java.net.URL url = new java.net.URL("http://admin:hello1234@10.20.130.230:20880/context/path?version=1.0.0&application=morgan#anchor1");
+
+ assertEquals("http", url.getProtocol());
+ assertEquals("admin:hello1234", url.getUserInfo());
+ assertEquals("10.20.130.230", url.getHost());
+ assertEquals(20880, url.getPort());
+ assertEquals("/context/path", url.getPath());
+ assertEquals("version=1.0.0&application=morgan", url.getQuery());
+ assertEquals("anchor1", url.getRef());
+
+ assertEquals("admin:hello1234@10.20.130.230:20880", url.getAuthority());
+ assertEquals("/context/path?version=1.0.0&application=morgan", url.getFile());
+ }
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/WrapperTest.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/WrapperTest.java
new file mode 100644
index 0000000..bb58caa
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/WrapperTest.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;
+
+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" });
+ }
+
+ 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/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/extensionloader/ExtensionLoaderTest.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ExtensionLoaderTest.java
new file mode 100644
index 0000000..b58f581
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ExtensionLoaderTest.java
@@ -0,0 +1,422 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.extensionloader;
+
+import static org.hamcrest.CoreMatchers.anyOf;
+import static org.hamcrest.CoreMatchers.*;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.*;
+import static org.junit.matchers.JUnitMatchers.containsString;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import org.junit.Test;
+
+import com.alibaba.dubbo.common.ExtensionLoader;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.extensionloader.ext1.Ext1;
+import com.alibaba.dubbo.common.extensionloader.ext1.impl.Ext1Impl1;
+import com.alibaba.dubbo.common.extensionloader.ext1.impl.Ext1Impl2;
+import com.alibaba.dubbo.common.extensionloader.ext2.Ext2;
+import com.alibaba.dubbo.common.extensionloader.ext2.UrlHolder;
+import com.alibaba.dubbo.common.extensionloader.ext3.Ext3;
+import com.alibaba.dubbo.common.extensionloader.ext4.Ext4;
+import com.alibaba.dubbo.common.extensionloader.ext5.Ext5NoAdaptiveMethod;
+import com.alibaba.dubbo.common.extensionloader.ext5.impl.Ext5Wrapper1;
+import com.alibaba.dubbo.common.extensionloader.ext5.impl.Ext5Wrapper2;
+import com.alibaba.dubbo.common.extensionloader.ext6_inject.Ext6;
+import com.alibaba.dubbo.common.extensionloader.ext6_inject.impl.Ext6Impl2;
+
+/**
+ * @author ding.lid
+ */
+public class ExtensionLoaderTest {
+ @Test
+ public void test_getDefault() throws Exception {
+ Ext1 ext = ExtensionLoader.getExtensionLoader(Ext1.class).getDefaultExtension();
+ assertThat(ext, instanceOf(Ext1Impl1.class));
+
+ String name = ExtensionLoader.getExtensionLoader(Ext1.class).getDefaultExtensionName();
+ assertEquals("impl1", name);
+ }
+
+ @Test
+ public void test_getDefault_NULL() throws Exception {
+ Ext2 ext = ExtensionLoader.getExtensionLoader(Ext2.class).getDefaultExtension();
+ assertNull(ext);
+
+ String name = ExtensionLoader.getExtensionLoader(Ext2.class).getDefaultExtensionName();
+ assertNull(name);
+ }
+
+ @Test
+ public void test_getExtension() throws Exception {
+ assertTrue(ExtensionLoader.getExtensionLoader(Ext1.class).getExtension("impl1") instanceof Ext1Impl1);
+ assertTrue(ExtensionLoader.getExtensionLoader(Ext1.class).getExtension("impl2") instanceof Ext1Impl2);
+ }
+
+ @Test
+ public void test_getExtension_WithWrapper() throws Exception {
+ Ext5NoAdaptiveMethod impl1 = ExtensionLoader.getExtensionLoader(Ext5NoAdaptiveMethod.class).getExtension("impl1");
+ assertThat(impl1, anyOf(instanceOf(Ext5Wrapper1.class), instanceOf(Ext5Wrapper2.class)));
+
+ Ext5NoAdaptiveMethod impl2 = ExtensionLoader.getExtensionLoader(Ext5NoAdaptiveMethod.class).getExtension("impl2") ;
+ assertThat(impl2, anyOf(instanceOf(Ext5Wrapper1.class), instanceOf(Ext5Wrapper2.class)));
+
+
+ URL url = new URL("p1", "1.2.3.4", 1010, "path1");
+ int echoCount1 = Ext5Wrapper1.echoCount.get();
+ int echoCount2 = Ext5Wrapper2.echoCount.get();
+ int yellCount1 = Ext5Wrapper1.yellCount.get();
+ int yellCount2 = Ext5Wrapper2.yellCount.get();
+
+ assertEquals("Ext5Impl1-echo", impl1.echo(url, "ha"));
+ assertEquals(echoCount1 + 1, Ext5Wrapper1.echoCount.get());
+ assertEquals(echoCount2 + 1, Ext5Wrapper2.echoCount.get());
+ assertEquals(yellCount1, Ext5Wrapper1.yellCount.get());
+ assertEquals(yellCount2, Ext5Wrapper2.yellCount.get());
+
+ assertEquals("Ext5Impl2-yell", impl2.yell(url, "ha"));
+ assertEquals(echoCount1 + 1, Ext5Wrapper1.echoCount.get());
+ assertEquals(echoCount2 + 1, Ext5Wrapper2.echoCount.get());
+ assertEquals(yellCount1 + 1, Ext5Wrapper1.yellCount.get());
+ assertEquals(yellCount2 + 1, Ext5Wrapper2.yellCount.get());
+ }
+
+ @Test
+ public void test_getExtension_ExceptionNoExtension() throws Exception {
+ try {
+ ExtensionLoader.getExtensionLoader(Ext1.class).getExtension("XXX");
+ } catch (IllegalStateException expected) {
+ assertThat(expected.getMessage(), containsString("No such extension com.alibaba.dubbo.common.extensionloader.ext1.Ext1 by name XXX"));
+ }
+ }
+
+ @Test
+ public void test_getExtension_ExceptionNoExtension_NameOnWrapperNoAffact() throws Exception {
+ try {
+ ExtensionLoader.getExtensionLoader(Ext5NoAdaptiveMethod.class).getExtension("XXX");
+ } catch (IllegalStateException expected) {
+ assertThat(expected.getMessage(), containsString("No such extension com.alibaba.dubbo.common.extensionloader.ext5.Ext5NoAdaptiveMethod by name XXX"));
+ }
+ }
+
+ @Test
+ public void test_getExtension_ExceptionNullArg() throws Exception {
+ try {
+ ExtensionLoader.getExtensionLoader(Ext1.class).getExtension(null);
+ } catch (IllegalArgumentException expected) {
+ assertThat(expected.getMessage(), containsString("Extension name == null"));
+ }
+ }
+
+ @Test
+ public void test_hasExtension() throws Exception {
+ assertTrue(ExtensionLoader.getExtensionLoader(Ext1.class).hasExtension("impl1"));
+ assertFalse(ExtensionLoader.getExtensionLoader(Ext1.class).hasExtension("impl1,impl2"));
+ assertFalse(ExtensionLoader.getExtensionLoader(Ext1.class).hasExtension("xxx"));
+
+ try {
+ ExtensionLoader.getExtensionLoader(Ext1.class).hasExtension(null);
+ fail();
+ } catch (IllegalArgumentException expected) {
+ assertThat(expected.getMessage(), containsString("Extension name == null"));
+ }
+ }
+
+ @Test
+ public void test_getSupportedExtensions() throws Exception {
+ Set<String> exts = ExtensionLoader.getExtensionLoader(Ext1.class).getSupportedExtensions();
+
+ Set<String> expected = new HashSet<String>();
+ expected.add("impl1");
+ expected.add("impl2");
+ expected.add("impl3");
+
+ assertEquals(expected, exts);
+ }
+
+ @Test
+ public void test_getSupportedExtensions_NoExtension() throws Exception {
+ Set<String> exts = ExtensionLoader.getExtensionLoader(ExtensionLoaderTest.class).getSupportedExtensions();
+ assertEquals(0, exts.size());
+ }
+
+ @Test
+ public void test_getAdaptiveExtension_defaultExtension() throws Exception {
+ Ext1 ext = ExtensionLoader.getExtensionLoader(Ext1.class).getAdaptiveExtension();
+
+ Map<String, String> map = new HashMap<String, String>();
+ URL url = new URL("p1", "1.2.3.4", 1010, "path1", map);
+
+ String echo = ext.echo(url, "haha");
+ assertEquals("Ext1Impl1-echo", echo);
+ }
+
+ @Test
+ public void test_getAdaptiveExtension() throws Exception {
+ Ext1 ext = ExtensionLoader.getExtensionLoader(Ext1.class).getAdaptiveExtension();
+
+ Map<String, String> map = new HashMap<String, String>();
+ map.put("ext1", "impl2");
+ URL url = new URL("p1", "1.2.3.4", 1010, "path1", map);
+
+ String echo = ext.echo(url, "haha");
+ assertEquals("Ext1Impl2-echo", echo);
+ }
+
+ @Test
+ public void test_getAdaptiveExtension_customizeKey() throws Exception {
+ Ext1 ext = ExtensionLoader.getExtensionLoader(Ext1.class).getAdaptiveExtension();
+
+ Map<String, String> map = new HashMap<String, String>();
+ map.put("key2", "impl2");
+ URL url = new URL("p1", "1.2.3.4", 1010, "path1", map);
+
+ String echo = ext.yell(url, "haha");
+ assertEquals("Ext1Impl2-yell", echo);
+
+ url = url.addParameter("key1", "impl3"); // 注意: URL是值类型
+ echo = ext.yell(url, "haha");
+ assertEquals("Ext1Impl3-yell", echo);
+ }
+
+ @Test
+ public void test_getAdaptiveExtension_UrlNpe() throws Exception {
+ Ext1 ext = ExtensionLoader.getExtensionLoader(Ext1.class).getAdaptiveExtension();
+
+ try {
+ ext.echo(null, "haha");
+ fail();
+ } catch (IllegalArgumentException e) {
+ assertEquals("url == null", e.getMessage());
+ }
+ }
+
+ @Test
+ public void test_getAdaptiveExtension_ExceptionWhenNoAdativeMethodOnInterface() throws Exception {
+ try {
+ ExtensionLoader.getExtensionLoader(Ext5NoAdaptiveMethod.class).getAdaptiveExtension();
+ fail();
+ } catch (IllegalStateException expected) {
+ assertThat(expected.getMessage(),
+ allOf(containsString("Can not create adaptive extenstion interface com.alibaba.dubbo.common.extensionloader.ext5.Ext5NoAdaptiveMethod"),
+ containsString("No adaptive method on extension com.alibaba.dubbo.common.extensionloader.ext5.Ext5NoAdaptiveMethod, refuse to create the adaptive class")));
+ }
+ // 多次get,都会报错且相同
+ try {
+ ExtensionLoader.getExtensionLoader(Ext5NoAdaptiveMethod.class).getAdaptiveExtension();
+ fail();
+ } catch (IllegalStateException expected) {
+ assertThat(expected.getMessage(),
+ allOf(containsString("Can not create adaptive extenstion interface com.alibaba.dubbo.common.extensionloader.ext5.Ext5NoAdaptiveMethod"),
+ containsString("No adaptive method on extension com.alibaba.dubbo.common.extensionloader.ext5.Ext5NoAdaptiveMethod, refuse to create the adaptive class")));
+ }
+ }
+
+ @Test
+ public void test_getAdaptiveExtension_ExceptionWhenNotAdativeMethod() throws Exception {
+ Ext1 ext = ExtensionLoader.getExtensionLoader(Ext1.class).getAdaptiveExtension();
+
+ Map<String, String> map = new HashMap<String, String>();
+ URL url = new URL("p1", "1.2.3.4", 1010, "path1", map);
+
+ try {
+ ext.bang(url, 33);
+ fail();
+ } catch (UnsupportedOperationException expected) {
+ assertThat(expected.getMessage(), containsString("method "));
+ assertThat(
+ expected.getMessage(),
+ containsString("of interface com.alibaba.dubbo.common.extensionloader.ext1.Ext1 is not adaptive method!"));
+ }
+ }
+
+ @Test
+ public void test_getAdaptiveExtension_ExceptionWhenNoUrlAttrib() throws Exception {
+ try {
+ ExtensionLoader.getExtensionLoader(Ext4.class).getAdaptiveExtension();
+ fail();
+ } catch (Exception expected) {
+ assertThat(expected.getMessage(), containsString("fail to create adative class for interface "));
+ assertThat(expected.getMessage(), containsString(": not found url parameter or url attribute in parameters of method "));
+ }
+ }
+
+ @Test
+ public void test_getAdaptiveExtension_protocolKey() throws Exception {
+ Ext3 ext = ExtensionLoader.getExtensionLoader(Ext3.class).getAdaptiveExtension();
+
+ Map<String, String> map = new HashMap<String, String>();
+ URL url = new URL("impl3", "1.2.3.4", 1010, "path1", map);
+
+ String echo = ext.echo(url, "s");
+ assertEquals("Ext3Impl3-echo", echo);
+
+ url = url.addParameter("key1", "impl2");
+ echo = ext.echo(url, "s");
+ assertEquals("Ext3Impl2-echo", echo);
+
+ String yell = ext.yell(url, "d");
+ assertEquals("Ext3Impl3-yell", yell);
+ }
+
+
+ @Test
+ public void test_getAdaptiveExtension_lastProtocolKey() throws Exception {
+ Ext2 ext = ExtensionLoader.getExtensionLoader(Ext2.class).getAdaptiveExtension();
+
+ Map<String, String> map = new HashMap<String, String>();
+ URL url = new URL("impl1", "1.2.3.4", 1010, "path1", map);
+ String yell = ext.yell(url, "s");
+ assertEquals("Ext2Impl1-yell", yell);
+
+ url = url.addParameter("key1", "impl2");
+ yell = ext.yell(url, "s");
+ assertEquals("Ext2Impl2-yell", yell);
+ }
+
+ @Test
+ public void test_urlHolder_getAdaptiveExtension() throws Exception {
+ Ext2 ext = ExtensionLoader.getExtensionLoader(Ext2.class).getAdaptiveExtension();
+
+ Map<String, String> map = new HashMap<String, String>();
+ map.put("ext2", "impl1");
+ URL url = new URL("p1", "1.2.3.4", 1010, "path1", map);
+
+ UrlHolder holder = new UrlHolder();
+ holder.setUrl(url);
+
+ String echo = ext.echo(holder, "haha");
+ assertEquals("Ext2Impl1-echo", echo);
+ }
+
+ @Test
+ public void test_urlHolder_getAdaptiveExtension_noExtension() throws Exception {
+ Ext2 ext = ExtensionLoader.getExtensionLoader(Ext2.class).getAdaptiveExtension();
+
+ URL url = new URL("p1", "1.2.3.4", 1010, "path1");
+
+ UrlHolder holder = new UrlHolder();
+ holder.setUrl(url);
+
+ try {
+ ext.echo(holder, "haha");
+ fail();
+ } catch (IllegalStateException expected) {
+ assertThat(expected.getMessage(), containsString("Fail to get extension("));
+ }
+
+ url = url.addParameter("ext2", "XXX");
+ holder.setUrl(url);
+ try {
+ ext.echo(holder, "haha");
+ fail();
+ } catch (IllegalStateException expected) {
+ assertThat(expected.getMessage(), containsString("No such extension"));
+ }
+ }
+
+ @Test
+ public void test_urlHolder_getAdaptiveExtension_UrlNpe() throws Exception {
+ Ext2 ext = ExtensionLoader.getExtensionLoader(Ext2.class).getAdaptiveExtension();
+
+ try {
+ ext.echo(null, "haha");
+ fail();
+ } catch (IllegalArgumentException e) {
+ assertEquals("com.alibaba.dubbo.common.extensionloader.ext2.UrlHolder argument == null", e.getMessage());
+ }
+
+ try {
+ ext.echo(new UrlHolder(), "haha");
+ fail();
+ } catch (IllegalArgumentException e) {
+ assertEquals("com.alibaba.dubbo.common.extensionloader.ext2.UrlHolder argument getUrl() == null", e.getMessage());
+ }
+ }
+
+ @Test
+ public void test_urlHolder_getAdaptiveExtension_ExceptionWhenNotAdativeMethod() throws Exception {
+ Ext2 ext = ExtensionLoader.getExtensionLoader(Ext2.class).getAdaptiveExtension();
+
+ Map<String, String> map = new HashMap<String, String>();
+ URL url = new URL("p1", "1.2.3.4", 1010, "path1", map);
+
+ try {
+ ext.bang(url, 33);
+ fail();
+ } catch (UnsupportedOperationException expected) {
+ assertThat(expected.getMessage(), containsString("method "));
+ assertThat(
+ expected.getMessage(),
+ containsString("of interface com.alibaba.dubbo.common.extensionloader.ext2.Ext2 is not adaptive method!"));
+ }
+ }
+
+ @Test
+ public void test_urlHolder_getAdaptiveExtension_ExceptionWhenNameNotProvided() throws Exception {
+ Ext2 ext = ExtensionLoader.getExtensionLoader(Ext2.class).getAdaptiveExtension();
+
+ URL url = new URL("p1", "1.2.3.4", 1010, "path1");
+
+ UrlHolder holder = new UrlHolder();
+ holder.setUrl(url);
+
+ try {
+ ext.echo(holder, "impl1");
+ fail();
+ } catch (IllegalStateException expected) {
+ assertThat(expected.getMessage(), containsString("Fail to get extension("));
+ }
+
+ url = url.addParameter("key1", "impl1");
+ holder.setUrl(url);
+ try {
+ ext.echo(holder, "haha");
+ fail();
+ } catch (IllegalStateException expected) {
+ assertThat(expected.getMessage(), containsString("Fail to get extension(com.alibaba.dubbo.common.extensionloader.ext2.Ext2) name from url"));
+ }
+ }
+
+ @Test
+ public void test_getAdaptiveExtension_inject() throws Exception {
+ 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"));
+
+ 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());
+ }
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext1/Ext1.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext1/Ext1.java
new file mode 100644
index 0000000..3744f08
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext1/Ext1.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.extensionloader.ext1;
+
+import com.alibaba.dubbo.common.Adaptive;
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.common.URL;
+
+/**
+ * @author ding.lid
+ */
+@Extension("impl1")
+public interface Ext1 {
+ @Adaptive
+ String echo(URL url, String s);
+
+ @Adaptive({"key1", "key2"})
+ String yell(URL url, String s);
+
+ String bang(URL url, int i);
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext1/impl/Ext1Impl1.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext1/impl/Ext1Impl1.java
new file mode 100644
index 0000000..f783345
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext1/impl/Ext1Impl1.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.extensionloader.ext1.impl;
+
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.extensionloader.ext1.Ext1;
+
+/**
+ * @author ding.lid
+ */
+@Extension(value = "impl1")
+public class Ext1Impl1 implements Ext1 {
+ public String echo(URL url, String s) {
+ return "Ext1Impl1-echo";
+ }
+
+ public String yell(URL url, String s) {
+ return "Ext1Impl1-yell";
+ }
+
+ public String bang(URL url, int i) {
+ return "bang1";
+ }
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext1/impl/Ext1Impl2.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext1/impl/Ext1Impl2.java
new file mode 100644
index 0000000..2e76cee
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext1/impl/Ext1Impl2.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.extensionloader.ext1.impl;
+
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.extensionloader.ext1.Ext1;
+
+/**
+ * @author ding.lid
+ *
+ */
+@Extension("impl2")
+public class Ext1Impl2 implements Ext1 {
+ public String echo(URL url, String s) {
+ return "Ext1Impl2-echo";
+ }
+
+ public String yell(URL url, String s) {
+ return "Ext1Impl2-yell";
+ }
+
+ public String bang(URL url, int i) {
+ return "bang2";
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext1/impl/Ext1Impl3.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext1/impl/Ext1Impl3.java
new file mode 100644
index 0000000..e3590ec
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext1/impl/Ext1Impl3.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.extensionloader.ext1.impl;
+
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.extensionloader.ext1.Ext1;
+
+/**
+ * @author ding.lid
+ *
+ */
+@Extension("impl3")
+public class Ext1Impl3 implements Ext1 {
+ public String echo(URL url, String s) {
+ return "Ext1Impl3-echo";
+ }
+
+ public String yell(URL url, String s) {
+ return "Ext1Impl3-yell";
+ }
+
+ public String bang(URL url, int i) {
+ return "bang3";
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext2/Ext2.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext2/Ext2.java
new file mode 100644
index 0000000..9a29f2d
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext2/Ext2.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.extensionloader.ext2;
+
+import com.alibaba.dubbo.common.Adaptive;
+import com.alibaba.dubbo.common.URL;
+
+/**
+ * 无Default
+ *
+ * @author ding.lid
+ */
+public interface Ext2 {
+ @Adaptive
+ String echo(UrlHolder holder, String s);
+
+ @Adaptive({"key1", "protocol"})
+ String yell(URL url, String s);
+
+ String bang(URL url, int i);
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext2/UrlHolder.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext2/UrlHolder.java
new file mode 100644
index 0000000..4c8aa82
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext2/UrlHolder.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.extensionloader.ext2;
+
+import com.alibaba.dubbo.common.URL;
+
+/**
+ * @author ding.lid
+ *
+ */
+public class UrlHolder {
+ private Double Num;
+
+ private URL url;
+
+ private String name;
+
+ private int age;
+
+ public Double getNum() {
+ return Num;
+ }
+
+ public void setNum(Double num) {
+ Num = num;
+ }
+
+ public URL getUrl() {
+ return url;
+ }
+
+ public void setUrl(URL url) {
+ this.url = url;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public int getAge() {
+ return age;
+ }
+
+ public void setAge(int age) {
+ this.age = age;
+ }
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext2/impl/Ext2Impl1.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext2/impl/Ext2Impl1.java
new file mode 100644
index 0000000..2abe4e2
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext2/impl/Ext2Impl1.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.extensionloader.ext2.impl;
+
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.extensionloader.ext2.Ext2;
+import com.alibaba.dubbo.common.extensionloader.ext2.UrlHolder;
+
+/**
+ * @author ding.lid
+ */
+@Extension(value = "impl1")
+public class Ext2Impl1 implements Ext2 {
+ public String echo(UrlHolder holder, String s) {
+ return "Ext2Impl1-echo";
+ }
+
+ public String yell(URL url, String s) {
+ return "Ext2Impl1-yell";
+ }
+
+ public String bang(URL url, int i) {
+ return "bang1";
+ }
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext2/impl/Ext2Impl2.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext2/impl/Ext2Impl2.java
new file mode 100644
index 0000000..51b8202
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext2/impl/Ext2Impl2.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.extensionloader.ext2.impl;
+
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.extensionloader.ext2.Ext2;
+import com.alibaba.dubbo.common.extensionloader.ext2.UrlHolder;
+
+/**
+ * @author ding.lid
+ *
+ */
+@Extension("impl2")
+public class Ext2Impl2 implements Ext2 {
+ public String echo(UrlHolder holder, String s) {
+ return "Ext2Impl2-echo";
+ }
+
+ public String yell(URL url, String s) {
+ return "Ext2Impl2-yell";
+ }
+
+ public String bang(URL url, int i) {
+ return "bang2";
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext2/impl/Ext2Impl3.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext2/impl/Ext2Impl3.java
new file mode 100644
index 0000000..f51998f
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext2/impl/Ext2Impl3.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.extensionloader.ext2.impl;
+
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.extensionloader.ext2.Ext2;
+import com.alibaba.dubbo.common.extensionloader.ext2.UrlHolder;
+
+/**
+ * @author ding.lid
+ *
+ */
+@Extension("impl3")
+public class Ext2Impl3 implements Ext2 {
+ public String echo(UrlHolder holder, String s) {
+ return "Ext2Impl3-echo";
+ }
+
+ public String yell(URL url, String s) {
+ return "Ext2Impl3-yell";
+ }
+
+ public String bang(URL url, int i) {
+ return "bang3";
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext3/Ext3.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext3/Ext3.java
new file mode 100644
index 0000000..ecdd3cc
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext3/Ext3.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.extensionloader.ext3;
+
+import com.alibaba.dubbo.common.Adaptive;
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.common.URL;
+
+/**
+ * @author ding.lid
+ */
+@Extension("impl3")
+public interface Ext3 {
+ @Adaptive({"key1", "protocol"})
+ String echo(URL url, String s);
+
+ @Adaptive({"protocol", "key2"})
+ String yell(URL url, String s);
+
+ String bang(URL url, int i);
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext3/impl/Ext3Impl1.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext3/impl/Ext3Impl1.java
new file mode 100644
index 0000000..d25174e
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext3/impl/Ext3Impl1.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.extensionloader.ext3.impl;
+
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.extensionloader.ext3.Ext3;
+
+/**
+ * @author ding.lid
+ */
+@Extension(value = "impl1")
+public class Ext3Impl1 implements Ext3 {
+ public String echo(URL url, String s) {
+ return "Ext3Impl3-echo";
+ }
+
+ public String yell(URL url, String s) {
+ return "Ext3Impl3-yell";
+ }
+
+ public String bang(URL url, int i) {
+ return "bang1";
+ }
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext3/impl/Ext3Impl2.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext3/impl/Ext3Impl2.java
new file mode 100644
index 0000000..678d546
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext3/impl/Ext3Impl2.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.extensionloader.ext3.impl;
+
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.extensionloader.ext3.Ext3;
+
+/**
+ * @author ding.lid
+ *
+ */
+@Extension("impl2")
+public class Ext3Impl2 implements Ext3 {
+ public String echo(URL url, String s) {
+ return "Ext3Impl2-echo";
+ }
+
+ public String yell(URL url, String s) {
+ return "Ext3Impl2-yell";
+ }
+
+ public String bang(URL url, int i) {
+ return "bang2";
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext3/impl/Ext3Impl3.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext3/impl/Ext3Impl3.java
new file mode 100644
index 0000000..90c30bb
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext3/impl/Ext3Impl3.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.extensionloader.ext3.impl;
+
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.extensionloader.ext3.Ext3;
+
+/**
+ * @author ding.lid
+ *
+ */
+@Extension(value="impl3")
+public class Ext3Impl3 implements Ext3 {
+ public String echo(URL url, String s) {
+ return "Ext3Impl3-echo";
+ }
+
+ public String yell(URL url, String s) {
+ return "Ext3Impl3-yell";
+ }
+
+ public String bang(URL url, int i) {
+ return "bang3";
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext4/Ext4.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext4/Ext4.java
new file mode 100644
index 0000000..76685d3
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext4/Ext4.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.extensionloader.ext4;
+
+import java.util.List;
+
+import com.alibaba.dubbo.common.Adaptive;
+import com.alibaba.dubbo.common.Extension;
+
+/**
+ * @author ding.lid
+ */
+@Extension("impl1")
+public interface Ext4 {
+ @Adaptive
+ String bark(String name, List<Object> list); // 没有URL参数的方法
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext4/impl/Ext4Impl1.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext4/impl/Ext4Impl1.java
new file mode 100644
index 0000000..9df9d0b
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext4/impl/Ext4Impl1.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.extensionloader.ext4.impl;
+
+import java.util.List;
+
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.common.extensionloader.ext4.Ext4;
+
+/**
+ * @author ding.lid
+ */
+@Extension(value = "impl1")
+public class Ext4Impl1 implements Ext4 {
+ public String bark(String name, List<Object> list) {
+ return null;
+ }
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext4/impl/Ext4Impl2.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext4/impl/Ext4Impl2.java
new file mode 100644
index 0000000..f3a8c14
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext4/impl/Ext4Impl2.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.extensionloader.ext4.impl;
+
+import java.util.List;
+
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.extensionloader.ext4.Ext4;
+
+/**
+ * @author ding.lid
+ *
+ */
+@Extension("impl2")
+public class Ext4Impl2 implements Ext4 {
+ public String echo(URL url, String s) {
+ return "Ext3Impl2-echo";
+ }
+
+ public String yell(URL url, String s) {
+ return "Ext3Impl2-yell";
+ }
+
+ public String bang(URL url, int i) {
+ return "bang2";
+ }
+
+ public String bark(String name, List<Object> list) {
+ return null;
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext5/Ext5NoAdaptiveMethod.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext5/Ext5NoAdaptiveMethod.java
new file mode 100644
index 0000000..a347956
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext5/Ext5NoAdaptiveMethod.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.extensionloader.ext5;
+
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.common.URL;
+
+/**
+ * @author ding.lid
+ */
+@Extension("impl1")
+public interface Ext5NoAdaptiveMethod {
+ String echo(URL url, String s);
+
+ String yell(URL url, String s);
+
+ String bang(URL url, int i);
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext5/impl/Ext5Impl1.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext5/impl/Ext5Impl1.java
new file mode 100644
index 0000000..762edf0
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext5/impl/Ext5Impl1.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.extensionloader.ext5.impl;
+
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.extensionloader.ext5.Ext5NoAdaptiveMethod;
+
+/**
+ * @author ding.lid
+ */
+@Extension("impl1")
+public class Ext5Impl1 implements Ext5NoAdaptiveMethod {
+ public String echo(URL url, String s) {
+ return "Ext5Impl1-echo";
+ }
+
+ public String yell(URL url, String s) {
+ return "Ext5Impl1-yell";
+ }
+
+ public String bang(URL url, int i) {
+ return "impl1";
+ }
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext5/impl/Ext5Impl2.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext5/impl/Ext5Impl2.java
new file mode 100644
index 0000000..f517a04
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext5/impl/Ext5Impl2.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.extensionloader.ext5.impl;
+
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.extensionloader.ext5.Ext5NoAdaptiveMethod;
+
+/**
+ * @author ding.lid
+ *
+ */
+@Extension("impl2")
+public class Ext5Impl2 implements Ext5NoAdaptiveMethod {
+ public String echo(URL url, String s) {
+ return "Ext5Impl2-echo";
+ }
+
+ public String yell(URL url, String s) {
+ return "Ext5Impl2-yell";
+ }
+
+ public String bang(URL url, int i) {
+ return "impl2";
+ }
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext5/impl/Ext5Wrapper1.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext5/impl/Ext5Wrapper1.java
new file mode 100644
index 0000000..f6b0125
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext5/impl/Ext5Wrapper1.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.extensionloader.ext5.impl;
+
+import java.util.concurrent.atomic.AtomicInteger;
+
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.extensionloader.ext5.Ext5NoAdaptiveMethod;
+
+/**
+ * @author ding.lid
+ */
+@Extension("XXX")
+public class Ext5Wrapper1 implements Ext5NoAdaptiveMethod {
+ Ext5NoAdaptiveMethod instance;
+
+ public static AtomicInteger echoCount = new AtomicInteger();
+ public static AtomicInteger yellCount = new AtomicInteger();
+ public static AtomicInteger bangCount = new AtomicInteger();
+
+ public Ext5Wrapper1(Ext5NoAdaptiveMethod instance) {
+ this.instance = instance;
+ }
+
+ public String echo(URL url, String s) {
+ echoCount.incrementAndGet();
+ return instance.echo(url, s);
+ }
+
+ public String yell(URL url, String s) {
+ yellCount.incrementAndGet();
+ return instance.yell(url, s);
+ }
+
+ public String bang(URL url, int i) {
+ bangCount.incrementAndGet();
+ return instance.bang(url, i);
+ }
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext5/impl/Ext5Wrapper2.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext5/impl/Ext5Wrapper2.java
new file mode 100644
index 0000000..ffb1dcb
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext5/impl/Ext5Wrapper2.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.extensionloader.ext5.impl;
+
+import java.util.concurrent.atomic.AtomicInteger;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.extensionloader.ext5.Ext5NoAdaptiveMethod;
+
+/**
+ * @author ding.lid
+ */
+public class Ext5Wrapper2 implements Ext5NoAdaptiveMethod {
+ Ext5NoAdaptiveMethod instance;
+
+ public static AtomicInteger echoCount = new AtomicInteger();
+ public static AtomicInteger yellCount = new AtomicInteger();
+ public static AtomicInteger bangCount = new AtomicInteger();
+
+ public Ext5Wrapper2(Ext5NoAdaptiveMethod instance) {
+ this.instance = instance;
+ }
+
+ public String echo(URL url, String s) {
+ echoCount.incrementAndGet();
+ return instance.echo(url, s);
+ }
+
+ public String yell(URL url, String s) {
+ yellCount.incrementAndGet();
+ return instance.yell(url, s);
+ }
+
+ public String bang(URL url, int i) {
+ bangCount.incrementAndGet();
+ return instance.bang(url, i);
+ }
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext6_inject/Ext6.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext6_inject/Ext6.java
new file mode 100644
index 0000000..7990f45
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext6_inject/Ext6.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.extensionloader.ext6_inject;
+
+import com.alibaba.dubbo.common.Adaptive;
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.common.URL;
+
+/**
+ * 无Default
+ *
+ * @author ding.lid
+ */
+@Extension
+public interface Ext6 {
+ @Adaptive
+ String echo(URL url, String s);
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext6_inject/impl/Ext6Impl1.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext6_inject/impl/Ext6Impl1.java
new file mode 100644
index 0000000..6da0c3c
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext6_inject/impl/Ext6Impl1.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.ext6_inject.impl;
+
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.extensionloader.ext1.Ext1;
+import com.alibaba.dubbo.common.extensionloader.ext6_inject.Ext6;
+
+/**
+ * @author ding.lid
+ */
+@Extension(value = "impl1")
+public class Ext6Impl1 implements Ext6 {
+ Ext1 ext1;
+
+ public void setExt1(Ext1 ext1) {
+ this.ext1 = ext1;
+ }
+
+ public String echo(URL url, String s) {
+ return "Ext6Impl1-echo-" + ext1.echo(url, s);
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext6_inject/impl/Ext6Impl2.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext6_inject/impl/Ext6Impl2.java
new file mode 100644
index 0000000..35ad0cd
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/extensionloader/ext6_inject/impl/Ext6Impl2.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.extensionloader.ext6_inject.impl;
+
+import java.util.List;
+
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.extensionloader.ext6_inject.Ext6;
+
+/**
+ * @author ding.lid
+ */
+@Extension("impl2")
+public class Ext6Impl2 implements Ext6 {
+ List<String> list;
+
+ public List<String> getList() {
+ return list;
+ }
+
+ public void setList(List<String> list) {
+ this.list = list;
+ }
+
+ public String echo(URL url, String s) {
+ throw new UnsupportedOperationException();
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/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..6044601
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/serialize/dubbo/BuilderTest.java
@@ -0,0 +1,525 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.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");
+ }
+
+ @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..2ca0979
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/serialize/serialization/DubboSerializationTest.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.serialize.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 {}
+
+ // FIXME DUBBO-63
+ @Ignore
+ @Test
+ public void test_URL_mutable_withType() throws Exception {}
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/serialize/serialization/FastJsonSerializationTest.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/serialize/serialization/FastJsonSerializationTest.java
new file mode 100644
index 0000000..cd30285
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/serialize/serialization/FastJsonSerializationTest.java
@@ -0,0 +1,266 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.serialize.serialization;
+
+import static org.junit.Assert.fail;
+
+import java.io.ByteArrayInputStream;
+
+import org.junit.Ignore;
+import org.junit.Test;
+
+import com.alibaba.dubbo.common.model.media.MediaContent;
+import com.alibaba.dubbo.common.serialize.ObjectInput;
+import com.alibaba.dubbo.common.serialize.ObjectOutput;
+import com.alibaba.dubbo.common.serialize.support.json.FastJsonSerialization;
+import com.alibaba.fastjson.JSONException;
+
+/**
+ * FIXME FastJson Serialization的失败,被暂时被忽略
+ *
+ * @author ding.lid
+ */
+public class FastJsonSerializationTest extends AbstractSerializationPersionOkTest {
+ {
+ serialization = new FastJsonSerialization();
+ }
+
+ @Ignore // FIXME
+ @Test
+ public void test_BytesRange() throws Exception {}
+
+ @Ignore("bool[] type missing to JSONArray")
+ @Test
+ public void test_boolArray() throws Exception {}
+
+ @Ignore("FastJson bug: com.alibaba.fastjson.JSONException: create asm serilizer error, class char")
+ @Test
+ public void test_charArray() throws Exception {}
+
+ @Ignore("FastJson bug: com.alibaba.fastjson.JSONException: create asm serilizer error, class char")
+ @Test
+ public void test_charArray_withType() throws Exception {}
+
+ @Ignore("short[] type missing to JSONArray")
+ @Test
+ public void test_shortArray() throws Exception {}
+
+ @Ignore("int[] type missing to JSONArray")
+ @Test
+ public void test_intArray() throws Exception {}
+
+ @Ignore("long[] type missing to JSONArray")
+ @Test
+ public void test_longArray() throws Exception {}
+
+ @Ignore("float[] type missing to JSONArray")
+ @Test
+ public void test_floatArray() throws Exception {}
+
+ @Ignore("double[] type missing to JSONArray")
+ @Test
+ public void test_doubleArray() throws Exception {}
+
+ @Ignore("String[] type missing to JSONArray")
+ @Test
+ public void test_StringArray() throws Exception {}
+
+ @Ignore("Integer[] type missing to JSONArray")
+ @Test
+ public void test_IntegerArray() throws Exception {}
+
+ @Ignore("Integer[] type missing to JSONArray")
+ @Test
+ public void test_EnumArray() throws Exception {}
+
+ @Ignore("type mising to Long")
+ @Test
+ public void test_Date() throws Exception {}
+
+ @Ignore("type mising to Long")
+ @Test
+ public void test_Time() throws Exception {}
+
+ @Ignore("com.alibaba.fastjson.JSONException: create asm deserializer error, java.sql.Time")
+ @Test
+ public void test_Time_withType() throws Exception {}
+
+ @Ignore("type mising to Integer")
+ @Test
+ public void test_ByteWrap() throws Exception {}
+
+ @Ignore("type mising to Integer")
+ @Test
+ public void test_LongWrap() throws Exception {}
+
+ @Ignore("type mising to Long")
+ @Test
+ public void test_BigInteger() throws Exception {}
+
+ @Ignore("SPerson type missing")
+ @Test
+ public void test_SPerson() throws Exception {}
+
+ @Ignore("BizException type missing to Map")
+ @Test
+ public void test_BizException() throws Exception {}
+
+ @Ignore("BizExceptionNoDefaultConstructor type missing to Map")
+ @Test
+ public void test_BizExceptionNoDefaultConstructor() throws Exception {}
+
+ // FIXME 没有缺省构造函数失败
+ @Ignore("NoDefaultConstructor")
+ @Test
+ public void test_BizExceptionNoDefaultConstructor_WithType() throws Exception {}
+
+ @Ignore("Enum type missing to String")
+ @Test
+ public void test_enum() throws Exception {}
+
+ @Ignore("String set missing to JSONArray")
+ @Test
+ public void test_StringSet() throws Exception {}
+
+ @Ignore("LinkedHashMap type missing to Map")
+ @Test
+ public void test_LinkedHashMap() throws Exception {}
+
+
+ @Ignore("person type missing")
+ @Test
+ public void test_SPersonList() throws Exception {}
+
+ @Ignore("person type missing")
+ @Test
+ public void test_SPersonSet() throws Exception {}
+
+ @Ignore("FastJson bug: com.alibaba.fastjson.JSONException: illegal identifier : 1")
+ @Test
+ public void test_IntSPersonMap() throws Exception {}
+
+ @Ignore("person type missing")
+ @Test
+ public void test_StringSPersonMap() throws Exception {}
+
+ @Ignore("person type missing")
+ @Test
+ public void test_StringSPersonListMap() throws Exception {}
+
+ @Ignore("person type missing")
+ @Test
+ public void test_SPersonListList() throws Exception {}
+
+ @Ignore("BigPerson type missing")
+ @Test
+ public void test_BigPerson() throws Exception {}
+
+ @Ignore("MediaContent type missing")
+ @Test
+ public void test_MediaContent() throws Exception {}
+
+ @Ignore("type missing")
+ @Test
+ public void test_MultiObject() throws Exception {}
+
+ @Test
+ public void test_MediaContent_badStream() throws Exception {
+ ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+ objectOutput.writeObject(mediaContent);
+ objectOutput.flushBuffer();
+
+ byte[] byteArray = byteArrayOutputStream.toByteArray();
+ for (int i = 0; i < byteArray.length; i++) {
+ if(i%3 == 0) {
+ byteArray[i] = (byte)~byteArray[i];
+ }
+ }
+ ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArray);
+
+ try {
+ ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
+ @SuppressWarnings("unused") // local variable, convenient for debug
+ Object read = deserialize.readObject();
+ fail();
+ } catch (JSONException expected) {
+ System.out.println(expected);
+ }
+ }
+
+ @Test
+ public void test_MediaContent_WithType_badStream() throws Exception {
+ ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+ objectOutput.writeObject(mediaContent);
+ objectOutput.flushBuffer();
+
+ byte[] byteArray = byteArrayOutputStream.toByteArray();
+ for (int i = 0; i < byteArray.length; i++) {
+ if(i%3 == 0) {
+ byteArray[i] = (byte)~byteArray[i];
+ }
+ }
+ ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArray);
+
+ try {
+ ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
+ @SuppressWarnings("unused") // local variable, convenient for debug
+ Object read = deserialize.readObject(MediaContent.class);
+ fail();
+ } catch (JSONException expected) {
+ System.out.println(expected);
+ }
+ }
+
+ // FIXME DUBBO-63
+ @Ignore
+ @Test
+ public void test_URL_mutable_withType() throws Exception {}
+
+ @Ignore
+ @Test(timeout=3000)
+ public void test_LoopReference() throws Exception {}
+
+ // ========== Person
+
+ @Ignore("person type missing")
+ @Test
+ public void test_Person() throws Exception {}
+
+ @Ignore("person type missing")
+ @Test
+ public void test_PersonList() throws Exception {}
+
+ @Ignore("person type missing")
+ @Test
+ public void test_PersonSet() throws Exception {}
+
+ @Ignore("FastJson bug: com.alibaba.fastjson.JSONException: illegal identifier : 1")
+ @Test
+ public void test_IntPersonMap() throws Exception {}
+
+ @Ignore("person type missing")
+ @Test
+ public void test_StringPersonMap() throws Exception {}
+
+ @Ignore("person type missing")
+ @Test
+ public void test_StringPersonListMap() throws Exception {}
+
+ @Ignore("person type missing")
+ @Test
+ public void test_PersonListList() throws Exception {}
+
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/serialize/serialization/Hessian2SerializationTest.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/serialize/serialization/Hessian2SerializationTest.java
new file mode 100644
index 0000000..2e20c49
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/serialize/serialization/Hessian2SerializationTest.java
@@ -0,0 +1,229 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.serialize.serialization;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.io.ByteArrayInputStream;
+import java.util.Arrays;
+
+import org.junit.Ignore;
+import org.junit.Test;
+
+import com.alibaba.dubbo.common.serialize.ObjectInput;
+import com.alibaba.dubbo.common.serialize.ObjectOutput;
+import com.alibaba.dubbo.common.serialize.support.hessian.Hessian2Serialization;
+
+/**
+ * @author ding.lid
+ *
+ */
+public class Hessian2SerializationTest extends AbstractSerializationPersionFailTest {
+ {
+ serialization = new Hessian2Serialization();
+ }
+
+ // Hessian2
+
+ @Test
+ public void test_boolArray_withType() throws Exception {
+ boolean[] data = new boolean[] { true, false, true};
+
+ ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+ objectOutput.writeObject(data);
+ objectOutput.flushBuffer();
+
+ ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
+ byteArrayOutputStream.toByteArray());
+ ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
+
+ assertTrue(Arrays.equals(data, (boolean[]) deserialize.readObject(boolean[].class)));
+
+ try {
+ deserialize.readObject(boolean[].class);
+ fail();
+ }
+ catch (ArrayIndexOutOfBoundsException e) {}
+ // NOTE: Hessian2抛出了ArrayIndexOutOfBoundsException 而不是 IOException!!
+ // 容忍这个问题!!
+ }
+
+ @Ignore("type missing, char[] -> String")
+ @Test
+ public void test_charArray() throws Exception {}
+
+ @Test
+ public void test_shortArray_withType() throws Exception {
+ short[] data = new short[] { 37, 39, 12 };
+
+ ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+ objectOutput.writeObject(data);
+ objectOutput.flushBuffer();
+
+ ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
+ byteArrayOutputStream.toByteArray());
+ ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
+
+ assertArrayEquals(data, (short[]) deserialize.readObject(short[].class));
+
+ try {
+ deserialize.readObject(short[].class);
+ fail();
+ }
+ catch (ArrayIndexOutOfBoundsException e) {}
+ // NOTE: Hessian2抛出了ArrayIndexOutOfBoundsException 而不是 IOException!!
+ // 容忍这个问题!!
+ }
+
+ @Test
+ public void test_intArray_withType() throws Exception {
+ int[] data = new int[] { 234, 0, -1};
+
+ ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+ objectOutput.writeObject(data);
+ objectOutput.flushBuffer();
+
+ ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
+ byteArrayOutputStream.toByteArray());
+ ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
+
+ assertArrayEquals(data, (int[]) deserialize.readObject());
+
+ try {
+ deserialize.readObject(int[].class);
+ fail();
+ }
+ catch (ArrayIndexOutOfBoundsException e) {}
+ // NOTE: Hessian2抛出了ArrayIndexOutOfBoundsException 而不是 IOException!!
+ // 容忍这个问题!!
+ }
+
+ @Test
+ public void test_longArray_withType() throws Exception {
+ long[] data = new long[] { 234, 0, -1};
+
+ ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+ objectOutput.writeObject(data);
+ objectOutput.flushBuffer();
+
+ ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
+ byteArrayOutputStream.toByteArray());
+ ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
+
+ assertArrayEquals(data, (long[]) deserialize.readObject());
+
+ try {
+ deserialize.readObject(long[].class);
+ fail();
+ }
+ catch (ArrayIndexOutOfBoundsException e) {}
+ // NOTE: Hessian2抛出了ArrayIndexOutOfBoundsException 而不是 IOException!!
+ // 容忍这个问题!!
+ }
+
+ @Test
+ public void test_floatArray_withType() throws Exception {
+ float[] data = new float[] { 37F, -3.14F, 123456.7F };
+
+ ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+ objectOutput.writeObject(data);
+ objectOutput.flushBuffer();
+
+ ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
+ byteArrayOutputStream.toByteArray());
+ ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
+
+ assertArrayEquals(data, (float[]) deserialize.readObject(), 0.0001F);
+
+ try {
+ deserialize.readObject(float[].class);
+ fail();
+ }
+ catch (ArrayIndexOutOfBoundsException e) {}
+ // NOTE: Hessian2抛出了ArrayIndexOutOfBoundsException 而不是 IOException!!
+ // 容忍这个问题!!
+ }
+
+ @Test
+ public void test_doubleArray_withType() throws Exception {
+ double[] data = new double[] { 37D, -3.14D, 123456.7D };
+
+ ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+ objectOutput.writeObject(data);
+ objectOutput.flushBuffer();
+
+ ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
+ byteArrayOutputStream.toByteArray());
+ ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
+
+ assertArrayEquals(data, (double[]) deserialize.readObject(double[].class), 0.0001);
+
+ try {
+ deserialize.readObject(double[].class);
+ fail();
+ }
+ catch (ArrayIndexOutOfBoundsException e) {}
+ // NOTE: Hessian2抛出了ArrayIndexOutOfBoundsException 而不是 IOException!!
+ // 容忍这个问题!!
+ }
+
+ @Test
+ public void test_StringArray_withType() throws Exception {
+
+ String[] data = new String[] { "1", "b" };
+
+
+ ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+ objectOutput.writeObject(data);
+ objectOutput.flushBuffer();
+
+ ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
+ byteArrayOutputStream.toByteArray());
+ ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
+
+ assertArrayEquals(data, deserialize.readObject(String[].class));
+
+ try {
+ deserialize.readObject(String[].class);
+ fail();
+ }
+ catch (ArrayIndexOutOfBoundsException e) {}
+ // NOTE: Hessian2抛出了ArrayIndexOutOfBoundsException 而不是 IOException!!
+ // 容忍这个问题!!
+ }
+
+ @Ignore("type missing, Byte -> Integer")
+ @Test
+ public void test_ByteWrap() throws Exception { }
+
+
+ // FIXME
+ @Ignore("com.alibaba.com.caucho.hessian.io.HessianProtocolException: 'java.math.BigInteger' could not be instantiated")
+ @Test
+ public void test_BigInteger() throws Exception {}
+
+ // FIXME
+ @Ignore("com.alibaba.com.caucho.hessian.io.HessianProtocolException: 'java.math.BigInteger' could not be instantiated")
+ @Test
+ public void test_BigInteger_withType() throws Exception {}
+
+ // FIXME
+ @Ignore("Bad Stream read other type data")
+ @Test
+ public void test_MediaContent_badStream() throws Exception {}
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/serialize/serialization/JavaSerializationTest.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/serialize/serialization/JavaSerializationTest.java
new file mode 100644
index 0000000..46f7c57
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/serialize/serialization/JavaSerializationTest.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.serialize.serialization;
+
+import com.alibaba.dubbo.common.serialize.support.java.JavaSerialization;
+
+/**
+ * @author ding.lid
+ *
+ */
+public class JavaSerializationTest extends AbstractSerializationPersionFailTest {
+ {
+ serialization = new JavaSerialization();
+ }
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/serialize/serialization/JsonSerializationTest.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/serialize/serialization/JsonSerializationTest.java
new file mode 100644
index 0000000..3fff4e0
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/serialize/serialization/JsonSerializationTest.java
@@ -0,0 +1,227 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.serialize.serialization;
+
+import org.junit.Ignore;
+import org.junit.Test;
+
+import com.alibaba.dubbo.common.serialize.support.json.JsonSerialization;
+
+/**
+ * FIXME Json Serialization的失败,被暂时被忽略
+ *
+ * @author ding.lid
+ *
+ */
+public class JsonSerializationTest extends AbstractSerializationPersionOkTest {
+ {
+ serialization = new JsonSerialization();
+ }
+
+ // FIXME
+ @Ignore
+ @Test
+ public void test_BytesRange() throws Exception {}
+
+ @Ignore("bool[] type missing to List<Boolean>")
+ @Test
+ public void test_boolArray() throws Exception {}
+
+ @Ignore("char[] type missing to List<Charator>")
+ @Test
+ public void test_charArray() throws Exception {}
+
+ @Ignore("short[] type missing to List<Short>")
+ @Test
+ public void test_shortArray() throws Exception {}
+
+ @Ignore("int[] type missing to List<Integer>")
+ @Test
+ public void test_intArray() throws Exception {}
+
+ @Ignore("long[] type missing to List<Long>")
+ @Test
+ public void test_longArray() throws Exception {}
+
+ @Ignore
+ @Test
+ public void test_floatArray() throws Exception {}
+
+ @Ignore
+ @Test
+ public void test_doubleArray() throws Exception {}
+
+ @Ignore("String[] type missing to List<String>")
+ @Test
+ public void test_StringArray() throws Exception {}
+
+ @Ignore("Integer[] type missing to List<Integer>")
+ @Test
+ public void test_IntegerArray() throws Exception {}
+
+ @Ignore
+ @Test
+ public void test_EnumArray() throws Exception {}
+
+ @Ignore
+ @Test
+ public void test_Date() throws Exception {}
+
+ @Ignore("毫秒部分丢失成0")
+ @Test
+ public void test_Date_withType() throws Exception {}
+
+ @Ignore
+ @Test
+ public void test_Time() throws Exception {}
+
+ @Ignore
+ @Test
+ public void test_Time_withType() throws Exception {}
+
+ @Ignore("Byte type missing to Long")
+ @Test
+ public void test_ByteWrap() throws Exception {}
+
+ @Ignore("BigInteger type missing to Long")
+ @Test
+ public void test_BigInteger() throws Exception {}
+
+ @Ignore
+ @Test
+ public void test_BigDecimal() throws Exception {}
+
+ @Ignore
+ @Test
+ public void test_BigDecimal_withType() throws Exception {}
+
+ @Ignore("BizException type missing")
+ @Test
+ public void test_BizException() throws Exception {}
+
+ @Ignore("BizExceptionNoDefaultConstructor type missing")
+ @Test
+ public void test_BizExceptionNoDefaultConstructor() throws Exception {}
+
+ @Ignore("NoDefaultConstructor")
+ @Test
+ public void test_BizExceptionNoDefaultConstructor_WithType() throws Exception {}
+
+ @Ignore("Enum type missing")
+ @Test
+ public void test_enum() throws Exception {}
+
+ @Ignore("Set type missing to Map")
+ @Test
+ public void test_StringSet() throws Exception {}
+
+ @Ignore("LinkedHashMap type missing to Map")
+ @Test
+ public void test_LinkedHashMap() throws Exception {}
+
+ //
+
+ @Ignore("SPerson type missing")
+ @Test
+ public void test_SPerson() throws Exception {}
+
+ @Ignore("SPerson type missing")
+ @Test
+ public void test_SPersonList() throws Exception {}
+
+ @Ignore("SPerson type missing")
+ @Test
+ public void test_SPersonSet() throws Exception {}
+
+ @Ignore("SPerson type missing")
+ @Test
+ public void test_IntSPersonMap() throws Exception {}
+
+ @Ignore("SPerson type missing")
+ @Test
+ public void test_StringSPersonMap() throws Exception {}
+
+ @Ignore("SPerson type missing")
+ @Test
+ public void test_StringSPersonListMap() throws Exception {}
+
+ @Ignore("SPerson type missing")
+ @Test
+ public void test_SPersonListList() throws Exception {}
+
+ @Ignore("type missing")
+ @Test
+ public void test_BigPerson() throws Exception {}
+
+ @Ignore("type missing")
+ @Test
+ public void test_BigPerson_WithType() throws Exception {}
+
+ @Ignore("type missing")
+ @Test
+ public void test_MediaContent() throws Exception {}
+
+ @Ignore("type missing")
+ @Test
+ public void test_MediaContent_WithType() throws Exception {}
+
+ @Ignore("type missing")
+ @Test
+ public void test_MultiObject() throws Exception {}
+
+ @Ignore("type missing")
+ @Test
+ public void test_MultiObject_WithType() throws Exception {}
+
+ @Ignore
+ @Test
+ public void test_LoopReference() throws Exception {}
+
+ // FIXME DUBBO-63
+ @Ignore
+ @Test
+ public void test_URL_mutable_withType() throws Exception {}
+
+ // Person
+
+ @Ignore("person type missing")
+ @Test
+ public void test_Person() throws Exception {}
+
+ @Ignore("person type missing")
+ @Test
+ public void test_PersonList() throws Exception {}
+
+ @Ignore("person type missing")
+ @Test
+ public void test_PersonSet() throws Exception {}
+
+ @Ignore("person type missing")
+ @Test
+ public void test_IntPersonMap() throws Exception {}
+
+ @Ignore("person type missing")
+ @Test
+ public void test_StringPersonMap() throws Exception {}
+
+ @Ignore("person type missing")
+ @Test
+ public void test_StringPersonListMap() throws Exception {}
+
+ @Ignore("person type missing")
+ @Test
+ public void test_PersonListList() throws Exception {}
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/utils/AtomicPositiveIntegerTest.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/utils/AtomicPositiveIntegerTest.java
new file mode 100644
index 0000000..a776182
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/utils/AtomicPositiveIntegerTest.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.utils;
+
+import static org.hamcrest.CoreMatchers.allOf;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.fail;
+import static org.junit.matchers.JUnitMatchers.containsString;
+
+import org.junit.Test;
+
+/**
+ * @author ding.lid
+ */
+public class AtomicPositiveIntegerTest {
+ AtomicPositiveInteger i1 = new AtomicPositiveInteger();
+
+ AtomicPositiveInteger i2 = new AtomicPositiveInteger(127);
+
+ AtomicPositiveInteger i3 = new AtomicPositiveInteger(Integer.MAX_VALUE);
+
+ @Test
+ public void test_get() throws Exception {
+ assertEquals(0, i1.get());
+ assertEquals(127, i2.get());
+ assertEquals(Integer.MAX_VALUE, i3.get());
+ }
+
+ @Test
+ public void test_set() throws Exception {
+ i1.set(100);
+ assertEquals(100, i1.get());
+
+ try {
+ i1.set(-1);
+ fail();
+ } catch (IllegalArgumentException expected) {
+ assertThat(expected.getMessage(),
+ allOf(containsString("new value"), containsString("< 0")));
+ }
+ }
+
+ @Test
+ public void test_getAndIncrement() throws Exception {
+ int get = i1.getAndIncrement();
+ assertEquals(0, get);
+ assertEquals(1, i1.get());
+
+ get = i2.getAndIncrement();
+ assertEquals(127, get);
+ assertEquals(128, i2.get());
+
+ get = i3.getAndIncrement();
+ assertEquals(Integer.MAX_VALUE, get);
+ assertEquals(0, i3.get());
+ }
+
+ @Test
+ public void test_getAndDecrement() throws Exception {
+ int get = i1.getAndDecrement();
+ assertEquals(0, get);
+ assertEquals(Integer.MAX_VALUE, i1.get());
+
+ get = i2.getAndDecrement();
+ assertEquals(127, get);
+ assertEquals(126, i2.get());
+
+ get = i3.getAndDecrement();
+ assertEquals(Integer.MAX_VALUE, get);
+ assertEquals(Integer.MAX_VALUE - 1, i3.get());
+ }
+
+ @Test
+ public void test_incrementAndGet() throws Exception {
+ int get = i1.incrementAndGet();
+ assertEquals(1, get);
+ assertEquals(1, i1.get());
+
+ get = i2.incrementAndGet();
+ assertEquals(128, get);
+ assertEquals(128, i2.get());
+
+ get = i3.incrementAndGet();
+ assertEquals(0, get);
+ assertEquals(0, i3.get());
+ }
+
+ @Test
+ public void test_decrementAndGet() throws Exception {
+ int get = i1.decrementAndGet();
+ assertEquals(Integer.MAX_VALUE, get);
+ assertEquals(Integer.MAX_VALUE, i1.get());
+
+ get = i2.decrementAndGet();
+ assertEquals(126, get);
+ assertEquals(126, i2.get());
+
+ get = i3.decrementAndGet();
+ assertEquals(Integer.MAX_VALUE - 1, get);
+ assertEquals(Integer.MAX_VALUE - 1, i3.get());
+ }
+
+ @Test
+ public void test_getAndSet() throws Exception {
+ int get = i1.getAndSet(100);
+ assertEquals(0, get);
+ assertEquals(100, i1.get());
+
+ try {
+ i1.getAndSet(-1);
+ } catch (IllegalArgumentException expected) {
+ assertThat(expected.getMessage(),
+ allOf(containsString("new value"), containsString("< 0")));
+ }
+ }
+
+ @Test
+ public void test_getAndAnd() throws Exception {
+ int get = i1.getAndAdd(3);
+ assertEquals(0, get);
+ assertEquals(3, i1.get());
+
+ get = i2.getAndAdd(3);
+ assertEquals(127, get);
+ assertEquals(127 + 3, i2.get());
+
+ get = i3.getAndAdd(3);
+ assertEquals(Integer.MAX_VALUE, get);
+ assertEquals(2, i3.get());
+ }
+
+
+ @Test
+ public void test_addAndGet() throws Exception {
+ int get = i1.addAndGet(3);
+ assertEquals(3, get);
+ assertEquals(3, i1.get());
+
+ get = i2.addAndGet(3);
+ assertEquals(127 + 3, get);
+ assertEquals(127 + 3, i2.get());
+
+ get = i3.addAndGet(3);
+ assertEquals(2, get);
+ assertEquals(2, i3.get());
+ }
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/utils/CollectionUtilsTest.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/utils/CollectionUtilsTest.java
new file mode 100644
index 0000000..e8e6f24
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/utils/CollectionUtilsTest.java
@@ -0,0 +1,150 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.utils;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.*;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.junit.Test;
+
+/**
+ * @author ding.lid
+ */
+public class CollectionUtilsTest {
+ @Test
+ public void test_sort() throws Exception {
+ List<Integer> list = new ArrayList<Integer>();
+ list.add(100);
+ list.add(10);
+ list.add(20);
+
+ List<Integer> expected = new ArrayList<Integer>();
+ expected.add(10);
+ expected.add(20);
+ expected.add(100);
+
+ assertEquals(expected, CollectionUtils.sort(list));
+ }
+
+ @Test
+ public void test_sort_null() throws Exception {
+ assertNull(CollectionUtils.sort(null));
+
+ assertTrue(CollectionUtils.sort(new ArrayList<Integer>()).isEmpty());
+ }
+
+ @Test
+ public void test_sortSimpleName() throws Exception {
+ List<String> list = new ArrayList<String>();
+ list.add("aaa.z");
+ list.add("b");
+ list.add(null);
+ list.add("zzz.a");
+ list.add("c");
+ list.add(null);
+
+ List<String> sorted = CollectionUtils.sortSimpleName(list);
+ assertNull(sorted.get(0));
+ assertNull(sorted.get(1));
+ }
+
+ @Test
+ public void test_sortSimpleName_null() throws Exception {
+ assertNull(CollectionUtils.sortSimpleName(null));
+
+ assertTrue(CollectionUtils.sortSimpleName(new ArrayList<String>()).isEmpty());
+ }
+
+ @Test
+ public void test_splitAll() throws Exception {
+ assertNull(CollectionUtils.splitAll(null, null));
+ assertNull(CollectionUtils.splitAll(null, "-"));
+
+ assertTrue(CollectionUtils.splitAll(new HashMap<String, List<String>>(), "-").isEmpty());
+
+ Map<String, List<String>> input = new HashMap<String, List<String>>();
+ input.put("key1", Arrays.asList("1:a", "2:b", "3:c"));
+ input.put("key2", Arrays.asList("1:a", "2:b"));
+ input.put("key3", null);
+ input.put("key4", new ArrayList<String>());
+
+ Map<String, Map<String, String>> expected = new HashMap<String, Map<String, String>>();
+ expected.put("key1", CollectionUtils.toStringMap("1", "a", "2", "b", "3", "c"));
+ expected.put("key2", CollectionUtils.toStringMap("1", "a", "2", "b"));
+ expected.put("key3", null);
+ expected.put("key4", new HashMap<String, String>());
+
+ assertEquals(expected, CollectionUtils.splitAll(input, ":"));
+ }
+
+ @Test
+ public void test_joinAll() throws Exception {
+ assertNull(CollectionUtils.joinAll(null, null));
+ assertNull(CollectionUtils.joinAll(null, "-"));
+
+ Map<String, List<String>> expected = new HashMap<String, List<String>>();
+ expected.put("key1", Arrays.asList("1:a", "2:b", "3:c"));
+ expected.put("key2", Arrays.asList("1:a", "2:b"));
+ expected.put("key3", null);
+ expected.put("key4", new ArrayList<String>());
+
+ Map<String, Map<String, String>> input = new HashMap<String, Map<String, String>>();
+ input.put("key1", CollectionUtils.toStringMap("1", "a", "2", "b", "3", "c"));
+ input.put("key2", CollectionUtils.toStringMap("1", "a", "2", "b"));
+ input.put("key3", null);
+ input.put("key4", new HashMap<String, String>());
+
+ Map<String, List<String>> output = CollectionUtils.joinAll(input, ":");
+ for (Map.Entry<String, List<String>> entry : output.entrySet()) {
+ if (entry.getValue() == null)
+ continue;
+ Collections.sort(entry.getValue());
+ }
+
+ assertEquals(expected, output);
+ }
+
+ @Test
+ public void test_mapEquals() throws Exception {
+ assertTrue(CollectionUtils.mapEquals(null, null));
+ assertFalse(CollectionUtils.mapEquals(null, new HashMap<String, String>()));
+ assertFalse(CollectionUtils.mapEquals(new HashMap<String, String>(), null));
+
+ assertTrue(CollectionUtils.mapEquals(CollectionUtils.toStringMap("1", "a", "2", "b"), CollectionUtils.toStringMap("1", "a", "2", "b")));
+ assertFalse(CollectionUtils.mapEquals(CollectionUtils.toStringMap("1", "a"), CollectionUtils.toStringMap("1", "a", "2", "b")));
+ }
+
+ @Test
+ public void test_toMap() throws Exception {
+ assertTrue(CollectionUtils.toMap().isEmpty());
+
+
+ Map<String, Integer> expected = new HashMap<String, Integer>();
+ expected.put("a", 1);
+ expected.put("b", 2);
+ expected.put("c", 3);
+
+ assertEquals(expected, CollectionUtils.toMap("a", 1, "b", 2, "c", 3));
+ }
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/utils/CompatibleTypeUtilsTest.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/utils/CompatibleTypeUtilsTest.java
new file mode 100644
index 0000000..7472abd
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/utils/CompatibleTypeUtilsTest.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.common.utils;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertSame;
+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.Date;
+
+import org.junit.Test;
+
+public class CompatibleTypeUtilsTest {
+
+ @Test
+ public void testIsIntegerLikeType() {
+ Class<?>[] types = { byte.class, Byte.class, short.class, Short.class, int.class,
+ Integer.class, long.class, Long.class, };
+
+ for (Class<?> c : types) {
+ assertTrue(CompatibleTypeUtils.isIntegerLikeType(c));
+ }
+
+ assertFalse(CompatibleTypeUtils.isIntegerLikeType(double.class));
+ assertFalse(CompatibleTypeUtils.isIntegerLikeType(Date.class));
+ }
+
+ @Test
+ public void testIsFloatLikeType() {
+ Class<?>[] types = { float.class, Float.class, double.class, Double.class };
+
+ for (Class<?> c : types) {
+ assertTrue(CompatibleTypeUtils.isFloatLikeType(c));
+ }
+
+ assertFalse(CompatibleTypeUtils.isFloatLikeType(int.class));
+ assertFalse(CompatibleTypeUtils.isFloatLikeType(Date.class));
+ }
+
+ @Test
+ public void testConvertIntegerLikeType() {
+ {
+ Object v = CompatibleTypeUtils.convertIntegerLikeType(3, byte.class);
+ assertEquals((byte) 3, ((Byte) v).byteValue());
+ }
+ {
+ Object v = CompatibleTypeUtils.convertIntegerLikeType(3L, Short.class);
+ assertEquals((short) 3, ((Short) v).shortValue());
+ }
+ {
+ Object v = CompatibleTypeUtils.convertIntegerLikeType((short) 3, int.class);
+ assertEquals(3, ((Integer) v).intValue());
+ }
+ {
+ Object v = CompatibleTypeUtils.convertIntegerLikeType((short) 3, long.class);
+ assertEquals(Long.class, v.getClass());
+ assertEquals(3L, ((Long) v).longValue());
+ }
+ {
+ Object v = CompatibleTypeUtils.convertIntegerLikeType((short) 3, Long.class);
+ assertEquals(Long.class, v.getClass());
+ assertEquals(3L, ((Long) v).longValue());
+ }
+ }
+
+ @Test
+ public void testConvertFloatLikeType() {
+ {
+ Object v = CompatibleTypeUtils.convertFloatLikeType(3D, float.class);
+ assertEquals(Float.class, v.getClass());
+ assertEquals(3F, ((Float) v).floatValue(), 0);
+ }
+ {
+ Object v = CompatibleTypeUtils.convertFloatLikeType(3F, double.class);
+ assertEquals(Double.class, v.getClass());
+ assertEquals(3D, ((Double) v).doubleValue(), 0);
+ }
+ {
+ Object v = CompatibleTypeUtils.convertFloatLikeType(3F, Double.class);
+ assertEquals(Double.class, v.getClass());
+ assertEquals(3D, ((Double) v).doubleValue(), 0);
+ }
+ }
+
+ @Test
+ public void testIsCharType() {
+ Class<?>[] types = { char.class, Character.class, };
+
+ for (Class<?> c : types) {
+ assertTrue(CompatibleTypeUtils.isCharType(c));
+ }
+
+ assertFalse(CompatibleTypeUtils.isCharType(double.class));
+ assertFalse(CompatibleTypeUtils.isCharType(Date.class));
+ }
+
+ @Test
+ public void testCovert2Char() {
+ Character c = CompatibleTypeUtils.covert2Char("a");
+ assertEquals(Character.valueOf('a'), c);
+ }
+
+ @Test
+ public void testCovert2Char_exception_moreChar() {
+ try {
+ CompatibleTypeUtils.covert2Char("ab");
+ fail();
+ } catch (IllegalArgumentException expected) {
+ assertThat(expected.getMessage(), containsString("the String MUST only 1 char"));
+ }
+ }
+
+ @Test
+ public void testNeedCompatibleTypeConvert() {
+ assertTrue(CompatibleTypeUtils.needCompatibleTypeConvert("abc", char.class));
+ assertTrue(CompatibleTypeUtils.needCompatibleTypeConvert(3L, int.class));
+ assertTrue(CompatibleTypeUtils.needCompatibleTypeConvert(3F, double.class));
+
+ // 类型参数为null时,要返回False
+ assertFalse(CompatibleTypeUtils.needCompatibleTypeConvert("a", null));
+
+ assertFalse(CompatibleTypeUtils.needCompatibleTypeConvert(null, String.class));
+ assertFalse(CompatibleTypeUtils.needCompatibleTypeConvert(null, int.class));
+ assertFalse(CompatibleTypeUtils.needCompatibleTypeConvert(null, Date.class));
+
+ assertFalse(CompatibleTypeUtils.needCompatibleTypeConvert("abc", String.class));
+ assertTrue(CompatibleTypeUtils.needCompatibleTypeConvert("abc", Date.class));
+ assertFalse(CompatibleTypeUtils.needCompatibleTypeConvert(3D, Double.class));
+ }
+
+ @Test
+ public void testCompatibleTypeConvert() {
+ {
+ Object v = CompatibleTypeUtils.compatibleTypeConvert("a", char.class);
+ assertEquals(Character.valueOf('a'), (Character) v);
+ }
+ {
+ Object v = CompatibleTypeUtils.compatibleTypeConvert(3L, int.class);
+ assertEquals(Integer.valueOf(3), (Integer) v);
+ }
+ {
+ Object v = CompatibleTypeUtils.compatibleTypeConvert(3F, Double.class);
+ assertEquals(Double.valueOf(3), (Double) v, 0);
+ }
+ {
+ Object input = new Object();
+ Object v = CompatibleTypeUtils.compatibleTypeConvert(input, Date.class);
+ assertSame(input, v);
+ }
+ }
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/utils/NetUtilsTest.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/utils/NetUtilsTest.java
new file mode 100644
index 0000000..fcf12f3
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/utils/NetUtilsTest.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.utils;
+
+import junit.framework.Assert;
+import junit.framework.TestCase;
+
+public class NetUtilsTest extends TestCase {
+
+ public void testValidAddress() throws Exception {
+ Assert.assertTrue(NetUtils.isValidAddress("10.20.130.230:20880"));
+ Assert.assertFalse(NetUtils.isValidAddress("10.20.130.230"));
+ Assert.assertFalse(NetUtils.isValidAddress("10.20.130.230:666666"));
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/utils/ParametersTest.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/utils/ParametersTest.java
new file mode 100644
index 0000000..ae006af
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/utils/ParametersTest.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.utils;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import junit.framework.TestCase;
+
+public class ParametersTest extends TestCase
+{
+ final String ServiceName = "com.alibaba.dubbo.rpc.service.GenericService";
+ final String ServiceVersion = "1.0.15";
+ final String LoadBalance = "lcr";
+
+ public void testMap2Parameters() throws Exception
+ {
+ Map<String, String> map = new HashMap<String, String>();
+ map.put("name", "com.alibaba.dubbo.rpc.service.GenericService");
+ map.put("version", "1.0.15");
+ map.put("lb", "lcr");
+ map.put("max.active", "500");
+ assertEquals(map.get("name"), ServiceName);
+ assertEquals(map.get("version"), ServiceVersion);
+ assertEquals(map.get("lb"), LoadBalance);
+ }
+
+ public void testString2Parameters() throws Exception
+ {
+ String qs = "name=com.alibaba.dubbo.rpc.service.GenericService&version=1.0.15&lb=lcr";
+ Map<String, String> map = StringUtils.parseQueryString(qs);
+ assertEquals(map.get("name"), ServiceName);
+ assertEquals(map.get("version"), ServiceVersion);
+ assertEquals(map.get("lb"), LoadBalance);
+ }
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/utils/PojoUtilsTest.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/utils/PojoUtilsTest.java
new file mode 100644
index 0000000..e4f669e
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/utils/PojoUtilsTest.java
@@ -0,0 +1,259 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.utils;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+
+import java.io.Serializable;
+import java.lang.reflect.Method;
+import java.lang.reflect.Type;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.Test;
+
+import com.alibaba.dubbo.common.model.Person;
+import com.alibaba.dubbo.common.model.SerializablePerson;
+import com.alibaba.dubbo.common.model.person.BigPerson;
+import com.alibaba.dubbo.common.model.person.FullAddress;
+import com.alibaba.dubbo.common.model.person.PersonInfo;
+import com.alibaba.dubbo.common.model.person.PersonStatus;
+import com.alibaba.dubbo.common.model.person.Phone;
+
+/**
+ * @author ding.lid
+ */
+public class PojoUtilsTest {
+
+ public void assertObject(Object data) {
+ assertObject(data, null);
+ }
+
+ public void assertObject(Object data, Type type) {
+ Object generalize = PojoUtils.generalize(data);
+ Object realize = PojoUtils.realize(generalize, data.getClass(), type);
+ assertEquals(data, realize);
+ }
+
+ public <T> void assertArrayObject(T[] data) {
+ Object generalize = PojoUtils.generalize(data);
+ @SuppressWarnings("unchecked")
+ T[] realize = (T[]) PojoUtils.realize(generalize, data.getClass());
+ assertArrayEquals(data, realize);
+ }
+
+ @Test
+ public void test_primitive() throws Exception {
+ assertObject(Boolean.TRUE);
+ assertObject(Boolean.FALSE);
+
+ assertObject(Byte.valueOf((byte) 78));
+
+ assertObject('a');
+ assertObject('中');
+
+ assertObject(Short.valueOf((short) 37));
+
+ assertObject(78);
+
+ assertObject(123456789L);
+
+ assertObject(3.14F);
+ assertObject(3.14D);
+ }
+
+ @Test
+ public void test_pojo() throws Exception {
+ assertObject(new Person());
+ assertObject(new SerializablePerson());
+ }
+
+ @Test
+ public void test_PrimitiveArray() throws Exception {
+ assertObject(new boolean[] { true, false });
+ assertObject(new Boolean[] { true, false, true });
+
+ assertObject(new byte[] { 1, 12, 28, 78 });
+ assertObject(new Byte[] { 1, 12, 28, 78 });
+
+ assertObject(new char[] { 'a', '中', '无' });
+ assertObject(new Character[] { 'a', '中', '无' });
+
+ assertObject(new short[] { 37, 39, 12 });
+ assertObject(new Short[] { 37, 39, 12 });
+
+ assertObject(new int[] { 37, -39, 12456 });
+ assertObject(new Integer[] { 37, -39, 12456 });
+
+ assertObject(new long[] { 37L, -39L, 123456789L });
+ assertObject(new Long[] { 37L, -39L, 123456789L });
+
+ assertObject(new float[] { 37F, -3.14F, 123456.7F });
+ assertObject(new Float[] { 37F, -39F, 123456.7F });
+
+ assertObject(new double[] { 37D, -3.14D, 123456.7D });
+ assertObject(new Double[] { 37D, -39D, 123456.7D});
+
+
+ assertArrayObject(new Boolean[] { true, false, true });
+
+ assertArrayObject(new Byte[] { 1, 12, 28, 78 });
+
+ assertArrayObject(new Character[] { 'a', '中', '无' });
+
+ assertArrayObject(new Short[] { 37, 39, 12 });
+
+ assertArrayObject(new Integer[] { 37, -39, 12456 });
+
+ assertArrayObject(new Long[] { 37L, -39L, 123456789L });
+
+ assertArrayObject(new Float[] { 37F, -39F, 123456.7F });
+
+ assertArrayObject(new Double[] { 37D, -39D, 123456.7D});
+ }
+
+ @Test
+ public void test_PojoArray() throws Exception {
+ Person[] array = new Person[2];
+ array[0] = new Person();
+ {
+ Person person = new Person();
+ person.setName("xxxx");
+ array[1] = person;
+ }
+ assertArrayObject(array);
+ }
+
+ public List<Person> returnListPersonMethod() {return null;}
+ public BigPerson returnBigPersonMethod() {return null;}
+ public Type getType(String methodName){
+ Method method;
+ try {
+ method = getClass().getDeclaredMethod(methodName, new Class<?>[]{} );
+ } catch (Exception e) {
+ throw new IllegalStateException(e);
+ }
+ Type gtype = method.getGenericReturnType();
+ return gtype;
+ }
+
+ @Test
+ public void test_simpleCollection() throws Exception {
+ Type gtype = getType("returnListPersonMethod");
+ List<Person> list = new ArrayList<Person>();
+ list.add(new Person());
+ {
+ Person person = new Person();
+ person.setName("xxxx");
+ list.add(person);
+ }
+ assertObject(list,gtype);
+ }
+
+
+ BigPerson bigPerson;
+ {
+ bigPerson = new BigPerson();
+ bigPerson.setPersonId("id1");
+ bigPerson.setLoginName("name1");
+ bigPerson.setStatus(PersonStatus.ENABLED);
+ bigPerson.setEmail("abc@123.com");
+ bigPerson.setPenName("pname");
+
+ ArrayList<Phone> phones = new ArrayList<Phone>();
+ Phone phone1 = new Phone("86", "0571", "11223344", "001");
+ Phone phone2 = new Phone("86", "0571", "11223344", "002");
+ phones.add(phone1);
+ phones.add(phone2);
+
+ PersonInfo pi = new PersonInfo();
+ pi.setPhones(phones);
+ Phone fax = new Phone("86", "0571", "11223344", null);
+ pi.setFax(fax);
+ FullAddress addr = new FullAddress("CN", "zj", "1234", "Road1", "333444");
+ pi.setFullAddress(addr);
+ pi.setMobileNo("1122334455");
+ pi.setMale(true);
+ pi.setDepartment("b2b");
+ pi.setHomepageUrl("www.abc.com");
+ pi.setJobTitle("dev");
+ pi.setName("name2");
+
+ bigPerson.setInfoProfile(pi);
+ }
+
+ @Test
+ public void test_total() throws Exception {
+ Object generalize = PojoUtils.generalize(bigPerson);
+ Type gtype = getType("returnBigPersonMethod");
+ Object realize = PojoUtils.realize(generalize, BigPerson.class,gtype);
+ assertEquals(bigPerson, realize);
+ }
+
+ @Test
+ public void test_total_Array() throws Exception {
+ Object[] persons = new Object[] { bigPerson, bigPerson, bigPerson };
+
+ Object generalize = PojoUtils.generalize(persons);
+ Object[] realize = (Object[]) PojoUtils.realize(generalize, Object[].class);
+ assertArrayEquals(persons, realize);
+ }
+
+ public static <T extends Comparable<T>> T min(T[] arr) {
+ if (arr == null || arr.length == 0)
+ return null;
+
+ T smallest = arr[0];
+ for (int i = 1; i < arr.length; ++i) {
+ if (smallest.compareTo(arr[i]) > 0) {
+ smallest = arr[i];
+ }
+ }
+ return smallest;
+ }
+
+ public static <T extends Comparable<? super T>> T min2(T[] arr) {
+ if (arr == null || arr.length == 0)
+ return null;
+
+ T smallest = arr[0];
+ for (int i = 1; i < arr.length; ++i) {
+ if (smallest.compareTo(arr[i]) > 0) {
+ smallest = arr[i];
+ }
+ }
+ return smallest;
+ }
+
+ public static <T extends Comparable<T> & Serializable> T max(T[] arr) {
+ if (arr == null || arr.length == 0)
+ return null;
+
+ T biggest = arr[0];
+ for (int i = 1; i < arr.length; ++i) {
+ if (biggest.compareTo(arr[i]) < 0) {
+ biggest = arr[i];
+ }
+ }
+ return biggest;
+ }
+
+ public static <T extends Comparable<T> & Serializable> T max2(Comparable<? extends Serializable>[] arr) {
+ return null;
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/utils/ReflectUtilsTest.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/utils/ReflectUtilsTest.java
new file mode 100644
index 0000000..e811154
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/utils/ReflectUtilsTest.java
@@ -0,0 +1,172 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.utils;
+
+import static org.junit.Assert.assertThat;
+import static org.junit.matchers.JUnitMatchers.containsString;
+
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.List;
+
+import junit.framework.TestCase;
+
+import org.junit.Test;
+
+public class ReflectUtilsTest extends TestCase
+{
+ public void testIsCompatible() throws Exception
+ {
+ assertEquals(ReflectUtils.isCompatible(short.class, (short)1), true);
+ assertEquals(ReflectUtils.isCompatible(int.class, 1), true);
+ assertEquals(ReflectUtils.isCompatible(double.class, 1.2), true);
+ assertEquals(ReflectUtils.isCompatible(Object.class, 1.2), true);
+ assertEquals(ReflectUtils.isCompatible(List.class, new ArrayList<String>()), true);
+ }
+
+ public void testNameDesc() throws Exception
+ {
+ // getName
+ assertEquals("boolean", ReflectUtils.getName(boolean.class));
+ assertEquals("int[][][]", ReflectUtils.getName(int[][][].class));
+ assertEquals("java.lang.Object[][]", ReflectUtils.getName(Object[][].class));
+
+ // getDesc
+ assertEquals("Z", ReflectUtils.getDesc(boolean.class));
+ assertEquals("[[[I", ReflectUtils.getDesc(int[][][].class));
+ assertEquals("[[Ljava/lang/Object;", ReflectUtils.getDesc(Object[][].class));
+
+ // name2desc
+ assertEquals("Z", ReflectUtils.name2desc(ReflectUtils.getName(boolean.class)));
+ assertEquals("[[[I", ReflectUtils.name2desc(ReflectUtils.getName(int[][][].class)));
+ assertEquals("[[Ljava/lang/Object;", ReflectUtils.name2desc(ReflectUtils.getName(Object[][].class)));
+
+ // desc2name
+ assertEquals("short[]", ReflectUtils.desc2name(ReflectUtils.getDesc(short[].class)));
+ assertEquals("int", ReflectUtils.desc2name(ReflectUtils.getDesc(int.class)));
+ assertEquals("java.lang.Object[][]", ReflectUtils.desc2name(ReflectUtils.getDesc(Object[][].class)));
+ }
+
+ public void testName2Class() throws Exception
+ {
+ assertEquals(boolean.class, ReflectUtils.name2class("boolean"));
+ assertEquals(boolean[].class, ReflectUtils.name2class("boolean[]"));
+ assertEquals(int[][].class, ReflectUtils.name2class(ReflectUtils.getName(int[][].class)));
+ assertEquals(ReflectUtilsTest[].class, ReflectUtils.name2class(ReflectUtils.getName(ReflectUtilsTest[].class)));
+ }
+
+ public void testDesc2Class() throws Exception
+ {
+ assertEquals(boolean.class, ReflectUtils.desc2class("Z"));
+ assertEquals(boolean[].class, ReflectUtils.desc2class("[Z"));
+ assertEquals(int[][].class, ReflectUtils.desc2class(ReflectUtils.getDesc(int[][].class)));
+ assertEquals(ReflectUtilsTest[].class, ReflectUtils.desc2class(ReflectUtils.getDesc(ReflectUtilsTest[].class)));
+
+ String desc;
+ Class<?>[] cs;
+
+ cs = new Class<?>[]{ int.class, getClass(), String.class, int[][].class, boolean[].class };
+ desc = ReflectUtils.getDesc(cs);
+ assertSame(cs, ReflectUtils.desc2classArray(desc));
+
+ cs = new Class<?>[]{};
+ desc = ReflectUtils.getDesc(cs);
+ assertSame(cs, ReflectUtils.desc2classArray(desc));
+
+ cs = new Class<?>[]{ void.class, String[].class, int[][].class, ReflectUtilsTest[][].class };
+ desc = ReflectUtils.getDesc(cs);
+ assertSame(cs, ReflectUtils.desc2classArray(desc));
+ }
+
+ protected void assertSame(Class<?>[] cs1, Class<?>[] cs2) throws Exception
+ {
+ assertEquals(cs1.length, cs2.length);
+ for(int i=0;i<cs1.length;i++)
+ assertEquals(cs1[i], cs2[i]);
+ }
+
+ @Test
+ public void test_findMethodByMethodSignature() throws Exception {
+ Method m = ReflectUtils.findMethodByMethodSignature(TestedClass.class, "method1", null);
+
+ assertEquals("method1", m.getName());
+ Class<?>[] parameterTypes = m.getParameterTypes();
+ assertEquals(1, parameterTypes.length);
+ assertEquals(int.class, parameterTypes[0]);
+ }
+
+ @Test
+ public void test_findMethodByMethodSignature_override() throws Exception {
+ {
+ Method m = ReflectUtils.findMethodByMethodSignature(TestedClass.class,
+ "overrideMethod", new String[] {"int"});
+
+ assertEquals("overrideMethod", m.getName());
+ Class<?>[] parameterTypes = m.getParameterTypes();
+ assertEquals(1, parameterTypes.length);
+ assertEquals(int.class, parameterTypes[0]);
+ }
+ {
+ Method m = ReflectUtils.findMethodByMethodSignature(TestedClass.class,
+ "overrideMethod", new String[] {"java.lang.Integer"});
+
+ assertEquals("overrideMethod", m.getName());
+ Class<?>[] parameterTypes = m.getParameterTypes();
+ assertEquals(1, parameterTypes.length);
+ assertEquals(Integer.class, parameterTypes[0]);
+ }
+ }
+
+ @Test
+ public void test_findMethodByMethodSignature_override_Morethan1() throws Exception {
+ try {
+ ReflectUtils.findMethodByMethodSignature(TestedClass.class, "overrideMethod", null);
+ fail();
+ } catch (IllegalStateException expected) {
+ assertThat(expected.getMessage(), containsString(
+ "Not unique method for method name("));
+ }
+ }
+
+ @Test
+ public void test_findMethodByMethodSignature_notFound() throws Exception {
+ try {
+ ReflectUtils.findMethodByMethodSignature(TestedClass.class, "notExsited", null);
+ fail();
+ } catch (NoSuchMethodException expected) {
+ assertThat(expected.getMessage(), containsString("No such method "));
+ assertThat(expected.getMessage(), containsString("in class"));
+ }
+ }
+
+ static class TestedClass {
+ public void method1(int x) {
+ }
+
+ public void overrideMethod(int x) {
+ }
+
+ public void overrideMethod(Integer x) {
+ }
+
+ public void overrideMethod(String s) {
+ }
+
+ public void overrideMethod(String s1, String s2) {
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/utils/StringUtilsTest.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/utils/StringUtilsTest.java
new file mode 100644
index 0000000..a5f3853
--- /dev/null
+++ b/dubbo-common/src/test/java/com/alibaba/dubbo/common/utils/StringUtilsTest.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.common.utils;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import junit.framework.TestCase;
+
+public class StringUtilsTest extends TestCase
+{
+ public void testJoin() throws Exception
+ {
+ String[] s = {"1","2","3"};
+ assertEquals(StringUtils.join(s), "123");
+ assertEquals(StringUtils.join(s, ','), "1,2,3");
+ }
+
+ public void testSplit() throws Exception
+ {
+ String s = "d,1,2,4";
+ assertEquals(StringUtils.split(s, ',').length, 4);
+ }
+
+ public void testTranslat() throws Exception
+ {
+ String s = "16314";
+ assertEquals(StringUtils.translat(s, "123456", "abcdef"), "afcad");
+ assertEquals(StringUtils.translat(s, "123456", "abcd"), "acad");
+ }
+
+ public void testJoin_Colletion_String() throws Exception
+ {
+ List<String> list = new ArrayList<String>();
+ assertEquals("", StringUtils.join(list, ","));
+
+ list.add("v1");
+ assertEquals("v1", StringUtils.join(list, "-"));
+
+ list.add("v2");
+ list.add("v3");
+ String out = StringUtils.join(list, ":");
+ assertEquals("v1:v2:v3", out);
+ }
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/java/com/alibaba/dubbo/common/utils/UrlUtilsTest.java b/dubbo-common/src/test/java/com/alibaba/dubbo/common/utils/UrlUtilsTest.java
new file mode 100644
index 0000000..eb685f9
--- /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("127.0.0.1:20880", "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("127.0.0.1:20880", "dubbo.version=2.0.0");
+ assertEquals(newService, newRegister.get("test/dubbo.test.api.HelloService:1.0.0"));
+ }
+
+ @Test
+ public void testSubscribe() {
+ String key = "perf/dubbo.test.api.HelloService:1.0.0";
+ Map<String, String> subscribe = new HashMap<String, String>();
+ subscribe.put(key, null);
+ Map<String, String> newSubscribe = UrlUtils.convertSubscribe(subscribe);
+ assertEquals(subscribe, newSubscribe);
+ }
+
+ @Test
+ public void testSubscribe2() {
+ String key = "dubbo.test.api.HelloService";
+ Map<String, String> subscribe = new HashMap<String, String>();
+ subscribe.put(key, "version=1.0.0&group=test&dubbo.version=2.0.0");
+ Map<String, String> newSubscribe = UrlUtils.convertSubscribe(subscribe);
+ assertEquals("dubbo.version=2.0.0", newSubscribe.get("test/dubbo.test.api.HelloService:1.0.0"));
+ }
+
+ @Test
+ public void testRevertRegister() {
+ String key = "perf/dubbo.test.api.HelloService:1.0.0";
+ Map<String, Map<String, String>> register = new HashMap<String, Map<String, String>>();
+ Map<String, String> service = new HashMap<String, String>();
+ service.put("127.0.0.1:20880", 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("127.0.0.1:20880", "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("127.0.0.1:20880", 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("127.0.0.1:20880", 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("127.0.0.1:20880", "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("127.0.0.1:20880", "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("127.0.0.1:20880", "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("127.0.0.1:20880", "group=perf&version=1.0.0");
+ expectedRegister.put("perf/dubbo.test.api.HelloService:1.0.0", service);
+ assertEquals(expectedRegister, newRegister);
+ }
+
+ // 为了兼容2.0.0版本的rpc
+ @Test
+ public void testRevertForbid() {
+ String service = "dubbo.test.api.HelloService";
+ List<String> forbid = new ArrayList<String>();
+ forbid.add(service);
+ Set<String> subscribed = new HashSet<String>();
+ subscribed.add("perf/+" + service + ":1.0.0");
+ List<String> newForbid = UrlUtils.revertForbid(forbid, subscribed);
+ List<String> expectForbid = new ArrayList<String>();
+ expectForbid.add("perf/+" + service + ":1.0.0");
+ assertEquals(expectForbid, newForbid);
+ }
+
+ @Test
+ public void testRevertForbid2() {
+ List<String> newForbid = UrlUtils.revertForbid(null, null);
+ assertNull(newForbid);
+ }
+
+ @Test
+ public void testRevertForbid3() {
+ String service1 = "dubbo.test.api.HelloService:1.0.0";
+ String service2 = "dubbo.test.api.HelloService:2.0.0";
+ List<String> forbid = new ArrayList<String>();
+ forbid.add(service1);
+ forbid.add(service2);
+ List<String> newForbid = UrlUtils.revertForbid(forbid, null);
+ assertEquals(forbid, newForbid);
+ }
+
+ @Test
+ public void testIsMatch() {
+ URL consumerUrl = URL.valueOf("?version=1.0.0&group=test");
+ URL providerUrl = URL.valueOf("127.0.0.1:8080/?version=1.0.0&group=test");
+ assertTrue(UrlUtils.isMatch(consumerUrl, providerUrl));
+ }
+
+ @Test
+ public void testIsMatch2() {
+ URL consumerUrl = URL.valueOf("?version=2.0.0&group=test");
+ URL providerUrl = URL.valueOf("127.0.0.1:8080/?version=1.0.0&group=test");
+ assertFalse(UrlUtils.isMatch(consumerUrl, providerUrl));
+ }
+
+ @Test
+ public void testIsMatch3() {
+ URL consumerUrl = URL.valueOf("?version=1.0.0&group=aa");
+ URL providerUrl = URL.valueOf("127.0.0.1:8080/?version=1.0.0&group=test");
+ assertFalse(UrlUtils.isMatch(consumerUrl, providerUrl));
+ }
+
+ @Test
+ public void testIsMatch4() {
+ URL consumerUrl = URL.valueOf("?version=1.0.0&group=*");
+ URL providerUrl = URL.valueOf("127.0.0.1:8080/?version=1.0.0&group=test");
+ assertTrue(UrlUtils.isMatch(consumerUrl, providerUrl));
+ }
+
+ @Test
+ public void testIsMatch5() {
+ URL consumerUrl = URL.valueOf("?version=*&group=test");
+ URL providerUrl = URL.valueOf("127.0.0.1:8080/?version=1.0.0&group=test");
+ assertTrue(UrlUtils.isMatch(consumerUrl, providerUrl));
+ }
+}
\ No newline at end of file
diff --git a/dubbo-common/src/test/resources/META-INF/services/com.alibaba.dubbo.common.extensionloader.ext1.Ext1 b/dubbo-common/src/test/resources/META-INF/services/com.alibaba.dubbo.common.extensionloader.ext1.Ext1
new file mode 100644
index 0000000..ab1a421
--- /dev/null
+++ b/dubbo-common/src/test/resources/META-INF/services/com.alibaba.dubbo.common.extensionloader.ext1.Ext1
@@ -0,0 +1,3 @@
+com.alibaba.dubbo.common.extensionloader.ext1.impl.Ext1Impl1#Hello World
+com.alibaba.dubbo.common.extensionloader.ext1.impl.Ext1Impl2 # Comment 2
+ com.alibaba.dubbo.common.extensionloader.ext1.impl.Ext1Impl3 # with head space
\ No newline at end of file
diff --git a/dubbo-common/src/test/resources/META-INF/services/com.alibaba.dubbo.common.extensionloader.ext2.Ext2 b/dubbo-common/src/test/resources/META-INF/services/com.alibaba.dubbo.common.extensionloader.ext2.Ext2
new file mode 100644
index 0000000..c58f1e4
--- /dev/null
+++ b/dubbo-common/src/test/resources/META-INF/services/com.alibaba.dubbo.common.extensionloader.ext2.Ext2
@@ -0,0 +1,3 @@
+com.alibaba.dubbo.common.extensionloader.ext2.impl.Ext2Impl1
+com.alibaba.dubbo.common.extensionloader.ext2.impl.Ext2Impl2
+com.alibaba.dubbo.common.extensionloader.ext2.impl.Ext2Impl3
\ No newline at end of file
diff --git a/dubbo-common/src/test/resources/META-INF/services/com.alibaba.dubbo.common.extensionloader.ext3.Ext3 b/dubbo-common/src/test/resources/META-INF/services/com.alibaba.dubbo.common.extensionloader.ext3.Ext3
new file mode 100644
index 0000000..b0e93dc
--- /dev/null
+++ b/dubbo-common/src/test/resources/META-INF/services/com.alibaba.dubbo.common.extensionloader.ext3.Ext3
@@ -0,0 +1,3 @@
+com.alibaba.dubbo.common.extensionloader.ext3.impl.Ext3Impl1
+com.alibaba.dubbo.common.extensionloader.ext3.impl.Ext3Impl2
+com.alibaba.dubbo.common.extensionloader.ext3.impl.Ext3Impl3
\ No newline at end of file
diff --git a/dubbo-common/src/test/resources/META-INF/services/com.alibaba.dubbo.common.extensionloader.ext4.Ext4 b/dubbo-common/src/test/resources/META-INF/services/com.alibaba.dubbo.common.extensionloader.ext4.Ext4
new file mode 100644
index 0000000..a70b49d
--- /dev/null
+++ b/dubbo-common/src/test/resources/META-INF/services/com.alibaba.dubbo.common.extensionloader.ext4.Ext4
@@ -0,0 +1,2 @@
+com.alibaba.dubbo.common.extensionloader.ext4.impl.Ext4Impl1
+com.alibaba.dubbo.common.extensionloader.ext4.impl.Ext4Impl2
\ No newline at end of file
diff --git a/dubbo-common/src/test/resources/META-INF/services/com.alibaba.dubbo.common.extensionloader.ext5.Ext5NoAdaptiveMethod b/dubbo-common/src/test/resources/META-INF/services/com.alibaba.dubbo.common.extensionloader.ext5.Ext5NoAdaptiveMethod
new file mode 100644
index 0000000..6b655f9
--- /dev/null
+++ b/dubbo-common/src/test/resources/META-INF/services/com.alibaba.dubbo.common.extensionloader.ext5.Ext5NoAdaptiveMethod
@@ -0,0 +1,4 @@
+com.alibaba.dubbo.common.extensionloader.ext5.impl.Ext5Impl1
+com.alibaba.dubbo.common.extensionloader.ext5.impl.Ext5Impl2
+com.alibaba.dubbo.common.extensionloader.ext5.impl.Ext5Wrapper1
+com.alibaba.dubbo.common.extensionloader.ext5.impl.Ext5Wrapper2
\ No newline at end of file
diff --git a/dubbo-common/src/test/resources/META-INF/services/com.alibaba.dubbo.common.extensionloader.ext6_inject.Ext6 b/dubbo-common/src/test/resources/META-INF/services/com.alibaba.dubbo.common.extensionloader.ext6_inject.Ext6
new file mode 100644
index 0000000..6dd3b3c
--- /dev/null
+++ b/dubbo-common/src/test/resources/META-INF/services/com.alibaba.dubbo.common.extensionloader.ext6_inject.Ext6
@@ -0,0 +1,2 @@
+com.alibaba.dubbo.common.extensionloader.ext6_inject.impl.Ext6Impl1
+com.alibaba.dubbo.common.extensionloader.ext6_inject.impl.Ext6Impl2
\ No newline at end of file
diff --git a/dubbo-common/src/test/resources/StreamUtilsTest.txt b/dubbo-common/src/test/resources/StreamUtilsTest.txt
new file mode 100644
index 0000000..ad47100
--- /dev/null
+++ b/dubbo-common/src/test/resources/StreamUtilsTest.txt
@@ -0,0 +1 @@
+0123456789
\ No newline at end of file
diff --git a/dubbo-common/src/test/resources/com/alibaba/dubbo/common/serialize/dubbo/SimpleDO.fc b/dubbo-common/src/test/resources/com/alibaba/dubbo/common/serialize/dubbo/SimpleDO.fc
new file mode 100644
index 0000000..c8c083c
--- /dev/null
+++ b/dubbo-common/src/test/resources/com/alibaba/dubbo/common/serialize/dubbo/SimpleDO.fc
@@ -0,0 +1,2 @@
+a,d,e,b,c
+str3,str2
\ No newline at end of file
diff --git a/dubbo-common/src/test/resources/log4j.xml b/dubbo-common/src/test/resources/log4j.xml
new file mode 100644
index 0000000..b3394a4
--- /dev/null
+++ b/dubbo-common/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..142be56
--- /dev/null
+++ b/dubbo-config/pom.xml
@@ -0,0 +1,105 @@
+<!--
+ - Copyright 1999-2011 Alibaba Group.
+ -
+ - Licensed under the Apache License, Version 2.0 (the "License");
+ - you may not use this file except in compliance with the License.
+ - You may obtain a copy of the License at
+ -
+ - http://www.apache.org/licenses/LICENSE-2.0
+ -
+ - Unless required by applicable law or agreed to in writing, software
+ - distributed under the License is distributed on an "AS IS" BASIS,
+ - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ - See the License for the specific language governing permissions and
+ - limitations under the License.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>com.alibaba</groupId>
+ <artifactId>dubbo-parent</artifactId>
+ <version>2.0.8</version>
+ </parent>
+ <artifactId>dubbo-config</artifactId>
+ <packaging>jar</packaging>
+ <name>Dubbo Config Module</name>
+ <description>The config module of dubbo project</description>
+ <properties>
+ <skip_maven_deploy>false</skip_maven_deploy>
+ </properties>
+ <dependencies>
+ <dependency>
+ <groupId>com.alibaba</groupId>
+ <artifactId>dubbo-registry</artifactId>
+ <version>${project.parent.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>com.alibaba</groupId>
+ <artifactId>dubbo-monitor</artifactId>
+ <version>${project.parent.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>com.alibaba</groupId>
+ <artifactId>dubbo-remoting</artifactId>
+ <version>${project.parent.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>com.alibaba</groupId>
+ <artifactId>dubbo-registry-simple</artifactId>
+ <version>${project.parent.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>com.alibaba</groupId>
+ <artifactId>dubbo-monitor-simple</artifactId>
+ <version>${project.parent.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>com.alibaba</groupId>
+ <artifactId>dubbo-rpc-default</artifactId>
+ <version>${project.parent.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>com.alibaba</groupId>
+ <artifactId>dubbo-rpc-rmi</artifactId>
+ <version>${project.parent.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>com.alibaba</groupId>
+ <artifactId>dubbo-remoting-netty</artifactId>
+ <version>${project.parent.version}</version>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>wagon-maven-plugin</artifactId>
+ <version>1.0-beta-3</version>
+ <executions>
+ <execution>
+ <id>upload-schema</id>
+ <phase>deploy</phase>
+ <goals>
+ <goal>upload-single</goal>
+ </goals>
+ <configuration>
+ <fromFile>${basedir}/src/main/resources/com/alibaba/dubbo/config/spring/schema/dubbo.xsd</fromFile>
+ <url>dav:http://code.alibabatech.com/schema/dubbo/</url>
+ <serverId>opensesame.releases.account</serverId>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+</project>
\ No newline at end of file
diff --git a/dubbo-config/src/main/java/com/alibaba/dubbo/config/AbstractConfig.java b/dubbo-config/src/main/java/com/alibaba/dubbo/config/AbstractConfig.java
new file mode 100644
index 0000000..72c3136
--- /dev/null
+++ b/dubbo-config/src/main/java/com/alibaba/dubbo/config/AbstractConfig.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.io.InputStream;
+import java.io.Serializable;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.net.URLEncoder;
+import java.util.Map;
+import java.util.Properties;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.ExtensionLoader;
+import com.alibaba.dubbo.common.logger.Logger;
+import com.alibaba.dubbo.common.logger.LoggerFactory;
+
+/**
+ * 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 Properties PROPERTIES = loadProperties();
+
+ private static final int MAX_LENGTH = 100;
+
+ private static final int MAX_PATH_LENGTH = 200;
+
+ private static final Pattern PATTERN_PATH = 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_NAME = Pattern.compile("[\\-._0-9a-zA-Z]+");
+
+ private static final Pattern PATTERN_NAME_HAS_COLON= Pattern.compile("[:\\-._0-9a-zA-Z]+");
+
+ protected static String getLegacyProperty(String key) {
+ String value = System.getProperty(key);
+ if (value == null || value.length() == 0) {
+ value = PROPERTIES.getProperty(key);
+ }
+ return value;
+ }
+
+ public static void mergeProperties(Properties properties) {
+ if (properties != null) {
+ PROPERTIES.putAll(properties);
+ }
+ }
+
+ private static Properties loadProperties() {
+ Properties properties = new Properties();
+ try {
+ InputStream input = Thread.currentThread().getContextClassLoader()
+ .getResourceAsStream("dubbo.properties");
+ if (input != null) {
+ try {
+ properties.load(input);
+ } finally {
+ input.close();
+ }
+ }
+ } catch (Throwable e) {
+ logger.warn("Fail to load dubbo.properties file: " + e.getMessage(), e);
+ }
+ return properties;
+ }
+
+ protected static void appendParameters(Map<String, String> parameters, Object config) {
+ appendParameters(parameters, config, null);
+ }
+
+ protected static void appendParameters(Map<String, String> parameters, Object config, String prefix) {
+ appendMaps(parameters, config, prefix, false);
+ }
+
+ 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) {
+ appendMaps(parameters, config, prefix, true);
+ }
+
+ 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;
+ }
+
+ @SuppressWarnings({ "unchecked", "rawtypes" })
+ private static void appendMaps(Map parameters, Object config, String prefix, boolean attribute) {
+ 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 (attribute){
+ if (parameter == null || !parameter.attribute())
+ continue;
+ } else {
+ if (method.getReturnType() == Object.class || parameter != null && parameter.excluded()) {
+ 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 (attribute){
+ if (prefix != null && prefix.length() > 0) {
+ key = prefix + "." + key;
+ }
+ if (value != null) parameters.put(key, value);
+ } else {
+ String str = String.valueOf(value).trim();
+ if (value != null && str.length() > 0) {
+ if (prefix != null && prefix.length() > 0) {
+ key = prefix + "." + key;
+ }
+ if (parameter != null && parameter.escaped()) {
+ str = URLEncoder.encode(str, "UTF-8");
+ }
+ parameters.put(key, str);
+ } else if (parameter != null && parameter.required()) {
+ throw new IllegalStateException(config.getClass().getSimpleName() + "." + key + " == null");
+ }
+ }
+ } else if (!attribute && "getParameters".equals(name)
+ && Modifier.isPublic(method.getModifiers())
+ && method.getParameterTypes().length == 0
+ && method.getReturnType() == Map.class) {
+ Map<String, String> map = (Map<String, String>) method.invoke(config, new Object[0]);
+ if (map != null && map.size() > 0) {
+ if (prefix != null && prefix.length() > 0) {
+ for (Map.Entry<String, String> entry : map.entrySet()) {
+ parameters.put(prefix + "." + entry.getKey(), entry.getValue());
+ }
+ } else {
+ parameters.putAll(map);
+ }
+ }
+ }
+ } catch (Exception e) {
+ throw new IllegalStateException(e.getMessage(), e);
+ }
+ }
+ }
+
+ protected static void checkExtension(Class<?> type, String property, String value) {
+ checkName(property, value);
+ if (value != null && value.length() > 0
+ && ! ExtensionLoader.getExtensionLoader(type).hasExtension(value)) {
+ throw new IllegalStateException("No such extension " + value + " for " + property + "/" + type.getName());
+ }
+ }
+
+ protected static void checkMultiExtension(Class<?> type, String property, String value) {
+ checkMultiName(property, value);
+ if (value != null && value.length() > 0) {
+ String[] values = value.split("\\s*[,]+\\s*");
+ for (String v : values) {
+ if (v.startsWith(Constants.REMOVE_VALUE_PREFIX)) {
+ v = v.substring(1);
+ }
+ if (! ExtensionLoader.getExtensionLoader(type).hasExtension(v)) {
+ throw new IllegalStateException("No such extension " + v + " for " + property + "/" + type.getName());
+ }
+ }
+ }
+ }
+
+ protected static void checkLength(String property, String value) {
+ checkProperty(property, value, MAX_LENGTH, null);
+ }
+
+ protected static void checkPathLength(String property, String value) {
+ checkProperty(property, value, MAX_PATH_LENGTH, null);
+ }
+
+ protected static void checkName(String property, String value) {
+ checkProperty(property, value, MAX_LENGTH, PATTERN_NAME);
+ }
+
+ protected static void checkNameHasColon(String property, String value) {
+ checkProperty(property, value, MAX_LENGTH, PATTERN_NAME_HAS_COLON);
+ }
+
+ protected static void checkMultiName(String property, String value) {
+ checkProperty(property, value, MAX_LENGTH, PATTERN_MULTI_NAME);
+ }
+
+ protected static void checkPathName(String property, String value) {
+ checkProperty(property, value, MAX_PATH_LENGTH, PATTERN_PATH);
+ }
+
+ protected static void checkMethodName(String property, String value) {
+ checkProperty(property, value, MAX_LENGTH, PATTERN_METHOD_NAME);
+ }
+
+ protected static void checkParameterName(Map<String, String> parameters) {
+ if (parameters == null || parameters.size() == 0) {
+ return;
+ }
+ for (Map.Entry<String, String> entry : parameters.entrySet()) {
+ //change by tony.chenl parameter value maybe has colon.for example napoli address
+ checkNameHasColon(entry.getKey(), entry.getValue());
+ }
+ }
+
+ protected static void checkProperty(String property, String value, int maxlength, Pattern pattern) {
+ if (value == null || value.length() == 0) {
+ return;
+ }
+ if(value.length() > maxlength){
+ throw new IllegalStateException("Invalid " + property + "=\"" + value + "\" is longer than " + maxlength);
+ }
+ if (pattern != null) {
+ Matcher matcher = pattern.matcher(value);
+ if(! matcher.matches()) {
+ throw new IllegalStateException("Invalid " + property + "=\"" + value + "\" contain illegal charactor, only digit, letter, '-', '_' and '.' is legal.");
+ }
+ }
+ }
+
+ static {
+ Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
+ public void run() {
+ if (logger.isInfoEnabled()) {
+ logger.info("Run shutdown hook now.");
+ }
+ ProtocolConfig.destroyAll();
+ }
+ }, "DubboShutdownHook"));
+ }
+
+ @Override
+ public String toString() {
+ try {
+ String tag = getClass().getSimpleName();
+ if (tag.equals("Config")) {
+ tag = tag.substring(0, tag.length() - "Config".length());
+ }
+ tag = tag.toLowerCase();
+ StringBuilder buf = new StringBuilder();
+ buf.append("<dubbo:");
+ buf.append(tag);
+ Method[] methods = 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())) {
+ 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) { // 防御性容错
+ return super.toString();
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-config/src/main/java/com/alibaba/dubbo/config/AbstractConsumerConfig.java b/dubbo-config/src/main/java/com/alibaba/dubbo/config/AbstractConsumerConfig.java
new file mode 100644
index 0000000..f44f996
--- /dev/null
+++ b/dubbo-config/src/main/java/com/alibaba/dubbo/config/AbstractConsumerConfig.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.config;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.rpc.InvokerListener;
+import com.alibaba.dubbo.rpc.RpcConstants;
+
+
+/**
+ * AbstractConsumerConfig
+ *
+ * @see com.alibaba.dubbo.config.ReferenceConfig
+ * @author william.liangf
+ */
+public abstract class AbstractConsumerConfig extends AbstractReferenceConfig {
+
+ private static final long serialVersionUID = -2786526984373031126L;
+
+ // ======== 引用缺省值,当引用属性未设置时使用该缺省值替代 ========
+
+ // 检查服务提供者是否存在
+ protected Boolean check;
+
+ // 是否使用泛接口
+ protected Boolean generic;
+
+ // 优先从JVM内获取引用实例
+ protected Boolean injvm;
+
+ // lazy create connection
+ protected Boolean lazy;
+
+ protected String reconnect;
+
+ protected Boolean sticky;
+
+ //stub是否支持event事件. //TODO slove merge problem
+ protected Boolean stubevent ;//= RpcConstants.DEFAULT_STUB_EVENT;
+
+ public Boolean isCheck() {
+ return check;
+ }
+
+ public void setCheck(Boolean check) {
+ this.check = check;
+ }
+
+ @Parameter(excluded = true)
+ public Boolean isGeneric() {
+ return generic;
+ }
+
+ public void setGeneric(Boolean generic) {
+ this.generic = generic;
+ }
+
+ public Boolean isInjvm() {
+ return injvm;
+ }
+
+ public void setInjvm(Boolean injvm) {
+ this.injvm = injvm;
+ }
+
+ @Override
+ public void setTimeout(Integer timeout) {
+ super.setTimeout(timeout);
+ if (timeout != null && timeout > 0) {
+ System.setProperty("sun.rmi.transport.tcp.responseTimeout", String.valueOf(timeout));
+ }
+ }
+
+ @Parameter(key = Constants.REFERENCE_FILTER_KEY)
+ public String getFilter() {
+ return super.getFilter();
+ }
+
+ @Parameter(key = Constants.INVOKER_LISTENER_KEY)
+ public String getListener() {
+ return super.getListener();
+ }
+
+ @Override
+ public void setListener(String listener) {
+ checkMultiExtension(InvokerListener.class, "listener", listener);
+ super.setListener(listener);
+ }
+
+ @Parameter(key = RpcConstants.LAZY_CONNECT_KEY)
+ public Boolean getLazy() {
+ return lazy;
+ }
+
+ public void setLazy(Boolean lazy) {
+ this.lazy = lazy;
+ }
+
+ @Override
+ public void setOnconnect(String onconnect) {
+ if (onconnect != null && onconnect.length() >0){
+ this.stubevent = true;
+ }
+ super.setOnconnect(onconnect);
+ }
+
+ @Override
+ public void setOndisconnect(String ondisconnect) {
+ if (ondisconnect != null && ondisconnect.length() >0){
+ this.stubevent = true;
+ }
+ super.setOndisconnect(ondisconnect);
+ }
+
+ @Parameter(key = RpcConstants.STUB_EVENT_KEY)
+ public Boolean getStubevent() {
+ return stubevent;
+ }
+
+ @Parameter(key = Constants.RECONNECT_KEY)
+ public String getReconnect() {
+ return reconnect;
+ }
+
+ public void setReconnect(String reconnect) {
+ this.reconnect = reconnect;
+ }
+
+ @Parameter(key = RpcConstants.CLUSTER_STICKY_KEY)
+ public Boolean getSticky() {
+ return sticky;
+ }
+
+ public void setSticky(Boolean sticky) {
+ this.sticky = sticky;
+ }
+}
\ No newline at end of file
diff --git a/dubbo-config/src/main/java/com/alibaba/dubbo/config/AbstractMethodConfig.java b/dubbo-config/src/main/java/com/alibaba/dubbo/config/AbstractMethodConfig.java
new file mode 100644
index 0000000..a44d85b
--- /dev/null
+++ b/dubbo-config/src/main/java/com/alibaba/dubbo/config/AbstractMethodConfig.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.config;
+
+import java.util.Map;
+
+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;
+
+ // 自定义参数
+ 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 Map<String, String> getParameters() {
+ return parameters;
+ }
+
+ public void setParameters(Map<String, String> parameters) {
+ checkParameterName(parameters);
+ this.parameters = parameters;
+ }
+
+ 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;
+ }
+}
\ No newline at end of file
diff --git a/dubbo-config/src/main/java/com/alibaba/dubbo/config/AbstractReferenceConfig.java b/dubbo-config/src/main/java/com/alibaba/dubbo/config/AbstractReferenceConfig.java
new file mode 100644
index 0000000..48a8ab2
--- /dev/null
+++ b/dubbo-config/src/main/java/com/alibaba/dubbo/config/AbstractReferenceConfig.java
@@ -0,0 +1,381 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.config;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.ExtensionLoader;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.Version;
+import com.alibaba.dubbo.common.utils.ConfigUtils;
+import com.alibaba.dubbo.common.utils.NetUtils;
+import com.alibaba.dubbo.common.utils.StringUtils;
+import com.alibaba.dubbo.common.utils.UrlUtils;
+import com.alibaba.dubbo.monitor.MonitorFactory;
+import com.alibaba.dubbo.monitor.MonitorService;
+import com.alibaba.dubbo.registry.RegistryFactory;
+import com.alibaba.dubbo.registry.RegistryService;
+import com.alibaba.dubbo.rpc.Filter;
+import com.alibaba.dubbo.rpc.InvokerListener;
+import com.alibaba.dubbo.rpc.ProxyFactory;
+import com.alibaba.dubbo.rpc.RpcConstants;
+import com.alibaba.dubbo.rpc.cluster.Cluster;
+
+/**
+ * AbstractDefaultConfig
+ *
+ * @author william.liangf
+ */
+public abstract class AbstractReferenceConfig extends AbstractMethodConfig {
+
+ private static final long serialVersionUID = -1559314110797223229L;
+
+ // 服务接口的本地实现类名
+ protected String local;
+
+ // 服务接口的本地实现类名
+ protected String stub;
+
+ // 服务接口的失败mock实现类名
+ protected String mock;
+
+ // 服务监控
+ protected MonitorConfig monitor;
+
+ // 代理类型
+ protected String proxy;
+
+ // 集群方式
+ protected String cluster;
+
+ // 过滤器
+ protected String filter;
+
+ // 监听器
+ protected String listener;
+
+ // 负责人
+ protected String owner;
+
+ // 连接数限制
+ protected Integer connections;
+
+ // 连接数限制
+ protected String layer;
+
+ // 应用信息
+ protected ApplicationConfig application;
+
+ // 注册中心
+ protected List<RegistryConfig> registries;
+
+ // callback实例个数限制
+ private Integer callbacks;
+
+ // 连接事件
+ protected String onconnect;
+
+ // 断开事件
+ protected String ondisconnect;
+
+ protected void checkRegistry() {
+ // 兼容旧版本
+ if (registries == null || registries.size() == 0) {
+ String address = getLegacyProperty("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\" />");
+ }
+ }
+
+ @SuppressWarnings("deprecation")
+ protected void checkApplication() {
+ // 兼容旧版本
+ if (application == null) {
+ String app = getLegacyProperty("dubbo.application.name");
+ if (app != null && app.length() > 0) {
+ application = new ApplicationConfig();
+ application.setName(app);
+ }
+ }
+ if (application == null) {
+ throw new IllegalStateException(
+ "No such application config! Please add <dubbo:application name=\"...\" /> to your spring config.");
+ }
+
+
+ String wait = getLegacyProperty(RpcConstants.SHUTDOWN_TIMEOUT_KEY);
+ if (wait != null && wait.trim().length() > 0) {
+ System.setProperty(RpcConstants.SHUTDOWN_TIMEOUT_KEY, wait.trim());
+ } else {
+ wait = getLegacyProperty(RpcConstants.SHUTDOWN_TIMEOUT_SECONDS_KEY);
+ if (wait != null && wait.trim().length() > 0) {
+ System.setProperty(RpcConstants.SHUTDOWN_TIMEOUT_SECONDS_KEY, wait.trim());
+ }
+ }
+ }
+
+ protected List<URL> loadRegistries() {
+ checkRegistry();
+ List<URL> registryList = new ArrayList<URL>();
+ if (registries != null && registries.size() > 0) {
+ for (RegistryConfig config : registries) {
+ if (! RegistryConfig.NO_AVAILABLE.equalsIgnoreCase(config.getAddress())) {
+ if (config.getAddress() == null || config.getAddress().length() == 0) {
+ throw new IllegalStateException("registry address == null");
+ }
+ Map<String, String> map = new HashMap<String, String>();
+ appendParameters(map, config);
+ map.put("path", RegistryService.class.getName());
+ if (! map.containsKey("protocol")) {
+ if (ExtensionLoader.getExtensionLoader(RegistryFactory.class).hasExtension("remote")) {
+ map.put("protocol", "remote");
+ } else {
+ map.put("protocol", "dubbo");
+ }
+ }
+ List<URL> urls = UrlUtils.parseURLs(config.getAddress(), map);
+ for (URL url : urls) {
+ url = url.addParameter(Constants.REGISTRY_KEY, url.getProtocol());
+ url = url.setProtocol(Constants.REGISTRY_PROTOCOL);
+ registryList.add(url);
+ }
+ }
+ }
+ }
+ return registryList;
+ }
+
+ protected URL loadMonitor(URL registryURL) {
+ if (monitor == null) {
+ return null;
+ }
+ Map<String, String> map = new HashMap<String, String>();
+ map.put(Constants.INTERFACE_KEY, MonitorService.class.getName());
+ appendParameters(map, monitor);
+ if (ConfigUtils.isNotEmpty(monitor.getAddress())) {
+ 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(monitor.getAddress(), map);
+ } else if (ConfigUtils.isNotEmpty(monitor.getGroup()) && registryURL != null) {
+ return registryURL.setProtocol("dubbo").addParameter(Constants.PROTOCOL_KEY, "registry").addParameterAndEncoded(RpcConstants.REFER_KEY, StringUtils.toQueryString(map));
+ }
+ return null;
+ }
+
+ /**
+ * @deprecated Replace to <code>getStub()</code>
+ * @return
+ */
+ @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;
+ }
+
+ public String getStub() {
+ return stub;
+ }
+
+ public void setStub(String stub) {
+ checkName("stub", stub);
+ this.stub = stub;
+ }
+
+ public void setLocal(Boolean local) {
+ if (local == null) {
+ setLocal((String) null);
+ } else {
+ setLocal(String.valueOf(local));
+ }
+ }
+
+ public String getMock() {
+ return mock;
+ }
+
+ public void setMock(String mock) {
+ checkName("mock", mock);
+ this.mock = mock;
+ }
+
+ public void setMock(Boolean mock) {
+ if (mock == null) {
+ setMock((String) null);
+ } else {
+ setMock(String.valueOf(mock));
+ }
+ }
+
+ 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)
+ public String getFilter() {
+ return filter;
+ }
+
+ public void setFilter(String filter) {
+ checkMultiExtension(Filter.class, "filter", filter);
+ this.filter = filter;
+ }
+
+ @Parameter(key = Constants.INVOKER_LISTENER_KEY)
+ public String getListener() {
+ checkMultiExtension(InvokerListener.class, "listener", listener);
+ return listener;
+ }
+
+ public void setListener(String listener) {
+ this.listener = listener;
+ }
+
+ public String getLayer() {
+ return layer;
+ }
+
+ public void setLayer(String layer) {
+ checkNameHasColon("layer", layer);
+ this.layer = layer;
+ }
+
+ public ApplicationConfig getApplication() {
+ return application;
+ }
+
+ public void setApplication(ApplicationConfig application) {
+ this.application = application;
+ }
+
+ public RegistryConfig getRegistry() {
+ return registries == null || registries.size() == 0 ? null : registries.get(0);
+ }
+
+ public void setRegistry(RegistryConfig registry) {
+ List<RegistryConfig> registries = new ArrayList<RegistryConfig>(1);
+ registries.add(registry);
+ this.registries = registries;
+ }
+
+ public List<RegistryConfig> getRegistries() {
+ return registries;
+ }
+
+ @SuppressWarnings({ "unchecked" })
+ public void setRegistries(List<? extends RegistryConfig> registries) {
+ this.registries = (List<RegistryConfig>)registries;
+ }
+
+ public MonitorConfig getMonitor() {
+ return monitor;
+ }
+
+ public void setMonitor(MonitorConfig monitor) {
+ this.monitor = monitor;
+ }
+
+ public void setMonitor(String monitor) {
+ this.monitor = new MonitorConfig(monitor);
+ }
+
+ public String getOwner() {
+ return owner;
+ }
+
+ public void setOwner(String owner) {
+ this.owner = owner;
+ }
+
+ public void setCallbacks(Integer callbacks) {
+ this.callbacks = callbacks;
+ }
+
+ public Integer getCallbacks() {
+ return callbacks;
+ }
+
+ public String getOnconnect() {
+ return onconnect;
+ }
+
+ public void setOnconnect(String onconnect) {
+ this.onconnect = onconnect;
+ }
+
+ public String getOndisconnect() {
+ return ondisconnect;
+ }
+
+ public void setOndisconnect(String ondisconnect) {
+ this.ondisconnect = ondisconnect;
+ }
+}
\ No newline at end of file
diff --git a/dubbo-config/src/main/java/com/alibaba/dubbo/config/AbstractServiceConfig.java b/dubbo-config/src/main/java/com/alibaba/dubbo/config/AbstractServiceConfig.java
new file mode 100644
index 0000000..3700667
--- /dev/null
+++ b/dubbo-config/src/main/java/com/alibaba/dubbo/config/AbstractServiceConfig.java
@@ -0,0 +1,197 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.config;
+
+import java.util.Arrays;
+import java.util.List;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.rpc.ExporterListener;
+
+/**
+ * AbstractServiceConfig
+ *
+ * @author william.liangf
+ */
+public abstract class AbstractServiceConfig extends AbstractReferenceConfig {
+
+ private static final long serialVersionUID = 1L;
+
+ // 服务版本
+ protected String version;
+
+ // 服务分组
+ protected String group;
+
+ // 服务是否已经deprecated
+ protected Boolean deprecated;
+
+ // 延迟注册
+ protected Integer delay;
+
+ // 权重
+ protected Integer weight;
+
+ // 应用文档
+ protected String document;
+
+ // 在注册中心上注册成动态的还是静态的服务
+ protected Boolean dynamic;
+
+ // 是否使用令牌
+ protected String token;
+
+ // 访问日志
+ protected String accesslog;
+
+ // 允许执行请求数
+ private Integer executes;
+
+ protected List<ProtocolConfig> protocols;
+
+ public String getVersion() {
+ return version;
+ }
+
+ public void setVersion(String version) {
+ checkName("version", version);
+ this.version = version;
+ }
+
+ public String getGroup() {
+ return group;
+ }
+
+ public void setGroup(String group) {
+ checkName("group", group);
+ this.group = group;
+ }
+
+ public Integer getDelay() {
+ return delay;
+ }
+
+ public void setDelay(Integer delay) {
+ this.delay = delay;
+ }
+
+ public Integer getWeight() {
+ return weight;
+ }
+
+ public void setWeight(Integer weight) {
+ this.weight = weight;
+ }
+
+ @Parameter(escaped = true)
+ public String getDocument() {
+ return document;
+ }
+
+ public void setDocument(String document) {
+ this.document = document;
+ }
+
+ public String getToken() {
+ return token;
+ }
+
+ public void setToken(String token) {
+ checkName("token", token);
+ this.token = token;
+ }
+
+ public void setToken(Boolean token) {
+ if (token == null) {
+ setToken((String) null);
+ } else {
+ setToken(String.valueOf(token));
+ }
+ }
+
+ public Boolean isDeprecated() {
+ return deprecated;
+ }
+
+ public void setDeprecated(Boolean deprecated) {
+ this.deprecated = deprecated;
+ }
+
+ public Boolean isDynamic() {
+ return dynamic;
+ }
+
+ public void setDynamic(Boolean dynamic) {
+ this.dynamic = dynamic;
+ }
+
+ public List<ProtocolConfig> getProtocols() {
+ return protocols;
+ }
+
+ @SuppressWarnings({ "unchecked" })
+ public void setProtocols(List<? extends ProtocolConfig> protocols) {
+ this.protocols = (List<ProtocolConfig>)protocols;
+ }
+
+ public ProtocolConfig getProtocol() {
+ return protocols == null || protocols.size() == 0 ? null : protocols.get(0);
+ }
+
+ public void setProtocol(ProtocolConfig protocol) {
+ this.protocols = Arrays.asList(new ProtocolConfig[] {protocol});
+ }
+
+ public String getAccesslog() {
+ return accesslog;
+ }
+
+ public void setAccesslog(String accesslog) {
+ this.accesslog = accesslog;
+ }
+
+ public void setAccesslog(Boolean accesslog) {
+ if (accesslog == null) {
+ setAccesslog((String) null);
+ } else {
+ setAccesslog(String.valueOf(accesslog));
+ }
+ }
+
+ public Integer getExecutes() {
+ return executes;
+ }
+
+ public void setExecutes(Integer executes) {
+ this.executes = executes;
+ }
+
+ @Parameter(key = Constants.SERVICE_FILTER_KEY)
+ public String getFilter() {
+ return super.getFilter();
+ }
+
+ @Parameter(key = Constants.EXPORTER_LISTENER_KEY)
+ public String getListener() {
+ return super.getListener();
+ }
+
+ @Override
+ public void setListener(String listener) {
+ checkMultiExtension(ExporterListener.class, "listener", listener);
+ super.setListener(listener);
+ }
+}
\ No newline at end of file
diff --git a/dubbo-config/src/main/java/com/alibaba/dubbo/config/ApplicationConfig.java b/dubbo-config/src/main/java/com/alibaba/dubbo/config/ApplicationConfig.java
new file mode 100644
index 0000000..fcf6840
--- /dev/null
+++ b/dubbo-config/src/main/java/com/alibaba/dubbo/config/ApplicationConfig.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.config;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import com.alibaba.dubbo.common.Constants;
+
+
+/**
+ * ApplicationConfig
+ *
+ * @author william.liangf
+ */
+public class ApplicationConfig extends AbstractConfig {
+
+ private static final long serialVersionUID = 5508512956753757169L;
+
+ // 应用名称
+ private String name;
+
+ // 应用负责人
+ private String owner;
+
+ // 组织名(BU或部门)
+ private String organization;
+
+ // 分层
+ private String architecture;
+
+ // 环境,如:dev/test/run
+ private String environment;
+
+ // 注册中心
+ protected List<RegistryConfig> registries;
+
+ // 服务监控
+ private MonitorConfig monitor;
+
+ public ApplicationConfig() {
+ }
+
+ public ApplicationConfig(String name) {
+ setName(name);
+ }
+
+ @Parameter(key = Constants.APPLICATION_KEY, required = true)
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ checkName("name", name);
+ this.name = name;
+ }
+
+ public String getOwner() {
+ return owner;
+ }
+
+ public void setOwner(String owner) {
+ checkName("owner", owner);
+ this.owner = owner;
+ }
+
+ public String getOrganization() {
+ return organization;
+ }
+
+ public void setOrganization(String organization) {
+ checkName("organization", organization);
+ this.organization = organization;
+ }
+
+ public String getArchitecture() {
+ return architecture;
+ }
+
+ public void setArchitecture(String architecture) {
+ checkName("architecture", architecture);
+ this.architecture = architecture;
+ }
+
+ public String getEnvironment() {
+ return environment;
+ }
+
+ public void setEnvironment(String environment) {
+ checkName("environment", environment);
+ if(environment != null) {
+ if (! ("develop".equals(environment) || "test".equals(environment) || "product".equals(environment))) {
+ throw new IllegalStateException("Unsupported environment: " + environment + ", only support develop/test/product, default is product.");
+ }
+ }
+ this.environment = environment;
+ }
+
+ public RegistryConfig getRegistry() {
+ return registries == null || registries.size() == 0 ? null : registries.get(0);
+ }
+
+ public void setRegistry(RegistryConfig registry) {
+ List<RegistryConfig> registries = new ArrayList<RegistryConfig>(1);
+ registries.add(registry);
+ this.registries = registries;
+ }
+
+ public List<RegistryConfig> getRegistries() {
+ return registries;
+ }
+
+ @SuppressWarnings({ "unchecked" })
+ public void setRegistries(List<? extends RegistryConfig> registries) {
+ this.registries = (List<RegistryConfig>)registries;
+ }
+
+ public MonitorConfig getMonitor() {
+ return monitor;
+ }
+
+ public void setMonitor(MonitorConfig monitor) {
+ this.monitor = monitor;
+ }
+
+ public void setMonitor(String monitor) {
+ this.monitor = new MonitorConfig(monitor);
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-config/src/main/java/com/alibaba/dubbo/config/ArgumentConfig.java b/dubbo-config/src/main/java/com/alibaba/dubbo/config/ArgumentConfig.java
new file mode 100644
index 0000000..6648e12
--- /dev/null
+++ b/dubbo-config/src/main/java/com/alibaba/dubbo/config/ArgumentConfig.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.config;
+
+import java.io.Serializable;
+
+/**
+ * @author chao.liuc
+ */
+public class ArgumentConfig implements Serializable {
+
+ private static final long serialVersionUID = -2165482463925213595L;
+
+ //arugment index -1 represents not set
+ private Integer index = -1;
+
+ //argument type
+ private String type;
+
+ //callback interface
+ private Boolean callback;
+
+ public void setIndex(Integer index) {
+ this.index = index;
+ }
+ @Parameter(excluded = true)
+ public Integer getIndex() {
+ return index;
+ }
+ @Parameter(excluded = true)
+ public String getType() {
+ return type;
+ }
+
+ public void setType(String type) {
+ this.type = type;
+ }
+
+ public void setCallback(Boolean callback) {
+ this.callback = callback;
+ }
+
+ public Boolean isCallback() {
+ return callback;
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-config/src/main/java/com/alibaba/dubbo/config/ConsumerConfig.java b/dubbo-config/src/main/java/com/alibaba/dubbo/config/ConsumerConfig.java
new file mode 100644
index 0000000..4006484
--- /dev/null
+++ b/dubbo-config/src/main/java/com/alibaba/dubbo/config/ConsumerConfig.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.config;
+
+/**
+ * ConsumerConfig
+ *
+ * @author william.liangf
+ */
+public class ConsumerConfig extends AbstractConsumerConfig {
+
+ private static final long serialVersionUID = 2827274711143680600L;
+
+}
\ No newline at end of file
diff --git a/dubbo-config/src/main/java/com/alibaba/dubbo/config/MethodConfig.java b/dubbo-config/src/main/java/com/alibaba/dubbo/config/MethodConfig.java
new file mode 100644
index 0000000..d1dab41
--- /dev/null
+++ b/dubbo-config/src/main/java/com/alibaba/dubbo/config/MethodConfig.java
@@ -0,0 +1,208 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.config;
+
+import java.util.List;
+
+import com.alibaba.dubbo.rpc.RpcConstants;
+
+/**
+ * MethodConfig
+ *
+ * @author william.liangf
+ */
+public class MethodConfig extends AbstractMethodConfig {
+
+ private static final long serialVersionUID = 884908855422675941L;
+
+ // 方法名
+ private String name;
+
+ // 统计参数
+ private Integer stat;
+
+ // 是否重试
+ private Boolean retry;
+
+ // 是否为可靠异步
+ private Boolean reliable;
+
+ // 方法使用线程数限制
+ private Integer executes;
+
+ // 是否过时
+ private Boolean deprecated;
+
+ // 是否需要开启stiky策略
+ private Boolean sticky;
+
+ // 是否需要返回
+ private Boolean isReturn;
+
+ //异步调用回调实例
+ private Object oninvoke;
+
+ //异步调用回调方法
+ private String oninvokeMethod;
+
+ //异步调用回调实例
+ private Object onreturn;
+
+ //异步调用回调方法
+ private String onreturnMethod;
+
+ //异步调用异常回调实例
+ private Object onthrow;
+
+ //异步调用异常回调方法
+ private String onthrowMethod;
+
+ private List<ArgumentConfig> arguments;
+
+ @Parameter(excluded = true)
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ checkMethodName("name", name);
+ this.name = name;
+ }
+
+ public Integer getStat() {
+ return stat;
+ }
+
+ @Deprecated
+ public void setStat(Integer stat) {
+ this.stat = stat;
+ }
+
+ @Deprecated
+ public Boolean isRetry() {
+ return retry;
+ }
+
+ @Deprecated
+ public void setRetry(Boolean retry) {
+ this.retry = retry;
+ }
+
+ @Deprecated
+ public Boolean isReliable() {
+ return reliable;
+ }
+
+ @Deprecated
+ public void setReliable(Boolean reliable) {
+ this.reliable = reliable;
+ }
+
+ public Integer getExecutes() {
+ return executes;
+ }
+
+ public void setExecutes(Integer executes) {
+ this.executes = executes;
+ }
+
+ public Boolean getDeprecated() {
+ return deprecated;
+ }
+
+ public void setDeprecated(Boolean deprecated) {
+ this.deprecated = deprecated;
+ }
+
+ @SuppressWarnings("unchecked")
+ public void setArguments(List<? extends ArgumentConfig> arguments) {
+ this.arguments = (List<ArgumentConfig>) arguments;
+ }
+
+ public List<ArgumentConfig> getArguments() {
+ return arguments;
+ }
+
+ public Boolean getSticky() {
+ return sticky;
+ }
+
+ public void setSticky(Boolean sticky) {
+ this.sticky = sticky;
+ }
+
+ @Parameter(key = RpcConstants.ON_RETURN_INSTANCE_KEY, excluded = true, attribute = true)
+ public Object getOnreturn() {
+ return onreturn;
+ }
+
+ public void setOnreturn(Object onreturn) {
+ this.onreturn = onreturn;
+ }
+
+ @Parameter(key = RpcConstants.ON_RETURN_METHOD_KEY, excluded = true, attribute = true)
+ public String getOnreturnMethod() {
+ return onreturnMethod;
+ }
+
+ public void setOnreturnMethod(String onreturnMethod) {
+ this.onreturnMethod = onreturnMethod;
+ }
+
+ @Parameter(key = RpcConstants.ON_THROW_INSTANCE_KEY, excluded = true, attribute = true)
+ public Object getOnthrow() {
+ return onthrow;
+ }
+
+ public void setOnthrow(Object onthrow) {
+ this.onthrow = onthrow;
+ }
+
+ @Parameter(key = RpcConstants.ON_THROW_METHOD_KEY, excluded = true, attribute = true)
+ public String getOnthrowMethod() {
+ return onthrowMethod;
+ }
+
+ public void setOnthrowMethod(String onthrowMethod) {
+ this.onthrowMethod = onthrowMethod;
+ }
+
+ @Parameter(key = RpcConstants.ON_INVOKE_INSTANCE_KEY, excluded = true, attribute = true)
+ public Object getOninvoke() {
+ return oninvoke;
+ }
+
+ public void setOninvoke(Object oninvoke) {
+ this.oninvoke = oninvoke;
+ }
+
+ @Parameter(key = RpcConstants.ON_INVOKE_METHOD_KEY, excluded = true, attribute = true)
+ public String getOninvokeMethod() {
+ return oninvokeMethod;
+ }
+
+ public void setOninvokeMethod(String oninvokeMethod) {
+ this.oninvokeMethod = oninvokeMethod;
+ }
+
+ public Boolean isReturn() {
+ return isReturn;
+ }
+
+ public void setReturn(Boolean isReturn) {
+ this.isReturn = isReturn;
+ }
+}
\ No newline at end of file
diff --git a/dubbo-config/src/main/java/com/alibaba/dubbo/config/MonitorConfig.java b/dubbo-config/src/main/java/com/alibaba/dubbo/config/MonitorConfig.java
new file mode 100644
index 0000000..e9d5ace
--- /dev/null
+++ b/dubbo-config/src/main/java/com/alibaba/dubbo/config/MonitorConfig.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.config;
+
+import java.io.Serializable;
+
+/**
+ * MonitorConfig
+ *
+ * @author william.liangf
+ */
+public class MonitorConfig implements Serializable {
+
+ private static final long serialVersionUID = -1184681514659198203L;
+
+ private String protocol;
+
+ private String address;
+
+ private String username;
+
+ private String password;
+
+ private String group;
+
+ private String version;
+
+ 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;
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-config/src/main/java/com/alibaba/dubbo/config/Parameter.java b/dubbo-config/src/main/java/com/alibaba/dubbo/config/Parameter.java
new file mode 100644
index 0000000..066756c
--- /dev/null
+++ b/dubbo-config/src/main/java/com/alibaba/dubbo/config/Parameter.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.config;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Parameter
+ *
+ * @author william.liangf
+ */
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.METHOD})
+public @interface Parameter {
+
+ String key() default "";
+
+ boolean required() default false;
+
+ boolean excluded() default false;
+
+ boolean escaped() default false;
+
+ boolean attribute() default false;
+
+}
\ No newline at end of file
diff --git a/dubbo-config/src/main/java/com/alibaba/dubbo/config/ProtocolConfig.java b/dubbo-config/src/main/java/com/alibaba/dubbo/config/ProtocolConfig.java
new file mode 100644
index 0000000..57d629d
--- /dev/null
+++ b/dubbo-config/src/main/java/com/alibaba/dubbo/config/ProtocolConfig.java
@@ -0,0 +1,348 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.config;
+
+import java.util.Map;
+
+import com.alibaba.dubbo.common.ExtensionLoader;
+import com.alibaba.dubbo.common.serialize.Serialization;
+import com.alibaba.dubbo.common.status.StatusChecker;
+import com.alibaba.dubbo.common.threadpool.ThreadPool;
+import com.alibaba.dubbo.registry.support.AbstractRegistryFactory;
+import com.alibaba.dubbo.remoting.Codec;
+import com.alibaba.dubbo.remoting.Transporter;
+import com.alibaba.dubbo.remoting.exchange.Exchanger;
+import com.alibaba.dubbo.remoting.telnet.TelnetHandler;
+import com.alibaba.dubbo.rpc.Protocol;
+
+/**
+ * ProtocolConfig
+ *
+ * @author william.liangf
+ */
+public class ProtocolConfig extends AbstractConfig {
+
+ private static final long serialVersionUID = 6913423882496634749L;
+
+ // 服务协议
+ private String name;
+
+ // 服务IP地址(多网卡时使用)
+ private String host;
+
+ // 服务端口
+ private Integer port;
+
+ // 上下文路径
+ private String contextpath;
+
+ // 线程池类型
+ private String threadpool;
+
+ // 线程池大小(固定大小)
+ private Integer threads;
+
+ // IO线程池大小(固定大小)
+ private Integer iothreads;
+
+ // 线程池队列大小
+ private Integer queues;
+
+ // 最大接收连接数
+ private Integer accepts;
+
+ // 协议编码
+ private String codec;
+
+ // 序列化方式
+ private String serialization;
+
+ // 字符集
+ private String charset;
+
+ // 最大请求数据长度
+ private Integer payload;
+
+ // 缓存区大小
+ private Integer buffer;
+
+ // 访问日志
+ private String accesslog;
+
+ // 网络传输方式
+ private String transporter;
+
+ // 信息交换方式
+ private String exchanger;
+
+ // 服务器端实现
+ private String server;
+
+ // 客户端实现
+ private String client;
+
+ // 支持的telnet命令,多个命令用逗号分隔
+ private String telnet;
+
+ // status检查
+ private String status;
+
+ // 是否注册
+ private Boolean register;
+
+ // 参数
+ private Map<String, String> parameters;
+
+ public ProtocolConfig() {
+ }
+
+ public ProtocolConfig(String name) {
+ setName(name);
+ }
+
+ @Parameter(excluded = true)
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ checkName("name", name);
+ this.name = name;
+ }
+
+ @Parameter(excluded = true)
+ public String getHost() {
+ return host;
+ }
+
+ public void setHost(String host) {
+ checkName("host", host);
+ this.host = host;
+ }
+
+ @Parameter(excluded = true)
+ public Integer getPort() {
+ return port;
+ }
+
+ public void setPort(Integer port) {
+ this.port = port;
+ }
+
+ @Deprecated
+ @Parameter(excluded = true)
+ public String getPath() {
+ return getContextpath();
+ }
+
+ @Deprecated
+ public void setPath(String path) {
+ setContextpath(path);
+ }
+
+ @Parameter(excluded = true)
+ public String getContextpath() {
+ return contextpath;
+ }
+
+ public void setContextpath(String contextpath) {
+ checkPathName("contextpath", contextpath);
+ this.contextpath = contextpath;
+ }
+
+ public String getThreadpool() {
+ return threadpool;
+ }
+
+ public void setThreadpool(String threadpool) {
+ checkExtension(ThreadPool.class, "threadpool", threadpool);
+ this.threadpool = threadpool;
+ }
+
+ public Integer getThreads() {
+ return threads;
+ }
+
+ public void setThreads(Integer threads) {
+ this.threads = threads;
+ }
+
+ public Integer getIothreads() {
+ return iothreads;
+ }
+
+ public void setIothreads(Integer iothreads) {
+ this.iothreads = iothreads;
+ }
+
+ public Integer getQueues() {
+ return queues;
+ }
+
+ public void setQueues(Integer queues) {
+ this.queues = queues;
+ }
+
+ public Integer getAccepts() {
+ return accepts;
+ }
+
+ public void setAccepts(Integer accepts) {
+ this.accepts = accepts;
+ }
+
+ public String getCodec() {
+ return codec;
+ }
+
+ public void setCodec(String codec) {
+ if ("dubbo".equals(name)) {
+ checkMultiExtension(Codec.class, "codec", codec);
+ }
+ this.codec = codec;
+ }
+
+ public String getSerialization() {
+ return serialization;
+ }
+
+ public void setSerialization(String serialization) {
+ if ("dubbo".equals(name)) {
+ checkMultiExtension(Serialization.class, "serialization", serialization);
+ }
+ this.serialization = serialization;
+ }
+
+ public String getCharset() {
+ return charset;
+ }
+
+ public void setCharset(String charset) {
+ this.charset = charset;
+ }
+
+ public Integer getPayload() {
+ return payload;
+ }
+
+ public void setPayload(Integer payload) {
+ this.payload = payload;
+ }
+
+ public Integer getBuffer() {
+ return buffer;
+ }
+
+ public void setBuffer(Integer buffer) {
+ this.buffer = buffer;
+ }
+
+ public String getServer() {
+ return server;
+ }
+
+ public void setServer(String server) {
+ if ("dubbo".equals(name)) {
+ checkMultiExtension(Transporter.class, "server", server);
+ }
+ this.server = server;
+ }
+
+ public String getClient() {
+ return client;
+ }
+
+ public void setClient(String client) {
+ if ("dubbo".equals(name)) {
+ checkMultiExtension(Transporter.class, "client", client);
+ }
+ this.client = client;
+ }
+
+ public String getAccesslog() {
+ return accesslog;
+ }
+
+ public void setAccesslog(String accesslog) {
+ this.accesslog = accesslog;
+ }
+
+ public String getTelnet() {
+ return telnet;
+ }
+
+ public void setTelnet(String telnet) {
+ checkMultiExtension(TelnetHandler.class, "telnet", telnet);
+ this.telnet = telnet;
+ }
+
+ public String getStatus() {
+ return status;
+ }
+
+ public void setStatus(String status) {
+ checkMultiExtension(StatusChecker.class, "status", status);
+ this.status = status;
+ }
+
+ public Boolean isRegister() {
+ return register;
+ }
+
+ public void setRegister(Boolean register) {
+ this.register = register;
+ }
+
+ public String getTransporter() {
+ return transporter;
+ }
+
+ public void setTransporter(String transporter) {
+ checkExtension(Transporter.class, "transporter", transporter);
+ this.transporter = transporter;
+ }
+
+ public String getExchanger() {
+ return exchanger;
+ }
+
+ public void setExchanger(String exchanger) {
+ checkExtension(Exchanger.class, "exchanger", exchanger);
+ this.exchanger = exchanger;
+ }
+
+ public Map<String, String> getParameters() {
+ return parameters;
+ }
+
+ public void setParameters(Map<String, String> parameters) {
+ this.parameters = parameters;
+ }
+
+ public void destory() {
+ if (name != null) {
+ ExtensionLoader.getExtensionLoader(Protocol.class).getExtension(name).destroy();;
+ }
+ }
+
+ public static void destroyAll() {
+ AbstractRegistryFactory.destroyAll();
+ for (String protocol : ExtensionLoader.getExtensionLoader(Protocol.class).getSupportedExtensions()) {
+ ExtensionLoader.getExtensionLoader(Protocol.class).getExtension(protocol).destroy();
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-config/src/main/java/com/alibaba/dubbo/config/ProviderConfig.java b/dubbo-config/src/main/java/com/alibaba/dubbo/config/ProviderConfig.java
new file mode 100644
index 0000000..7830e77
--- /dev/null
+++ b/dubbo-config/src/main/java/com/alibaba/dubbo/config/ProviderConfig.java
@@ -0,0 +1,340 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.config;
+
+import java.util.Arrays;
+
+import com.alibaba.dubbo.common.status.StatusChecker;
+import com.alibaba.dubbo.common.threadpool.ThreadPool;
+import com.alibaba.dubbo.remoting.Transporter;
+import com.alibaba.dubbo.remoting.exchange.Exchanger;
+import com.alibaba.dubbo.remoting.telnet.TelnetHandler;
+
+/**
+ * ProviderConfig
+ *
+ * @see com.alibaba.dubbo.config.ProtocolConfig
+ * @see com.alibaba.dubbo.config.ServiceConfig
+ * @author william.liangf
+ */
+public class ProviderConfig extends AbstractServiceConfig {
+
+ private static final long serialVersionUID = 6913423882496634749L;
+
+ // ======== 协议缺省值,当协议属性未设置时使用该缺省值替代 ========
+
+ // 服务IP地址(多网卡时使用)
+ private String host;
+
+ // 服务端口
+ private Integer port;
+
+ // 上下
+ private String contextpath;
+
+ // 线程池类型
+ private String threadpool;
+
+ // 线程池大小(固定大小)
+ private Integer threads;
+
+ // IO线程池大小(固定大小)
+ private Integer iothreads;
+
+ // 线程池队列大小
+ private Integer queues;
+
+ // 最大接收连接数
+ private Integer accepts;
+
+ // 协议编码
+ private String codec;
+
+ // 序列化方式
+ private String serialization;
+
+ // 字符集
+ private String charset;
+
+ // 最大请求数据长度
+ private Integer payload;
+
+ // 缓存区大小
+ private Integer buffer;
+
+ // 网络传输方式
+ private String transporter;
+
+ // 信息交换方式
+ private String exchanger;
+
+ // 服务器端实现
+ private String server;
+
+ // 客户端实现
+ private String client;
+
+ // 支持的telnet命令,多个命令用逗号分隔
+ private String telnet;
+
+ // status检查
+ private String status;
+
+ // 停止时等候时间
+ private Integer wait;
+
+ // 是否为缺省
+ private Boolean isDefault;
+
+ @Deprecated
+ public void setProtocol(String protocol) {
+ this.protocols = Arrays.asList(new ProtocolConfig[] {new ProtocolConfig(protocol)});
+ }
+
+ @Parameter(excluded = true)
+ public Boolean isDefault() {
+ return isDefault;
+ }
+
+ @Deprecated
+ public void setDefault(Boolean isDefault) {
+ this.isDefault = isDefault;
+ }
+
+ @Parameter(excluded = true)
+ public String getHost() {
+ return host;
+ }
+
+ public void setHost(String host) {
+ this.host = host;
+ }
+
+ @Parameter(excluded = true)
+ public Integer getPort() {
+ return port;
+ }
+
+ @Deprecated
+ public void setPort(Integer port) {
+ this.port = port;
+ }
+
+ @Deprecated
+ @Parameter(excluded = true)
+ public String getPath() {
+ return getContextpath();
+ }
+
+ @Deprecated
+ public void setPath(String path) {
+ setContextpath(path);
+ }
+
+ @Parameter(excluded = true)
+ public String getContextpath() {
+ return contextpath;
+ }
+
+ public void setContextpath(String contextpath) {
+ checkPathName("contextpath", contextpath);
+ this.contextpath = contextpath;
+ }
+
+ public String getThreadpool() {
+ return threadpool;
+ }
+
+ public void setThreadpool(String threadpool) {
+ checkExtension(ThreadPool.class, "threadpool", threadpool);
+ this.threadpool = threadpool;
+ }
+
+ public Integer getThreads() {
+ return threads;
+ }
+
+ public void setThreads(Integer threads) {
+ this.threads = threads;
+ }
+
+ public Integer getIothreads() {
+ return iothreads;
+ }
+
+ public void setIothreads(Integer iothreads) {
+ this.iothreads = iothreads;
+ }
+
+ public Integer getQueues() {
+ return queues;
+ }
+
+ public void setQueues(Integer queues) {
+ this.queues = queues;
+ }
+
+ public Integer getAccepts() {
+ return accepts;
+ }
+
+ public void setAccepts(Integer accepts) {
+ this.accepts = accepts;
+ }
+
+ public String getCodec() {
+ return codec;
+ }
+
+ public void setCodec(String codec) {
+ this.codec = codec;
+ }
+
+ public String getSerialization() {
+ return serialization;
+ }
+
+ public void setSerialization(String serialization) {
+ this.serialization = serialization;
+ }
+
+ public String getCharset() {
+ return charset;
+ }
+
+ public void setCharset(String charset) {
+ this.charset = charset;
+ }
+
+ public Integer getPayload() {
+ return payload;
+ }
+
+ public void setPayload(Integer payload) {
+ this.payload = payload;
+ }
+
+ public Integer getBuffer() {
+ return buffer;
+ }
+
+ public void setBuffer(Integer buffer) {
+ this.buffer = buffer;
+ }
+
+ public String getServer() {
+ return server;
+ }
+
+ public void setServer(String server) {
+ this.server = server;
+ }
+
+ public String getClient() {
+ return client;
+ }
+
+ public void setClient(String client) {
+ this.client = client;
+ }
+
+ public String getTelnet() {
+ return telnet;
+ }
+
+ public void setTelnet(String telnet) {
+ checkMultiExtension(TelnetHandler.class, "telnet", telnet);
+ this.telnet = telnet;
+ }
+
+ public String getStatus() {
+ return status;
+ }
+
+ public void setStatus(String status) {
+ checkMultiExtension(StatusChecker.class, "status", status);
+ this.status = status;
+ }
+
+ @Parameter(key = "default.cluster")
+ @Override
+ public String getCluster() {
+ return super.getCluster();
+ }
+
+ @Parameter(key = "default.connections")
+ @Override
+ public Integer getConnections() {
+ return super.getConnections();
+ }
+
+ @Parameter(key = "default.timeout")
+ @Override
+ public Integer getTimeout() {
+ return super.getTimeout();
+ }
+
+ @Parameter(key = "default.retries")
+ @Override
+ public Integer getRetries() {
+ return super.getRetries();
+ }
+
+ @Parameter(key = "default.loadbalance")
+ @Override
+ public String getLoadbalance() {
+ return super.getLoadbalance();
+ }
+
+ @Parameter(key = "default.async")
+ @Override
+ public Boolean isAsync() {
+ return super.isAsync();
+ }
+
+ @Parameter(key = "default.actives")
+ @Override
+ public Integer getActives() {
+ return super.getActives();
+ }
+
+ public String getTransporter() {
+ return transporter;
+ }
+
+ public void setTransporter(String transporter) {
+ checkExtension(Transporter.class, "transporter", transporter);
+ this.transporter = transporter;
+ }
+
+ public String getExchanger() {
+ return exchanger;
+ }
+
+ public void setExchanger(String exchanger) {
+ checkExtension(Exchanger.class, "exchanger", exchanger);
+ this.exchanger = exchanger;
+ }
+
+ public Integer getWait() {
+ return wait;
+ }
+
+ public void setWait(Integer wait) {
+ this.wait = wait;
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-config/src/main/java/com/alibaba/dubbo/config/ReferenceConfig.java b/dubbo-config/src/main/java/com/alibaba/dubbo/config/ReferenceConfig.java
new file mode 100644
index 0000000..c5abd7f
--- /dev/null
+++ b/dubbo-config/src/main/java/com/alibaba/dubbo/config/ReferenceConfig.java
@@ -0,0 +1,524 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.config;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.ExtensionLoader;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.Version;
+import com.alibaba.dubbo.common.bytecode.Wrapper;
+import com.alibaba.dubbo.common.utils.ConfigUtils;
+import com.alibaba.dubbo.common.utils.NetUtils;
+import com.alibaba.dubbo.common.utils.ReflectUtils;
+import com.alibaba.dubbo.common.utils.StringUtils;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.Protocol;
+import com.alibaba.dubbo.rpc.ProxyFactory;
+import com.alibaba.dubbo.rpc.RpcConstants;
+import com.alibaba.dubbo.rpc.StaticContext;
+import com.alibaba.dubbo.rpc.cluster.Cluster;
+import com.alibaba.dubbo.rpc.cluster.directory.StaticDirectory;
+import com.alibaba.dubbo.rpc.cluster.support.AvailableCluster;
+import com.alibaba.dubbo.rpc.cluster.support.ClusterUtils;
+import com.alibaba.dubbo.rpc.service.GenericService;
+
+/**
+ * ReferenceConfig
+ *
+ * @author william.liangf
+ */
+public class ReferenceConfig<T> extends AbstractConsumerConfig {
+
+ private static final long serialVersionUID = -5864351140409987595L;
+
+ private static final Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
+
+ private static final Cluster cluster = ExtensionLoader.getExtensionLoader(Cluster.class).getAdaptiveExtension();
+
+ private static final ProxyFactory proxyFactory = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();
+
+ // 接口类型
+ private String interfaceName;
+
+ private Class<?> interfaceClass;
+
+ // 版本
+ private String version;
+
+ // 服务分组
+ private String group;
+
+ // 客户端类型
+ private String client;
+
+ // 点对点直连服务提供地址
+ private String url;
+
+ // 方法配置
+ private List<MethodConfig> methods;
+
+ // 缺省配置
+ private ConsumerConfig consumer;
+
+ // 接口代理类引用
+ private transient T ref;
+
+ private transient Invoker<?> invoker;
+
+ private transient boolean initialized;
+
+ private transient boolean destroyed;
+
+ public synchronized T get() {
+ if (destroyed){
+ throw new IllegalStateException("Already destroyed!");
+ }
+ if (ref == null) {
+ init();
+ }
+ return ref;
+ }
+
+ public synchronized void destroy() {
+ if (ref == null) {
+ throw new IllegalStateException("Uninitialized.");
+ }
+ if (destroyed){
+ return;
+ }
+ destroyed = true;
+ try {
+ invoker.destroy();
+ } catch (Throwable t) {
+ logger.warn("Unexpected err when destroy invoker of ReferenceConfig(" + url + ").", t);
+ }
+ invoker = null;
+ ref = null;
+ }
+
+ private void init() {
+ if (initialized) {
+ return;
+ }
+ initialized = true;
+ if (interfaceName == null || interfaceName.length() == 0) {
+ throw new IllegalStateException("<dubbo:reference interface=\"\" /> interface not allow null!");
+ }
+ // 获取消费者全局配置
+ checkDefault();
+ if (generic == null && consumer != null) {
+ generic = consumer.isGeneric();
+ }
+ if (generic == null) {
+ generic = false;
+ }
+ if (generic) {
+ interfaceClass = GenericService.class;
+ } else {
+ try {
+ interfaceClass = Class.forName(interfaceName, true, Thread.currentThread()
+ .getContextClassLoader());
+ } catch (ClassNotFoundException e) {
+ throw new IllegalStateException(e.getMessage(), e);
+ }
+ checkInterface();
+ }
+ String resolve = System.getProperty(interfaceName);
+ String resolveFile = null;
+ if (resolve == null || resolve.length() == 0) {
+ resolveFile = System.getProperty("dubbo.resolve.file");
+ if (resolveFile == null || resolveFile.length() == 0) {
+ File userResolveFile = new File(new File(System.getProperty("user.home")), "dubbo-resolve.properties");
+ if (userResolveFile.exists()) {
+ resolveFile = userResolveFile.getAbsolutePath();
+ }
+ }
+ if (resolveFile != null && resolveFile.length() > 0) {
+ Properties properties = new Properties();
+ FileInputStream fis = null;
+ try {
+ fis = new FileInputStream(new File(resolveFile));
+ properties.load(fis);
+ } catch (IOException e) {
+ throw new IllegalStateException("Unload " + resolveFile + ", cause: " + e.getMessage(), e);
+ } finally {
+ try {
+ if(null != fis) fis.close();
+ } catch (IOException e) {
+ logger.warn(e.getMessage(), e);
+ }
+ }
+ resolve = properties.getProperty(interfaceName);
+ }
+ }
+ if (resolve != null && resolve.length() > 0) {
+ url = resolve;
+ if (logger.isWarnEnabled()) {
+ if (resolveFile != null && resolveFile.length() > 0) {
+ logger.warn("Using default dubbo resolve file " + resolveFile + " replace " + interfaceName + "" + resolve + " to p2p invoke remote service.");
+ } else {
+ logger.warn("Using -D" + interfaceName + "=" + resolve + " to p2p invoke remote service.");
+ }
+ }
+ }
+ if (consumer != null) {
+ if (application == null) {
+ application = consumer.getApplication();
+ }
+ if (registries == null) {
+ registries = consumer.getRegistries();
+ }
+ if (monitor == null) {
+ monitor = consumer.getMonitor();
+ }
+ }
+ if (application != null) {
+ if (registries == null) {
+ registries = application.getRegistries();
+ }
+ if (monitor == null) {
+ monitor = application.getMonitor();
+ }
+ }
+ checkApplication();
+ Map<String, String> map = new HashMap<String, String>();
+ Map<Object, Object> attributes = new HashMap<Object, Object>();
+ map.put("dubbo", Version.getVersion());
+ if (! generic) {
+ map.put("revision", Version.getVersion(interfaceClass, version));
+ map.put("methods", StringUtils.join(new HashSet<String>(Arrays.asList(Wrapper.getWrapper(interfaceClass).getDeclaredMethodNames())), ","));
+ }
+ map.put(Constants.INTERFACE_KEY, interfaceName);
+ appendParameters(map, application);
+ appendParameters(map, consumer, Constants.DEFAULT_KEY);
+ appendParameters(map, this);
+ String prifix = StringUtils.getServiceKey(map);
+ if (methods != null && methods.size() > 0) {
+ for (MethodConfig method : methods) {
+ appendParameters(map, method, method.getName());
+ appendAttributes(attributes, method, prifix +"."+method.getName());
+ checkAndConvertImplicitConfig(method, map, attributes);
+ }
+ }
+ //attributes通过系统context进行存储.
+ StaticContext.getSystemContext().putAll(attributes);
+ ref = createProxy(map);
+ }
+
+ private static void checkAndConvertImplicitConfig(MethodConfig method, Map<String,String> map, Map<Object,Object> attributes){
+ //check config conflict
+ if (Boolean.FALSE.equals(method.isReturn()) && (method.getOnreturn() != null || method.getOnthrow() != null)) {
+ throw new IllegalStateException("method config error : return attribute must be set true when onreturn or onthrow has been setted.");
+ }
+ //convert onreturn methodName to Method
+ String onReturnMethodKey = StaticContext.getKey(map,method.getName(),RpcConstants.ON_RETURN_METHOD_KEY);
+ Object onReturnMethod = attributes.get(onReturnMethodKey);
+ if (onReturnMethod != null && onReturnMethod instanceof String){
+ attributes.put(onReturnMethodKey, getMethodByName(method.getOnreturn().getClass(), onReturnMethod.toString()));
+ }
+ //convert onthrow methodName to Method
+ String onThrowMethodKey = StaticContext.getKey(map,method.getName(),RpcConstants.ON_THROW_METHOD_KEY);
+ Object onThrowMethod = attributes.get(onThrowMethodKey);
+ if (onThrowMethod != null && onThrowMethod instanceof String){
+ attributes.put(onThrowMethodKey, getMethodByName(method.getOnthrow().getClass(), onThrowMethod.toString()));
+ }
+ //convert oninvoke methodName to Method
+ String onInvokeMethodKey = StaticContext.getKey(map,method.getName(),RpcConstants.ON_INVOKE_METHOD_KEY);
+ Object onInvokeMethod = attributes.get(onInvokeMethodKey);
+ if (onInvokeMethod != null && onInvokeMethod instanceof String){
+ attributes.put(onInvokeMethodKey, getMethodByName(method.getOninvoke().getClass(), onInvokeMethod.toString()));
+ }
+ }
+
+ private static Method getMethodByName(Class<?> clazz, String methodName){
+ try {
+ return ReflectUtils.findMethodByMethodName(clazz, methodName);
+ } catch (Exception e) {
+ throw new IllegalStateException(e);
+ }
+ }
+
+ @SuppressWarnings({ "unchecked", "rawtypes" })
+ private T createProxy(Map<String, String> map) {
+ Boolean j = injvm;
+ if (j == null && consumer != null) {
+ j = consumer.isInjvm();
+ }
+ if (j != null && j) {
+ URL url = new URL("injvm", NetUtils.LOCALHOST, 0, interfaceClass.getName()).addParameters(map);
+ invoker = protocol.refer(interfaceClass, url);
+ if (logger.isInfoEnabled()) {
+ logger.info("Using injvm service " + interfaceClass.getName());
+ }
+ } else {
+ List<URL> urls = new ArrayList<URL>();
+ if (url != null && url.length() > 0) { // 用户指定URL,指定的URL可能是对点对直连地址,也可能是注册中心URL
+ String[] us = Constants.SEMICOLON_SPLIT_PATTERN.split(url);
+ if (us != null && us.length > 0) {
+ for (String u : us) {
+ URL url = URL.valueOf(u);
+ if (url.getPath() == null || url.getPath().length() == 0) {
+ url = url.setPath(interfaceName);
+ }
+ if (Constants.REGISTRY_PROTOCOL.equals(url.getProtocol())) {
+ urls.add(url.addParameterAndEncoded(RpcConstants.REFER_KEY, StringUtils.toQueryString(map)));
+ } else {
+ urls.add(ClusterUtils.mergeUrl(url, map));
+ }
+ }
+ }
+ } else { // 通过注册中心配置拼装URL
+ List<URL> us = loadRegistries();
+ if (us != null && us.size() > 0) {
+ for (URL u : us) {
+ URL monitorUrl = loadMonitor(u);
+ if (monitorUrl != null) {
+ map.put(Constants.MONITOR_KEY, URL.encode(monitorUrl.toFullString()));
+ }
+ urls.add(u.addParameterAndEncoded(RpcConstants.REFER_KEY, StringUtils.toQueryString(map)));
+ }
+ }
+ if (urls == null || urls.size() == 0) {
+ throw new IllegalStateException("No such any registry to reference " + interfaceName + " on the consumer " + NetUtils.getLocalHost() + " use dubbo version " + Version.getVersion() + ", please config <dubbo:registry address=\"...\" /> to your spring config.");
+ }
+ }
+ if (urls.size() == 1) {
+ invoker = protocol.refer(interfaceClass, urls.get(0));
+ } else {
+ List<Invoker<?>> invokers = new ArrayList<Invoker<?>>();
+ URL registryURL = null;
+ for (URL url : urls) {
+ invokers.add(protocol.refer(interfaceClass, url));
+ if (Constants.REGISTRY_PROTOCOL.equals(url.getProtocol())) {
+ registryURL = url; // 用了最后一个registry url
+ }
+ }
+ if (registryURL != null) { // 有 注册中心协议的URL
+ // 对有注册中心的Cluster 只用 AvailableCluster
+ URL u = registryURL.addParameter(Constants.CLUSTER_KEY, AvailableCluster.NAME);
+ invoker = cluster.merge(new StaticDirectory(u, invokers));
+ } else { // 不是 注册中心的URL
+ invoker = cluster.merge(new StaticDirectory(invokers));
+ }
+ }
+ }
+ Boolean c = check;
+ if (c == null && consumer != null) {
+ c = consumer.isCheck();
+ }
+ if (c == null) {
+ c = true; // default true
+ }
+ if (c && ! invoker.isAvailable()) {
+ throw new IllegalStateException("Failed to check the status of the service " + interfaceName + ". No provider available for the service " + (group == null ? "" : group + "/") + interfaceName + (version == null ? "" : ":" + version) + " from the url " + invoker.getUrl() + " to the consumer " + NetUtils.getLocalHost() + " use dubbo version " + Version.getVersion());
+ }
+ if (logger.isInfoEnabled()) {
+ logger.info("Refer dubbo service " + interfaceClass.getName() + " from url " + invoker.getUrl());
+ }
+ // 创建服务代理
+ return (T) proxyFactory.getProxy(invoker);
+ }
+
+ private void checkInterface() {
+ // 检查接口类型必需为接口
+ 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:reference interface=\"" + interfaceClass.getName() + "\" ... ><dubbo:method name=\"\" ... /></<dubbo:reference>");
+ }
+ boolean hasMethod = false;
+ for (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);
+ }
+ }
+ }
+ 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 stub 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)) {
+ 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());
+ }
+ }
+ }
+
+ private void checkDefault() {
+ if (consumer == null) {
+ consumer = new ConsumerConfig();
+ String t = getLegacyProperty("dubbo.service.invoke.timeout");
+ if (t != null && t.length() > 0) {
+ consumer.setTimeout(Integer.parseInt(t.trim()));
+ }
+ String r = getLegacyProperty("dubbo.service.max.retry.providers");
+ if (r != null && r.length() > 0) {
+ consumer.setRetries(Integer.parseInt(r.trim()) - 1);
+ }
+ String c = getLegacyProperty("dubbo.service.allow.no.provider");
+ if (c != null && c.length() > 0) {
+ consumer.setCheck(!Boolean.parseBoolean(c));
+ }
+ }
+ }
+
+ public Class<?> getInterfaceClass() {
+ if (interfaceClass != null) {
+ return interfaceClass;
+ }
+ if ((generic != null && generic.booleanValue())
+ || (consumer != null && consumer.isGeneric() != null
+ && consumer.isGeneric().booleanValue())) {
+ return GenericService.class;
+ }
+ try {
+ if (interfaceName != null && interfaceName.length() > 0) {
+ this.interfaceClass = Class.forName(interfaceName, true, Thread.currentThread()
+ .getContextClassLoader());
+ }
+ } catch (ClassNotFoundException t) {
+ throw new IllegalStateException(t.getMessage(), t);
+ }
+ return interfaceClass;
+ }
+
+ /**
+ * @deprecated
+ * @see #setInterface(Class)
+ * @param interfaceClass
+ */
+ @Deprecated
+ public void setInterfaceClass(Class<?> interfaceClass) {
+ setInterface(interfaceClass);
+ }
+
+ public String getInterface() {
+ return interfaceName;
+ }
+
+ public void setInterface(String interfaceName) {
+ this.interfaceName = interfaceName;
+ }
+
+ public void setInterface(Class<?> interfaceClass) {
+ if (interfaceClass != null && ! interfaceClass.isInterface()) {
+ throw new IllegalStateException("The interface class " + interfaceClass + " is not a interface!");
+ }
+ this.interfaceClass = interfaceClass;
+ this.interfaceName = interfaceClass == null ? null : interfaceClass.getName();
+ }
+
+ public String getVersion() {
+ return version;
+ }
+
+ public void setVersion(String version) {
+ checkName("version", version);
+ this.version = version;
+ }
+
+ public String getGroup() {
+ return group;
+ }
+
+ public void setGroup(String group) {
+ checkName("group", group);
+ this.group = group;
+ }
+
+ public String getClient() {
+ return client;
+ }
+
+ public void setClient(String client) {
+ checkName("client", client);
+ this.client = client;
+ }
+
+ @Parameter(excluded = true)
+ public String getUrl() {
+ return url;
+ }
+
+ public void setUrl(String url) {
+ this.url = url;
+ }
+
+ public List<MethodConfig> getMethods() {
+ return methods;
+ }
+
+ @SuppressWarnings("unchecked")
+ public void setMethods(List<? extends MethodConfig> methods) {
+ this.methods = (List<MethodConfig>) methods;
+ }
+
+ public ConsumerConfig getConsumer() {
+ return consumer;
+ }
+
+ public void setConsumer(ConsumerConfig consumer) {
+ this.consumer = consumer;
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-config/src/main/java/com/alibaba/dubbo/config/RegistryConfig.java b/dubbo-config/src/main/java/com/alibaba/dubbo/config/RegistryConfig.java
new file mode 100644
index 0000000..dec15ea
--- /dev/null
+++ b/dubbo-config/src/main/java/com/alibaba/dubbo/config/RegistryConfig.java
@@ -0,0 +1,280 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.config;
+
+import com.alibaba.dubbo.common.ExtensionLoader;
+import com.alibaba.dubbo.registry.support.AbstractRegistryFactory;
+import com.alibaba.dubbo.remoting.Transporter;
+import com.alibaba.dubbo.rpc.RpcConstants;
+
+/**
+ * RegistryConfig
+ *
+ * @author william.liangf
+ */
+public class RegistryConfig extends AbstractConfig {
+
+ private static final long serialVersionUID = 5508512956753757169L;
+
+ public static final String NO_AVAILABLE = "N/A";
+
+ // 注册中心地址
+ private String address;
+
+ // 注册中心登录用户名
+ private String username;
+
+ // 注册中心登录密码
+ private String password;
+
+ // 注册中心缺省端口
+ private Integer port;
+
+ // 注册中心协议
+ private String protocol;
+
+ // 客户端实现
+ private String transporter;
+
+ private String server;
+
+ private String client;
+
+ private String group;
+
+ private String version;
+
+ // 注册中心请求超时时间(毫秒)
+ private Integer timeout;
+
+ // 动态注册中心列表存储文件
+ private String file;
+
+ // 停止时等候完成通知时间
+ private Integer wait;
+
+ // 启动时检查注册中心是否存在
+ private Boolean check;
+
+ // 在该注册中心上注册是动态的还是静态的服务
+ private Boolean dynamic;
+
+ // 在该注册中心上服务是否暴露
+ private Boolean register;
+
+ // 在该注册中心上服务是否引用
+ private Boolean subscribe;
+
+ 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
+ */
+ @Deprecated
+ public Integer getWait() {
+ return wait;
+ }
+
+ /**
+ * @deprecated
+ * @see com.alibaba.dubbo.config.ProviderConfig#setWait(Integer)
+ * @param wait
+ */
+ @Deprecated
+ public void setWait(Integer wait) {
+ this.wait = wait;
+ if( wait!=null && wait>0)
+ System.setProperty(RpcConstants.SHUTDOWN_TIMEOUT_KEY, String.valueOf(wait));
+ }
+
+ public Boolean isCheck() {
+ return check;
+ }
+
+ public void setCheck(Boolean check) {
+ this.check = check;
+ }
+
+ public String getFile() {
+ return file;
+ }
+
+ public void setFile(String file) {
+ checkPathLength("file", file);
+ this.file = file;
+ }
+
+ /**
+ * @deprecated
+ * @see #getTransporter()
+ * @return
+ */
+ @Deprecated
+ @Parameter(excluded = true)
+ public String getTransport() {
+ return getTransporter();
+ }
+
+ /**
+ * @deprecated
+ * @see #setTransporter(String)
+ * @param transport
+ */
+ @Deprecated
+ public void setTransport(String transport) {
+ setTransporter(transport);
+ }
+
+ public String getTransporter() {
+ return transporter;
+ }
+
+ public void setTransporter(String transporter) {
+ checkName("transporter", transporter);
+ if(transporter != null && transporter.length() > 0 && ! ExtensionLoader.getExtensionLoader(Transporter.class).hasExtension(transporter)){
+ throw new IllegalStateException("No such transporter type : " + transporter);
+ }
+ this.transporter = transporter;
+ }
+
+ public String getServer() {
+ return server;
+ }
+
+ public void setServer(String server) {
+ checkName("server", server);
+ if(server != null && server.length() > 0 && ! ExtensionLoader.getExtensionLoader(Transporter.class).hasExtension(server)){
+ throw new IllegalStateException("No such server type : " + server);
+ }
+ this.server = server;
+ }
+
+ public String getClient() {
+ return client;
+ }
+
+ public void setClient(String client) {
+ checkName("client", client);
+ if(client != null && client.length() > 0 && ! ExtensionLoader.getExtensionLoader(Transporter.class).hasExtension(client)){
+ throw new IllegalStateException("No such client type : " + client);
+ }
+ this.client = client;
+ }
+
+ public Integer getTimeout() {
+ return timeout;
+ }
+
+ public void setTimeout(Integer timeout) {
+ this.timeout = timeout;
+ }
+
+ public Boolean isDynamic() {
+ return dynamic;
+ }
+
+ public void setDynamic(Boolean dynamic) {
+ this.dynamic = dynamic;
+ }
+
+ public Boolean isRegister() {
+ return register;
+ }
+
+ public void setRegister(Boolean register) {
+ this.register = register;
+ }
+
+ public Boolean isSubscribe() {
+ return subscribe;
+ }
+
+ public void setSubscribe(Boolean subscribe) {
+ this.subscribe = subscribe;
+ }
+
+ public static void closeAll() {
+ AbstractRegistryFactory.destroyAll();
+ }
+
+ public String getGroup() {
+ return group;
+ }
+
+ public void setGroup(String group) {
+ this.group = group;
+ }
+
+ public String getVersion() {
+ return version;
+ }
+
+ public void setVersion(String version) {
+ this.version = version;
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-config/src/main/java/com/alibaba/dubbo/config/ServiceConfig.java b/dubbo-config/src/main/java/com/alibaba/dubbo/config/ServiceConfig.java
new file mode 100644
index 0000000..c7c0048
--- /dev/null
+++ b/dubbo-config/src/main/java/com/alibaba/dubbo/config/ServiceConfig.java
@@ -0,0 +1,614 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.Socket;
+import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.ExtensionLoader;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.Version;
+import com.alibaba.dubbo.common.bytecode.Wrapper;
+import com.alibaba.dubbo.common.utils.ConfigUtils;
+import com.alibaba.dubbo.common.utils.NetUtils;
+import com.alibaba.dubbo.common.utils.ReflectUtils;
+import com.alibaba.dubbo.common.utils.StringUtils;
+import com.alibaba.dubbo.rpc.Exporter;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.Protocol;
+import com.alibaba.dubbo.rpc.ProxyFactory;
+import com.alibaba.dubbo.rpc.RpcConstants;
+import com.alibaba.dubbo.rpc.service.GenericService;
+
+/**
+ * ServiceConfig
+ *
+ * @author william.liangf
+ */
+public class ServiceConfig<T> extends AbstractServiceConfig {
+
+ private static final long serialVersionUID = 3033787999037024738L;
+
+ private static final Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
+
+ private static final ProxyFactory proxyFactory = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();
+
+ // 接口类型
+ private String interfaceName;
+
+ private Class<?> interfaceClass;
+
+ // 接口实现类引用
+ private T ref;
+
+ // 服务名称
+ private String path;
+
+ // 是否注册
+ private Boolean register;
+
+ // 方法配置
+ private List<MethodConfig> methods;
+
+ private ProviderConfig provider;
+
+ private final List<URL> urls = new ArrayList<URL>();
+
+ private final List<Exporter<?>> exporters = new ArrayList<Exporter<?>>();
+
+ private transient boolean exported;
+
+ private transient boolean unexported;
+
+ private transient boolean generic;
+
+ public boolean isExported() {
+ return exported;
+ }
+
+ public synchronized void export() {
+ if (delay != null && delay > 0) {
+ Thread thread = new Thread(new Runnable() {
+ public void run() {
+ try {
+ Thread.sleep(delay);
+ } catch (Throwable e) {
+ }
+ doExport();
+ }
+ });
+ thread.setDaemon(true);
+ thread.setName("DelayExportServiceThread");
+ thread.start();
+ } else {
+ doExport();
+ }
+ }
+
+ protected synchronized void doExport() {
+ if (unexported) {
+ throw new IllegalStateException("Already unexported!");
+ }
+ if (exported) {
+ return;
+ }
+ if (interfaceName == null || interfaceName.length() == 0) {
+ throw new IllegalStateException("<dubbo:service interface=\"\" /> interface not allow null!");
+ }
+ if (provider != null) {
+ if (application == null) {
+ application = provider.getApplication();
+ }
+ if (registries == null) {
+ registries = provider.getRegistries();
+ }
+ if (monitor == null) {
+ monitor = provider.getMonitor();
+ }
+ if (protocols == null) {
+ protocols = provider.getProtocols();
+ }
+ }
+ if (application != null) {
+ if (registries == null) {
+ registries = application.getRegistries();
+ }
+ if (monitor == null) {
+ monitor = application.getMonitor();
+ }
+ }
+ if (ref instanceof GenericService) {
+ interfaceClass = GenericService.class;
+ generic = true;
+ } else {
+ try {
+ interfaceClass = Class.forName(interfaceName, true, Thread.currentThread()
+ .getContextClassLoader());
+ } catch (ClassNotFoundException e) {
+ throw new IllegalStateException(e.getMessage(), e);
+ }
+ checkInterface();
+ 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();
+ if (path == null || path.length() == 0) {
+ path = interfaceName;
+ }
+ doExportUrls();
+ exported = true;
+ }
+
+ private void checkInterface() {
+ // 检查接口不为空,并且类型必需为接口
+ if (interfaceClass == null) {
+ throw new IllegalStateException("interface not allow null!");
+ }
+ // 检查引用不为空,并且引用必需实现接口
+ 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 + "!");
+ }
+ // 检查方法是否在接口中存在
+ 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);
+ }
+ }
+ }
+ 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)) {
+ 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());
+ }
+ }
+ }
+
+ public synchronized void unexport() {
+ if (!exported) {
+ throw new IllegalStateException("No exported!");
+ }
+ if (unexported) {
+ return;
+ }
+ if (exporters != null && exporters.size() > 0) {
+ for (Exporter<?> exporter : exporters) {
+ try {
+ exporter.unexport();
+ } catch (Throwable t) {
+ logger.warn("unexpected err when unexport" + exporter, t);
+ }
+ }
+ exporters.clear();
+ }
+ unexported = true;
+ }
+
+ @SuppressWarnings({ "unchecked", "rawtypes" })
+ private void doExportUrls() {
+ List<URL> registryURLs = loadRegistries();
+ for (ProtocolConfig protocolConfig : protocols) {
+ String name = protocolConfig.getName();
+ if (name == null || name.length() == 0) {
+ name = "dubbo";
+ }
+ String host = protocolConfig.getHost();
+ if (provider != null && (host == null || host.length() == 0)) {
+ host = provider.getHost();
+ }
+ boolean anyhost = false;
+ if (NetUtils.isInvalidLocalHost(host)) {
+ anyhost = true;
+ try {
+ host = InetAddress.getLocalHost().getHostAddress();
+ } catch (UnknownHostException e) {
+ logger.warn(e.getMessage(), e);
+ }
+ if (NetUtils.isInvalidLocalHost(host)) {
+ if (registryURLs != null && registryURLs.size() > 0) {
+ for (URL registryURL : registryURLs) {
+ try {
+ Socket socket = new Socket(registryURL.getHost(), registryURL.getPort());
+ try {
+ host = socket.getLocalAddress().getHostAddress();
+ break;
+ } finally {
+ socket.close();
+ }
+ } catch (Exception e) {
+ logger.warn(e.getMessage(), e);
+ }
+ }
+ }
+ if (NetUtils.isInvalidLocalHost(host)) {
+ host = NetUtils.getLocalHost();
+ }
+ }
+ }
+ Integer port = protocolConfig.getPort();
+ if (provider != null && (port == null || port == 0)) {
+ port = provider.getPort();
+ }
+ if (port == null || port == 0) {
+ port = ExtensionLoader.getExtensionLoader(Protocol.class).getExtension(name).getDefaultPort();
+ }
+ if (port == null || port <= 0) {
+ port = NetUtils.getAvailablePort();
+ logger.warn("Use random available port(" + port + ") for protocol " + name);
+ }
+ Map<String, String> map = new HashMap<String, String>();
+ if (anyhost) {
+ map.put(Constants.ANYHOST_KEY, "true");
+ }
+ map.put("dubbo", Version.getVersion());
+ appendParameters(map, application);
+ appendParameters(map, provider);
+ appendParameters(map, protocolConfig);
+ appendParameters(map, this);
+ map.put("prompt", "dubbo");
+ if (methods != null && methods.size() > 0) {
+ for (MethodConfig method : methods) {
+ appendParameters(map, method, method.getName());
+ List<ArgumentConfig> arguments = method.getArguments();
+ if (arguments != null && arguments.size() > 0) {
+ for (ArgumentConfig argument : arguments) {
+ //类型自动转换.
+ if(argument.getType() != null && argument.getType().length() >0){
+ Method[] methods = interfaceClass.getMethods();
+ //遍历所有方法
+ if(methods != null && methods.length > 0){
+ for (int i = 0; i < methods.length; i++) {
+ String methodName = methods[i].getName();
+ //匹配方法名称,获取方法签名.
+ if(methodName.equals(method.getName())){
+ Class<?>[] argtypes = methods[i].getParameterTypes();
+ //一个方法中单个callback
+ if (argument.getIndex() != -1 ){
+ if (argtypes[argument.getIndex()].getName().equals(argument.getType())){
+ appendParameters(map, argument, method.getName() + "." + argument.getIndex());
+ }else {
+ throw new IllegalArgumentException("argument config error : the index attribute and type attirbute not match :index :"+argument.getIndex() + ", type:" + argument.getType());
+ }
+ } else {
+ //一个方法中多个callback
+ for (int j = 0 ;j<argtypes.length ;j++) {
+ Class<?> argclazz = argtypes[j];
+ if (argclazz.getName().equals(argument.getType())){
+ appendParameters(map, argument, method.getName() + "." + j);
+ if (argument.getIndex() != -1 && argument.getIndex() != j){
+ throw new IllegalArgumentException("argument config error : the index attribute and type attirbute not match :index :"+argument.getIndex() + ", type:" + argument.getType());
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }else if(argument.getIndex() != -1){
+ appendParameters(map, argument, method.getName() + "." + argument.getIndex());
+ }else {
+ throw new IllegalArgumentException("argument config must set index or type attribute.eg: <dubbo:argument index='0' .../> or <dubbo:argument type=xxx .../>");
+ }
+
+ }
+ }
+ }
+ }
+ if (generic) {
+ map.put("generic", String.valueOf(true));
+ map.put("methods", Constants.ANY_VALUE);
+ } else {
+ map.put("revision", Version.getVersion(interfaceClass, version));
+ map.put("methods", StringUtils.join(new HashSet<String>(Arrays.asList(Wrapper.getWrapper(interfaceClass).getDeclaredMethodNames())), ","));
+ }
+ if (! ConfigUtils.isEmpty(token)) {
+ if (ConfigUtils.isDefault(token)) {
+ map.put("token", UUID.randomUUID().toString());
+ } else {
+ map.put("token", token);
+ }
+ }
+ if ("injvm".equals(protocolConfig.getName())) {
+ protocolConfig.setRegister(false);
+ map.put("notify", "false");
+ }
+ // 导出服务
+ String contextPath = protocolConfig.getContextpath();
+ if ((contextPath == null || contextPath.length() == 0) && provider != null) {
+ contextPath = provider.getContextpath();
+ }
+ URL url = new URL(name, host, port, (contextPath == null || contextPath.length() == 0 ? "" : contextPath + "/") + path, map);
+ if (logger.isInfoEnabled()) {
+ logger.info("Export dubbo service " + interfaceClass.getName() + " to url " + url);
+ }
+ if (registryURLs != null && registryURLs.size() > 0
+ && url.getParameter("register", true)) {
+ for (URL registryURL : registryURLs) {
+ URL monitorUrl = loadMonitor(registryURL);
+ if (monitorUrl != null) {
+ url = url.addParameterAndEncoded(Constants.MONITOR_KEY, monitorUrl.toFullString());
+ }
+ String providerURL = url.toFullString();
+ if (logger.isInfoEnabled()) {
+ logger.info("Register dubbo service " + interfaceClass.getName() + " url " + providerURL + " to registry " + registryURL);
+ }
+ Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, registryURL.addParameterAndEncoded(RpcConstants.EXPORT_KEY, providerURL));
+ Exporter<?> exporter = protocol.export(invoker);
+ exporters.add(exporter);
+ }
+ } else {
+ Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, url);
+ Exporter<?> exporter = protocol.export(invoker);
+ exporters.add(exporter);
+ }
+ this.urls.add(url);
+ }
+ }
+
+ private void checkProtocol() {
+ if ((protocols == null || protocols.size() == 0)
+ && provider != null) {
+ setProtocols(provider.getProtocols());
+ }
+ // 兼容旧版本
+ if (protocols == null || protocols.size() == 0) {
+ ProtocolConfig providerBean = new ProtocolConfig();
+ String p = getLegacyProperty("dubbo.service.protocol");
+ if (p != null && p.length() > 0) {
+ providerBean.setName(p);
+ }
+ String h = getLegacyProperty("dubbo.service.server.host");
+ if (h != null && h.length() > 0) {
+ providerBean.setHost(h);
+ }
+ String o = getLegacyProperty("dubbo.service.server.port");
+ if (o != null && o.length() > 0) {
+ providerBean.setPort(Integer.parseInt(o.trim()));
+ }
+ String t = getLegacyProperty("dubbo.service.max.thread.pool.size");
+ if (t != null && t.length() > 0) {
+ providerBean.setThreads(Integer.parseInt(t.trim()));
+ }
+ setProtocol(providerBean);
+ }
+ }
+
+ public Class<?> getInterfaceClass() {
+ return interfaceClass == null ? GenericService.class : interfaceClass;
+ }
+
+ /**
+ * @deprecated
+ * @see #setInterface(Class)
+ * @param interfaceClass
+ */
+ public void setInterfaceClass(Class<?> interfaceClass) {
+ setInterface(interfaceClass);
+ }
+
+ public String getInterface() {
+ return interfaceName;
+ }
+
+ public void setInterface(String interfaceName) {
+ this.interfaceName = interfaceName;
+ }
+
+ public void setInterface(Class<?> interfaceClass) {
+ if (interfaceClass != null && ! interfaceClass.isInterface()) {
+ throw new IllegalStateException("The interface class " + interfaceClass + " is not a interface!");
+ }
+ this.interfaceClass = interfaceClass;
+ this.interfaceName = interfaceClass == null ? null : interfaceClass.getName();
+ }
+
+ public T getRef() {
+ return ref;
+ }
+
+ public void setRef(T ref) {
+ this.ref = ref;
+ }
+
+ @Parameter(excluded = true)
+ public String getPath() {
+ return path;
+ }
+
+ public void setPath(String path) {
+ checkPathName("path", path);
+ this.path = path;
+ }
+
+ public Boolean isRegister() {
+ return register;
+ }
+
+ public void setRegister(Boolean register) {
+ this.register = register;
+ if (Boolean.FALSE.equals(register)) {
+ setRegistry(new RegistryConfig(RegistryConfig.NO_AVAILABLE));
+ }
+ }
+
+ public List<MethodConfig> getMethods() {
+ return methods;
+ }
+
+ @SuppressWarnings("unchecked")
+ public void setMethods(List<? extends MethodConfig> methods) {
+ this.methods = (List<MethodConfig>) methods;
+ }
+
+ public ProviderConfig getProvider() {
+ return provider;
+ }
+
+ public void setProvider(ProviderConfig provider) {
+ this.provider = provider;
+ }
+
+ public List<URL> getExportedUrls(){
+ return urls;
+ }
+
+ // ======== Deprecated ========
+
+ /**
+ * @deprecated Replace to getProtocols()
+ */
+ @Deprecated
+ public List<ProviderConfig> getProviders() {
+ return convertProtocolToProvider(protocols);
+ }
+
+ /**
+ * @deprecated Replace to setProtocols()
+ */
+ @Deprecated
+ public void setProviders(List<ProviderConfig> providers) {
+ this.protocols = convertProviderToProtocol(providers);
+ }
+
+ @Deprecated
+ private static final List<ProtocolConfig> convertProviderToProtocol(List<ProviderConfig> providers) {
+ if (providers == null || providers.size() == 0) {
+ return null;
+ }
+ List<ProtocolConfig> protocols = new ArrayList<ProtocolConfig>(providers.size());
+ for (ProviderConfig provider : providers) {
+ protocols.add(convertProviderToProtocol(provider));
+ }
+ return protocols;
+ }
+
+ @Deprecated
+ private static final List<ProviderConfig> convertProtocolToProvider(List<ProtocolConfig> protocols) {
+ if (protocols == null || protocols.size() == 0) {
+ return null;
+ }
+ List<ProviderConfig> providers = new ArrayList<ProviderConfig>(protocols.size());
+ for (ProtocolConfig provider : protocols) {
+ providers.add(convertProtocolToProvider(provider));
+ }
+ return providers;
+ }
+
+ @Deprecated
+ private static final ProtocolConfig convertProviderToProtocol(ProviderConfig provider) {
+ ProtocolConfig protocol = new ProtocolConfig();
+ protocol.setName(provider.getProtocol().getName());
+ protocol.setServer(provider.getServer());
+ protocol.setClient(provider.getClient());
+ protocol.setCodec(provider.getCodec());
+ protocol.setHost(provider.getHost());
+ protocol.setPort(provider.getPort());
+ protocol.setPath(provider.getPath());
+ protocol.setPayload(provider.getPayload());
+ protocol.setThreads(provider.getThreads());
+ protocol.setParameters(provider.getParameters());
+ return protocol;
+ }
+
+ @Deprecated
+ private static final ProviderConfig convertProtocolToProvider(ProtocolConfig protocol) {
+ ProviderConfig provider = new ProviderConfig();
+ provider.setProtocol(protocol);
+ provider.setServer(protocol.getServer());
+ provider.setClient(protocol.getClient());
+ provider.setCodec(protocol.getCodec());
+ provider.setHost(protocol.getHost());
+ provider.setPort(protocol.getPort());
+ provider.setPath(protocol.getPath());
+ provider.setPayload(protocol.getPayload());
+ provider.setThreads(protocol.getThreads());
+ provider.setParameters(protocol.getParameters());
+ return provider;
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-config/src/main/java/com/alibaba/dubbo/config/spring/ReferenceBean.java b/dubbo-config/src/main/java/com/alibaba/dubbo/config/spring/ReferenceBean.java
new file mode 100644
index 0000000..6cb9526
--- /dev/null
+++ b/dubbo-config/src/main/java/com/alibaba/dubbo/config/spring/ReferenceBean.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.config.spring;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+
+import org.springframework.beans.factory.FactoryBean;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
+
+import com.alibaba.dubbo.config.ApplicationConfig;
+import com.alibaba.dubbo.config.ConsumerConfig;
+import com.alibaba.dubbo.config.MonitorConfig;
+import com.alibaba.dubbo.config.ProtocolConfig;
+import com.alibaba.dubbo.config.ReferenceConfig;
+import com.alibaba.dubbo.config.RegistryConfig;
+import com.alibaba.dubbo.config.ServiceConfig;
+
+/**
+ * ReferenceFactoryBean
+ *
+ * @author william.liangf
+ */
+public class ReferenceBean<T> extends ReferenceConfig<T> implements FactoryBean, ApplicationContextAware, InitializingBean {
+
+ private static final long serialVersionUID = 213195494150089726L;
+
+ private transient ApplicationContext applicationContext;
+
+ public void setApplicationContext(ApplicationContext applicationContext) {
+ this.applicationContext = applicationContext;
+ }
+
+ private 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);
+ }
+
+ @SuppressWarnings({ "unchecked"})
+ public Object getObject() throws Exception {
+ if (getConsumer() == null) {
+ Map<String, ConsumerConfig> consumerConfigMap = applicationContext == null ? null : applicationContext.getBeansOfType(ConsumerConfig.class, false, false);
+ if (consumerConfigMap != null && consumerConfigMap.size() > 0) {
+ ConsumerConfig consumerConfig = null;
+ Collection<ConsumerConfig> defaultConfigs = consumerConfigMap.values();
+ for (ConsumerConfig config : defaultConfigs) {
+ if (config.getClass() == ConsumerConfig.class) {
+ if (consumerConfig != null) {
+ throw new IllegalStateException("Duplicate consumer configs: " + consumerConfig + " and " + config);
+ }
+ consumerConfig = config;
+ }
+ }
+ if (consumerConfig != null) {
+ setConsumer(consumerConfig);
+ }
+ }
+ }
+ if (getApplication() == null
+ && (getConsumer() == null || getConsumer().getApplication() == null)) {
+ Map<String, ApplicationConfig> applicationConfigMap = applicationContext == null ? null : applicationContext.getBeansOfType(ApplicationConfig.class, false, false);
+ if (applicationConfigMap != null && applicationConfigMap.size() > 0) {
+ if (applicationConfigMap.size() > 1) {
+ throw new IllegalStateException("Duplicate application configs: " + applicationConfigMap.values());
+ }
+ ApplicationConfig applicationConfig = applicationConfigMap.values().iterator().next();
+ setApplication(applicationConfig);
+ }
+ }
+ if ((getRegistries() == null || getRegistries().size() == 0)
+ && (getConsumer() == null || getConsumer().getRegistries() == null || getConsumer().getRegistries().size() == 0)
+ && (getApplication() == null || getApplication().getRegistries() == null || getApplication().getRegistries().size() == 0)) {
+ Map<String, RegistryConfig> registryConfigMap = applicationContext == null ? null : applicationContext.getBeansOfType(RegistryConfig.class, false, false);
+ if (registryConfigMap != null && registryConfigMap.size() > 0) {
+ Collection<RegistryConfig> registryConfigs = registryConfigMap.values();
+ if (registryConfigs != null && registryConfigs.size() > 0) {
+ super.setRegistries(new ArrayList<RegistryConfig>(registryConfigs));
+ }
+ }
+ }
+ if (getMonitor() == null
+ && (getConsumer() == null || getConsumer().getMonitor() == null)
+ && (getApplication() == null || getApplication().getMonitor() == null)) {
+ Map<String, MonitorConfig> monitorConfigMap = applicationContext == null ? null : applicationContext.getBeansOfType(MonitorConfig.class, false, false);
+ if (monitorConfigMap != null && monitorConfigMap.size() > 0) {
+ if (monitorConfigMap.size() > 1) {
+ throw new IllegalStateException("Duplicate monitor configs: " + monitorConfigMap.values());
+ }
+ MonitorConfig monitorConfig = monitorConfigMap.values().iterator().next();
+ super.setMonitor(monitorConfig);
+ }
+ }
+ if (isInjvm() == null
+ && (getConsumer() == null || getConsumer().isInjvm() == null)
+ && applicationContext != null) {
+ Map<String, ServiceConfig<T>> serviceConfigMap = applicationContext.getBeansOfType(ServiceConfig.class, false, false);
+ if (serviceConfigMap != null && serviceConfigMap.size() > 0) {
+ for (ServiceConfig<T> serviceConfig : serviceConfigMap.values()) {
+ if (isEquals(serviceConfig.getInterface(), getInterface())
+ && isEquals(serviceConfig.getVersion(), getVersion())
+ && isEquals(serviceConfig.getGroup(), getGroup())) {
+ List<ProtocolConfig> protocols = serviceConfig.getProtocols();
+ if ((protocols == null || protocols.size() == 0)
+ && serviceConfig.getProvider() != null) {
+ protocols = serviceConfig.getProvider().getProtocols();
+ }
+ for (ProtocolConfig protocol : protocols) {
+ if ("injvm".equals(protocol.getName())) {
+ setInjvm(true);
+ break;
+ }
+ }
+ break;
+ }
+ }
+ }
+ }
+ return get();
+ }
+
+ public Class<?> getObjectType() {
+ return getInterfaceClass();
+ }
+
+ public boolean isSingleton() {
+ return true;
+ }
+
+ public void afterPropertiesSet() throws Exception {
+ getObject();
+ }
+
+}
diff --git a/dubbo-config/src/main/java/com/alibaba/dubbo/config/spring/ServiceBean.java b/dubbo-config/src/main/java/com/alibaba/dubbo/config/spring/ServiceBean.java
new file mode 100644
index 0000000..a19b1d4
--- /dev/null
+++ b/dubbo-config/src/main/java/com/alibaba/dubbo/config/spring/ServiceBean.java
@@ -0,0 +1,173 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.config.spring;
+
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Map;
+
+import org.springframework.beans.factory.BeanNameAware;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
+import org.springframework.context.ApplicationEvent;
+import org.springframework.context.ApplicationListener;
+
+import com.alibaba.dubbo.config.ApplicationConfig;
+import com.alibaba.dubbo.config.MonitorConfig;
+import com.alibaba.dubbo.config.ProtocolConfig;
+import com.alibaba.dubbo.config.ProviderConfig;
+import com.alibaba.dubbo.config.RegistryConfig;
+import com.alibaba.dubbo.config.ServiceConfig;
+
+/**
+ * ServiceFactoryBean
+ *
+ * @author william.liangf
+ */
+public class ServiceBean<T> extends ServiceConfig<T> implements InitializingBean, ApplicationContextAware, ApplicationListener, BeanNameAware {
+
+ private static final long serialVersionUID = 213195494150089726L;
+
+ private static transient ApplicationContext SPRING_CONTEXT;
+
+ private transient ApplicationContext applicationContext;
+
+ private transient String beanName;
+
+ private transient boolean supportedApplicationListener;
+
+ public static ApplicationContext getSpringContext() {
+ return SPRING_CONTEXT;
+ }
+
+ public void setApplicationContext(ApplicationContext applicationContext) {
+ this.applicationContext = applicationContext;
+ if (applicationContext != null) {
+ SPRING_CONTEXT = applicationContext;
+ try {
+ Method method = applicationContext.getClass().getMethod("addApplicationListener", new Class<?>[]{ApplicationListener.class}); // 兼容Spring2.0.1
+ method.invoke(applicationContext, new Object[] {this});
+ supportedApplicationListener = true;
+ } catch (Throwable t) {
+ }
+ }
+ }
+
+ public void setBeanName(String name) {
+ this.beanName = name;
+ }
+
+ public void onApplicationEvent(ApplicationEvent event) {
+ if ("org.springframework.context.event.ContextStartedEvent".equals(event.getClass().getName())) { // 兼容Spring2.0.1
+ if (isDelay()) {
+ if (logger.isInfoEnabled()) {
+ logger.info("The service ready on spring started. service: " + getInterface());
+ }
+ export();
+ }
+ }
+ }
+
+ private boolean isDelay() {
+ Integer delay = getDelay();
+ ProviderConfig provider = getProvider();
+ if (delay == null && provider != null) {
+ delay = provider.getDelay();
+ }
+ return supportedApplicationListener && delay != null && delay.intValue() == -1;
+ }
+
+ @SuppressWarnings({ "unchecked" })
+ public void afterPropertiesSet() throws Exception {
+ if (getProvider() == null) {
+ Map<String, ProviderConfig> providerConfigMap = applicationContext == null ? null : applicationContext.getBeansOfType(ProviderConfig.class, false, false);
+ if (providerConfigMap != null && providerConfigMap.size() > 0) {
+ Collection<ProviderConfig> providerConfigs = providerConfigMap.values();
+ ProviderConfig providerConfig = providerConfigs.iterator().next();
+ if (providerConfigs.size() > 1) {
+ Map<String, ProtocolConfig> protocolConfigMap = applicationContext == null ? null : applicationContext.getBeansOfType(ProtocolConfig.class, false, false);
+ if (protocolConfigMap != null && protocolConfigMap.size() > 0) {
+ throw new IllegalStateException("Duplicate provider configs: " + providerConfigs);
+ }
+ for (ProviderConfig config : providerConfigs) {
+ if (config.isDefault() != null && config.isDefault()) {
+ providerConfig = config;
+ }
+ }
+ }
+ setProvider(providerConfig);
+ }
+ }
+ if (getApplication() == null
+ && (getProvider() == null || getProvider().getApplication() == null)) {
+ Map<String, ApplicationConfig> applicationConfigMap = applicationContext == null ? null : applicationContext.getBeansOfType(ApplicationConfig.class, false, false);
+ if (applicationConfigMap != null && applicationConfigMap.size() > 0) {
+ if (applicationConfigMap.size() > 1) {
+ throw new IllegalStateException("Duplicate application configs: " + applicationConfigMap.values());
+ }
+ ApplicationConfig applicationConfig = applicationConfigMap.values().iterator().next();
+ setApplication(applicationConfig);
+ }
+ }
+ if ((getRegistries() == null || getRegistries().size() == 0)
+ && (getProvider() == null || getProvider().getRegistries() == null || getProvider().getRegistries().size() == 0)
+ && (getApplication() == null || getApplication().getRegistries() == null || getApplication().getRegistries().size() == 0)) {
+ Map<String, RegistryConfig> registryConfigMap = applicationContext == null ? null : applicationContext.getBeansOfType(RegistryConfig.class, false, false);
+ if (registryConfigMap != null && registryConfigMap.size() > 0) {
+ Collection<RegistryConfig> registryConfigs = registryConfigMap.values();
+ if (registryConfigs != null && registryConfigs.size() > 0) {
+ super.setRegistries(new ArrayList<RegistryConfig>(registryConfigs));
+ }
+ }
+ }
+ if (getMonitor() == null
+ && (getProvider() == null || getProvider().getMonitor() == null)
+ && (getApplication() == null || getApplication().getMonitor() == null)) {
+ Map<String, MonitorConfig> monitorConfigMap = applicationContext == null ? null : applicationContext.getBeansOfType(MonitorConfig.class, false, false);
+ if (monitorConfigMap != null && monitorConfigMap.size() > 0) {
+ if (monitorConfigMap.size() > 1) {
+ throw new IllegalStateException("Duplicate monitor configs: " + monitorConfigMap.values());
+ }
+ MonitorConfig monitorConfig = monitorConfigMap.values().iterator().next();
+ super.setMonitor(monitorConfig);
+ }
+ }
+ if ((getProtocols() == null || getProtocols().size() == 0)
+ && (getProvider() == null || getProvider().getProtocols() == null || getProvider().getProtocols().size() == 0)) {
+ Map<String, ProtocolConfig> protocolConfigMap = applicationContext == null ? null : applicationContext.getBeansOfType(ProtocolConfig.class, false, false);
+ if (protocolConfigMap != null && protocolConfigMap.size() > 0) {
+ if (protocolConfigMap.size() > 1) {
+ throw new IllegalStateException("Found multi-protocols: " + protocolConfigMap.values() + ", You must be set default protocol in: <dubbo:provider protocol=\"dubbo\" />, or set service protocol in: <dubbo:service protocol=\"dubbo\" />");
+ }
+ ProtocolConfig protocolConfig = protocolConfigMap.values().iterator().next();
+ setProtocol(protocolConfig);
+ }
+ }
+ if (getPath() == null || getPath().length() == 0) {
+ if (beanName != null && beanName.length() > 0
+ && getInterface() != null && getInterface().length() > 0
+ && beanName.startsWith(getInterface())) {
+ setPath(beanName);
+ }
+ }
+ if (! isDelay()) {
+ export();
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-config/src/main/java/com/alibaba/dubbo/config/spring/schema/DubboBeanDefinitionParser.java b/dubbo-config/src/main/java/com/alibaba/dubbo/config/spring/schema/DubboBeanDefinitionParser.java
new file mode 100644
index 0000000..09fd7b6
--- /dev/null
+++ b/dubbo-config/src/main/java/com/alibaba/dubbo/config/spring/schema/DubboBeanDefinitionParser.java
@@ -0,0 +1,362 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.config.spring.schema;
+
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.Date;
+import java.util.regex.Pattern;
+
+import org.springframework.beans.PropertyValue;
+import org.springframework.beans.factory.config.BeanDefinition;
+import org.springframework.beans.factory.config.BeanDefinitionHolder;
+import org.springframework.beans.factory.config.RuntimeBeanReference;
+import org.springframework.beans.factory.config.TypedStringValue;
+import org.springframework.beans.factory.support.ManagedList;
+import org.springframework.beans.factory.support.ManagedMap;
+import org.springframework.beans.factory.support.RootBeanDefinition;
+import org.springframework.beans.factory.xml.BeanDefinitionParser;
+import org.springframework.beans.factory.xml.ParserContext;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.ExtensionLoader;
+import com.alibaba.dubbo.common.logger.Logger;
+import com.alibaba.dubbo.common.logger.LoggerFactory;
+import com.alibaba.dubbo.config.ArgumentConfig;
+import com.alibaba.dubbo.config.MethodConfig;
+import com.alibaba.dubbo.config.MonitorConfig;
+import com.alibaba.dubbo.config.ProtocolConfig;
+import com.alibaba.dubbo.config.RegistryConfig;
+import com.alibaba.dubbo.rpc.Protocol;
+
+/**
+ * AbstractBeanDefinitionParser
+ *
+ * @author william.liangf
+ */
+public class DubboBeanDefinitionParser implements BeanDefinitionParser {
+
+ private static final Logger logger = LoggerFactory.getLogger(DubboBeanDefinitionParser.class);
+
+ private final Class<?> beanClass;
+
+ private final boolean required;
+
+ public DubboBeanDefinitionParser(Class<?> beanClass, boolean required) {
+ this.beanClass = beanClass;
+ this.required = required;
+ }
+
+ public BeanDefinition parse(Element element, ParserContext parserContext) {
+ return parse(element, parserContext, beanClass, required);
+ }
+
+ private static BeanDefinition parse(Element element, ParserContext parserContext, Class<?> beanClass, boolean required) {
+ RootBeanDefinition beanDefinition = new RootBeanDefinition();
+ beanDefinition.setBeanClass(beanClass);
+ beanDefinition.setLazyInit(false);
+ String id = element.getAttribute("id");
+ if ((id == null || id.length() == 0) && required) {
+ String generatedBeanName = element.getAttribute("name");
+ if (generatedBeanName == null || generatedBeanName.length() == 0) {
+ generatedBeanName = element.getAttribute("interface");
+ }
+ if (generatedBeanName == null || generatedBeanName.length() == 0) {
+ generatedBeanName = beanClass.getName();
+ }
+ id = generatedBeanName;
+ int counter = 2;
+ while(parserContext.getRegistry().containsBeanDefinition(id)) {
+ id = generatedBeanName + (counter ++);
+ }
+ }
+ if (id != null && id.length() > 0) {
+ if (parserContext.getRegistry().containsBeanDefinition(id)) {
+ throw new IllegalStateException("Duplicate spring bean id " + id);
+ }
+ parserContext.getRegistry().registerBeanDefinition(id, beanDefinition);
+ }
+ if (ProtocolConfig.class.equals(beanClass)) {
+ for (String name : parserContext.getRegistry().getBeanDefinitionNames()) {
+ BeanDefinition definition = parserContext.getRegistry().getBeanDefinition(name);
+ PropertyValue property = definition.getPropertyValues().getPropertyValue("protocol");
+ if (property != null) {
+ Object value = property.getValue();
+ if (value instanceof ProtocolConfig && id.equals(((ProtocolConfig) value).getName())) {
+ definition.getPropertyValues().addPropertyValue("protocol", new RuntimeBeanReference(id));
+ }
+ }
+ }
+ }
+ for (Method setter : beanClass.getMethods()) {
+ String name = setter.getName();
+ if (name.length() > 3 && name.startsWith("set")
+ && Modifier.isPublic(setter.getModifiers())
+ && setter.getParameterTypes().length == 1) {
+ Class<?> type = setter.getParameterTypes()[0];
+ String property = name.substring(3, 4).toLowerCase() + name.substring(4);
+ Method getter = null;
+ try {
+ getter = beanClass.getMethod("get" + name.substring(3), new Class<?>[0]);
+ } catch (NoSuchMethodException e) {
+ try {
+ getter = beanClass.getMethod("is" + name.substring(3), new Class<?>[0]);
+ } catch (NoSuchMethodException e2) {
+ }
+ }
+ if (getter == null
+ || ! Modifier.isPublic(getter.getModifiers())
+ || ! type.equals(getter.getReturnType())) {
+ continue;
+ }
+ if ("parameters".equals(property)) {
+ parseParameters(element.getChildNodes(), beanDefinition);
+ } else if ("methods".equals(property)) {
+ parseMethods(id, element.getChildNodes(), beanDefinition, parserContext);
+ } else if ("arguments".equals(property)) {
+ parseArguments(id, element.getChildNodes(), beanDefinition, parserContext);
+ } else {
+ String value = element.getAttribute(property);
+ if (value != null) {
+ value = value.trim();
+ if (value.length() > 0) {
+ if ("registry".equals(property) && RegistryConfig.NO_AVAILABLE.equalsIgnoreCase(value)) {
+ RegistryConfig registryConfig = new RegistryConfig();
+ registryConfig.setAddress(RegistryConfig.NO_AVAILABLE);
+ beanDefinition.getPropertyValues().addPropertyValue(property, registryConfig);
+ } else if ("registry".equals(property) && value.indexOf(',') != -1) {
+ parseMultiRef("registries", value, beanDefinition, parserContext);
+ } else if ("provider".equals(property) && value.indexOf(',') != -1) {
+ parseMultiRef("providers", value, beanDefinition, parserContext);
+ } else if ("protocol".equals(property) && value.indexOf(',') != -1) {
+ parseMultiRef("protocols", value, beanDefinition, parserContext);
+ } else {
+ Object reference;
+ if (isPrimitive(type)) {
+ if ("async".equals(property) && "false".equals(value)
+ || "timeout".equals(property) && "0".equals(value)
+ || "delay".equals(property) && "0".equals(value)
+ || "version".equals(property) && "0.0.0".equals(value)
+ || "stat".equals(property) && "-1".equals(value)
+ || "reliable".equals(property) && "false".equals(value)) {
+ // 兼容旧版本xsd中的default值
+ value = null;
+ }
+ reference = value;
+ if (! "id".equals(property)
+ && ! "name".equals(property)
+ && ! "interface".equals(property)) {
+ String sysProperty = element.getPrefix() + "." + element.getLocalName() + "." + id + "." + property;
+ String sysValue = System.getProperty(sysProperty);
+ if (sysValue != null && sysValue.trim().length() > 0) {
+ reference = sysValue.trim();
+ } else {
+ sysProperty = element.getPrefix() + "." + element.getLocalName() + "." + property;
+ sysValue = System.getProperty(sysProperty);
+ if (sysValue != null && sysValue.trim().length() > 0) {
+ reference = sysValue.trim();
+ }
+ }
+ }
+ } else if ("protocol".equals(property)
+ && ExtensionLoader.getExtensionLoader(Protocol.class).hasExtension(value)
+ && (! parserContext.getRegistry().containsBeanDefinition(value)
+ || ! ProtocolConfig.class.getName().equals(parserContext.getRegistry().getBeanDefinition(value).getBeanClassName()))) {
+ if ("dubbo:provider".equals(element.getTagName())) {
+ logger.warn("Recommended replace <dubbo:provider protocol=\"" + value + "\" ... /> to <dubbo:protocol name=\"" + value + "\" ... />");
+ }
+ // 兼容旧版本配置
+ ProtocolConfig protocol = new ProtocolConfig();
+ protocol.setName(value);
+ reference = protocol;
+ } else if ("monitor".equals(property)
+ && (! parserContext.getRegistry().containsBeanDefinition(value)
+ || ! MonitorConfig.class.getName().equals(parserContext.getRegistry().getBeanDefinition(value).getBeanClassName()))) {
+ // 兼容旧版本配置
+ reference = convertMonitor(value);
+ } else if ("onreturn".equals(property)) {
+ int index = value.lastIndexOf(".");
+ String returnRef = value.substring(0, index);
+ String returnMethod = value.substring(index + 1);
+ reference = new RuntimeBeanReference(returnRef);
+ beanDefinition.getPropertyValues().addPropertyValue("onreturnMethod", returnMethod);
+ } else if ("onthrow".equals(property)) {
+ int index = value.lastIndexOf(".");
+ String throwRef = value.substring(0, index);
+ String throwMethod = value.substring(index + 1);
+ reference = new RuntimeBeanReference(throwRef);
+ beanDefinition.getPropertyValues().addPropertyValue("onthrowMethod", throwMethod);
+ } else {
+ if ("ref".equals(property) && parserContext.getRegistry().containsBeanDefinition(value)) {
+ BeanDefinition refBean = parserContext.getRegistry().getBeanDefinition(value);
+ if (! refBean.isSingleton()) {
+ throw new IllegalStateException("The exported service ref " + value + " must be singleton! Please set the " + value + " bean scope to singleton, eg: <bean id=\"" + value+ "\" scope=\"singleton\" ...>");
+ }
+ }
+ reference = new RuntimeBeanReference(value);
+ }
+ beanDefinition.getPropertyValues().addPropertyValue(property, reference);
+ }
+ }
+ }
+ }
+ }
+ }
+ return beanDefinition;
+ }
+
+ private static final Pattern GROUP_AND_VERION = Pattern.compile("^[\\-.0-9_a-zA-Z]+(\\:[\\-.0-9_a-zA-Z]+)?$");
+
+ protected static MonitorConfig convertMonitor(String monitor) {
+ if (monitor == null || monitor.length() == 0) {
+ return null;
+ }
+ if (GROUP_AND_VERION.matcher(monitor).matches()) {
+ String group;
+ String version;
+ int i = monitor.indexOf(':');
+ if (i > 0) {
+ group = monitor.substring(0, i);
+ version = monitor.substring(i + 1);
+ } else {
+ group = monitor;
+ version = null;
+ }
+ MonitorConfig monitorConfig = new MonitorConfig();
+ monitorConfig.setGroup(group);
+ monitorConfig.setVersion(version);
+ return monitorConfig;
+ }
+ return null;
+ }
+
+ private static boolean isPrimitive(Class<?> cls) {
+ return cls.isPrimitive() || cls == Boolean.class || cls == Byte.class
+ || cls == Character.class || cls == Short.class || cls == Integer.class
+ || cls == Long.class || cls == Float.class || cls == Double.class
+ || cls == String.class || cls == Date.class || cls == Class.class;
+ }
+
+ @SuppressWarnings("unchecked")
+ private static void parseMultiRef(String property, String value, RootBeanDefinition beanDefinition,
+ ParserContext parserContext) {
+ String[] values = value.split("\\s*[,]+\\s*");
+ ManagedList list = null;
+ for (int i = 0; i < values.length; i++) {
+ String v = values[i];
+ if (v != null && v.length() > 0) {
+ if (list == null) {
+ list = new ManagedList();
+ }
+ list.add(new RuntimeBeanReference(v));
+ }
+ }
+ beanDefinition.getPropertyValues().addPropertyValue(property, list);
+ }
+
+ @SuppressWarnings("unchecked")
+ private static void parseParameters(NodeList nodeList, RootBeanDefinition beanDefinition) {
+ if (nodeList != null && nodeList.getLength() > 0) {
+ ManagedMap parameters = null;
+ for (int i = 0; i < nodeList.getLength(); i++) {
+ Node node = nodeList.item(i);
+ if (node instanceof Element) {
+ if ("parameter".equals(node.getNodeName())
+ || "parameter".equals(node.getLocalName())) {
+ if (parameters == null) {
+ parameters = new ManagedMap();
+ }
+ String key = ((Element) node).getAttribute("key");
+ String value = ((Element) node).getAttribute("value");
+ boolean hide = "true".equals(((Element) node).getAttribute("hide"));
+ if (hide) {
+ key = Constants.HIDE_KEY_PREFIX + key;
+ }
+ parameters.put(key, new TypedStringValue(value, String.class));
+ }
+ }
+ }
+ if (parameters != null) {
+ beanDefinition.getPropertyValues().addPropertyValue("parameters", parameters);
+ }
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ private static void parseMethods(String id, NodeList nodeList, RootBeanDefinition beanDefinition,
+ ParserContext parserContext) {
+ if (nodeList != null && nodeList.getLength() > 0) {
+ ManagedList methods = null;
+ for (int i = 0; i < nodeList.getLength(); i++) {
+ Node node = nodeList.item(i);
+ if (node instanceof Element) {
+ Element element = (Element) node;
+ if ("method".equals(node.getNodeName()) || "method".equals(node.getLocalName())) {
+ String methodName = element.getAttribute("name");
+ if (methodName == null || methodName.length() == 0) {
+ throw new IllegalStateException("<dubbo:method> name attribute == null");
+ }
+ if (methods == null) {
+ methods = new ManagedList();
+ }
+ BeanDefinition methodBeanDefinition = parse(((Element) node),
+ parserContext, MethodConfig.class, false);
+ String name = id + "." + methodName;
+ BeanDefinitionHolder methodBeanDefinitionHolder = new BeanDefinitionHolder(
+ methodBeanDefinition, name);
+ methods.add(methodBeanDefinitionHolder);
+ }
+ }
+ }
+ if (methods != null) {
+ beanDefinition.getPropertyValues().addPropertyValue("methods", methods);
+ }
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ private static void parseArguments(String id, NodeList nodeList, RootBeanDefinition beanDefinition,
+ ParserContext parserContext) {
+ if (nodeList != null && nodeList.getLength() > 0) {
+ ManagedList arguments = null;
+ for (int i = 0; i < nodeList.getLength(); i++) {
+ Node node = nodeList.item(i);
+ if (node instanceof Element) {
+ Element element = (Element) node;
+ if ("argument".equals(node.getNodeName()) || "argument".equals(node.getLocalName())) {
+ String argumentIndex = element.getAttribute("index");
+ if (arguments == null) {
+ arguments = new ManagedList();
+ }
+ BeanDefinition argumentBeanDefinition = parse(((Element) node),
+ parserContext, ArgumentConfig.class, false);
+ String name = id + "." + argumentIndex;
+ BeanDefinitionHolder argumentBeanDefinitionHolder = new BeanDefinitionHolder(
+ argumentBeanDefinition, name);
+ arguments.add(argumentBeanDefinitionHolder);
+ }
+ }
+ }
+ if (arguments != null) {
+ beanDefinition.getPropertyValues().addPropertyValue("arguments", arguments);
+ }
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-config/src/main/java/com/alibaba/dubbo/config/spring/schema/DubboNamespaceHandler.java b/dubbo-config/src/main/java/com/alibaba/dubbo/config/spring/schema/DubboNamespaceHandler.java
new file mode 100644
index 0000000..c30e676
--- /dev/null
+++ b/dubbo-config/src/main/java/com/alibaba/dubbo/config/spring/schema/DubboNamespaceHandler.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.config.spring.schema;
+
+import org.springframework.beans.factory.xml.NamespaceHandlerSupport;
+
+import com.alibaba.dubbo.common.Version;
+import com.alibaba.dubbo.config.ApplicationConfig;
+import com.alibaba.dubbo.config.ConsumerConfig;
+import com.alibaba.dubbo.config.MonitorConfig;
+import com.alibaba.dubbo.config.ProtocolConfig;
+import com.alibaba.dubbo.config.ProviderConfig;
+import com.alibaba.dubbo.config.RegistryConfig;
+import com.alibaba.dubbo.config.spring.ReferenceBean;
+import com.alibaba.dubbo.config.spring.ServiceBean;
+
+/**
+ * DubboNamespaceHandler
+ *
+ * @author william.liangf
+ */
+public class DubboNamespaceHandler extends NamespaceHandlerSupport {
+
+ static {
+ Version.checkDuplicate(DubboNamespaceHandler.class);
+ }
+
+ public void init() {
+ registerBeanDefinitionParser("application", new DubboBeanDefinitionParser(ApplicationConfig.class, true));
+ registerBeanDefinitionParser("registry", new DubboBeanDefinitionParser(RegistryConfig.class, true));
+ registerBeanDefinitionParser("monitor", new DubboBeanDefinitionParser(MonitorConfig.class, true));
+ registerBeanDefinitionParser("provider", new DubboBeanDefinitionParser(ProviderConfig.class, true));
+ registerBeanDefinitionParser("consumer", new DubboBeanDefinitionParser(ConsumerConfig.class, true));
+ registerBeanDefinitionParser("protocol", new DubboBeanDefinitionParser(ProtocolConfig.class, true));
+ registerBeanDefinitionParser("service", new DubboBeanDefinitionParser(ServiceBean.class, true));
+ registerBeanDefinitionParser("reference", new DubboBeanDefinitionParser(ReferenceBean.class, false));
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-config/src/main/java/com/alibaba/dubbo/config/spring/status/DataSourceStatusChecker.java b/dubbo-config/src/main/java/com/alibaba/dubbo/config/spring/status/DataSourceStatusChecker.java
new file mode 100644
index 0000000..1e756d7
--- /dev/null
+++ b/dubbo-config/src/main/java/com/alibaba/dubbo/config/spring/status/DataSourceStatusChecker.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.config.spring.status;
+
+import java.sql.Connection;
+import java.sql.DatabaseMetaData;
+import java.sql.ResultSet;
+import java.util.Map;
+
+import javax.sql.DataSource;
+
+import org.springframework.context.ApplicationContext;
+
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.common.logger.Logger;
+import com.alibaba.dubbo.common.logger.LoggerFactory;
+import com.alibaba.dubbo.common.status.Status;
+import com.alibaba.dubbo.common.status.StatusChecker;
+import com.alibaba.dubbo.config.spring.ServiceBean;
+
+/**
+ * DataSourceStatusChecker
+ *
+ * @author william.liangf
+ */
+@Extension("datasource")
+public class DataSourceStatusChecker implements StatusChecker {
+
+ private static final Logger logger = LoggerFactory.getLogger(DataSourceStatusChecker.class);
+
+ @SuppressWarnings("unchecked")
+ public Status check() {
+ ApplicationContext context = ServiceBean.getSpringContext();
+ if (context == null) {
+ return new Status(Status.Level.UNKNOWN);
+ }
+ Map<String, DataSource> dataSources = context.getBeansOfType(DataSource.class, false, false);
+ if (dataSources == null || dataSources.size() == 0) {
+ return new Status(Status.Level.UNKNOWN);
+ }
+ Status.Level level = Status.Level.OK;
+ StringBuilder buf = new StringBuilder();
+ for (Map.Entry<String, DataSource> entry : dataSources.entrySet()) {
+ DataSource dataSource = entry.getValue();
+ if (buf.length() > 0) {
+ buf.append(", ");
+ }
+ buf.append(entry.getKey());
+ try {
+ Connection connection = dataSource.getConnection();
+ try {
+ DatabaseMetaData metaData = connection.getMetaData();
+ ResultSet resultSet = metaData.getTypeInfo();
+ try {
+ if (! resultSet.next()) {
+ level = Status.Level.ERROR;
+ }
+ } finally {
+ resultSet.close();
+ }
+ buf.append(metaData.getURL());
+ buf.append("(");
+ buf.append(metaData.getDatabaseProductName());
+ buf.append("-");
+ buf.append(metaData.getDatabaseProductVersion());
+ buf.append(")");
+ } finally {
+ connection.close();
+ }
+ } catch (Throwable e) {
+ logger.warn(e.getMessage(), e);
+ return new Status(level, e.getMessage());
+ }
+ }
+ return new Status(level, buf.toString());
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-config/src/main/java/com/alibaba/dubbo/config/spring/status/SpringStatusChecker.java b/dubbo-config/src/main/java/com/alibaba/dubbo/config/spring/status/SpringStatusChecker.java
new file mode 100644
index 0000000..f2f8504
--- /dev/null
+++ b/dubbo-config/src/main/java/com/alibaba/dubbo/config/spring/status/SpringStatusChecker.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.config.spring.status;
+
+import java.lang.reflect.Method;
+
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.Lifecycle;
+
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.common.logger.Logger;
+import com.alibaba.dubbo.common.logger.LoggerFactory;
+import com.alibaba.dubbo.common.status.Status;
+import com.alibaba.dubbo.common.status.StatusChecker;
+import com.alibaba.dubbo.config.spring.ServiceBean;
+
+/**
+ * SpringStatusChecker
+ *
+ * @author william.liangf
+ */
+@Extension("spring")
+public class SpringStatusChecker implements StatusChecker {
+
+ private static final Logger logger = LoggerFactory.getLogger(SpringStatusChecker.class);
+
+ public Status check() {
+ ApplicationContext context = ServiceBean.getSpringContext();
+ if (context == null) {
+ return new Status(Status.Level.UNKNOWN);
+ }
+ Status.Level level = Status.Level.OK;
+ if (context instanceof Lifecycle) {
+ if (((Lifecycle)context).isRunning()) {
+ level = Status.Level.OK;
+ } else {
+ level = Status.Level.ERROR;
+ }
+ } else {
+ level = Status.Level.UNKNOWN;
+ }
+ StringBuilder buf = new StringBuilder();
+ try {
+ Class<?> cls = context.getClass();
+ Method method = null;
+ while (cls != null && method == null) {
+ try {
+ method = cls.getDeclaredMethod("getConfigLocations", new Class<?>[0]);
+ } catch (NoSuchMethodException t) {
+ cls = cls.getSuperclass();
+ }
+ }
+ if (method != null) {
+ if (! method.isAccessible()) {
+ method.setAccessible(true);
+ }
+ String[] configs = (String[]) method.invoke(context, new Object[0]);
+ if (configs != null && configs.length > 0) {
+ String dubboConfig = configs[0];
+ for (String config : configs) {
+ if (config.contains("dubbo")
+ || config.contains("provider")
+ || config.contains("server")) {
+ dubboConfig = config;
+ break;
+ }
+ }
+ int i = dubboConfig.lastIndexOf('/');
+ if (i > 0) {
+ dubboConfig = dubboConfig.substring(i + 1);
+ }
+ buf.append(dubboConfig);
+ }
+ }
+ } catch (Throwable t) {
+ logger.warn(t.getMessage(), t);
+ }
+ return new Status(level, buf.toString());
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-config/src/main/resources/META-INF/services/com.alibaba.dubbo.common.status.StatusChecker b/dubbo-config/src/main/resources/META-INF/services/com.alibaba.dubbo.common.status.StatusChecker
new file mode 100644
index 0000000..4c153f9
--- /dev/null
+++ b/dubbo-config/src/main/resources/META-INF/services/com.alibaba.dubbo.common.status.StatusChecker
@@ -0,0 +1,2 @@
+com.alibaba.dubbo.config.spring.status.SpringStatusChecker
+com.alibaba.dubbo.config.spring.status.DataSourceStatusChecker
\ No newline at end of file
diff --git a/dubbo-config/src/main/resources/META-INF/spring.handlers b/dubbo-config/src/main/resources/META-INF/spring.handlers
new file mode 100644
index 0000000..33510e6
--- /dev/null
+++ b/dubbo-config/src/main/resources/META-INF/spring.handlers
@@ -0,0 +1 @@
+http\://code.alibabatech.com/schema/dubbo=com.alibaba.dubbo.config.spring.schema.DubboNamespaceHandler
\ No newline at end of file
diff --git a/dubbo-config/src/main/resources/META-INF/spring.schemas b/dubbo-config/src/main/resources/META-INF/spring.schemas
new file mode 100644
index 0000000..364e467
--- /dev/null
+++ b/dubbo-config/src/main/resources/META-INF/spring.schemas
@@ -0,0 +1 @@
+http\://code.alibabatech.com/schema/dubbo/dubbo.xsd=com/alibaba/dubbo/config/spring/schema/dubbo.xsd
\ No newline at end of file
diff --git a/dubbo-config/src/main/resources/com/alibaba/dubbo/config/spring/schema/dubbo.xsd b/dubbo-config/src/main/resources/com/alibaba/dubbo/config/spring/schema/dubbo.xsd
new file mode 100644
index 0000000..d2bd213
--- /dev/null
+++ b/dubbo-config/src/main/resources/com/alibaba/dubbo/config/spring/schema/dubbo.xsd
@@ -0,0 +1,902 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<xsd:schema xmlns="http://code.alibabatech.com/schema/dubbo"
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+ targetNamespace="http://code.alibabatech.com/schema/dubbo">
+
+ <xsd:import namespace="http://www.w3.org/XML/1998/namespace"/>
+
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ Namespace support for the dubbo services provided by dubbo framework. ]]></xsd:documentation>
+ </xsd:annotation>
+
+ <xsd:element name="application">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The application config ]]></xsd:documentation>
+ </xsd:annotation>
+ <xsd:complexType>
+ <xsd:attribute name="id" type="xsd:ID">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The unique identifier for a bean. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="name" type="xsd:string" use="required">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The application name. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="owner" type="xsd:string">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The application owner name (email prefix). ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="organization" type="xsd:string">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The organization name. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="architecture" type="xsd:string">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The architecture. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="environment" type="xsd:string">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The application environment, eg: dev/test/run ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="registry" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The application registry. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="monitor" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The application monitor. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ </xsd:complexType>
+ </xsd:element>
+
+ <xsd:element name="registry">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The registry config ]]></xsd:documentation>
+ </xsd:annotation>
+ <xsd:complexType>
+ <xsd:attribute name="id" type="xsd:ID">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The unique identifier for a bean. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="address" type="xsd:string" use="required">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The registry address. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="port" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The registry default port. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="protocol" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The registry lookup protocol. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="username" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The registry username. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="password" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The registry password. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="transport" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The protocol transporter type. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="transporter" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The protocol transporter type. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="server" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The protocol server type. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="client" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The protocol client type. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="group" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The registry group. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="version" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The registry version. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="timeout" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The request timeout. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="file" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The registry adddress file store. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="wait" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The wait time for shutdown. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="check" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ Check registry status on stratup. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="dynamic" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ the service registered to this registry is dynamic(true) or static(false). ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="register" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ register service to this registry(true) or not(false). ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="subscribe" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ subscribe service to this registry(true) or not(false). ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ </xsd:complexType>
+ </xsd:element>
+
+ <xsd:element name="monitor">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The logstat monitor config ]]></xsd:documentation>
+ </xsd:annotation>
+ <xsd:complexType>
+ <xsd:attribute name="address" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The monitor address. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="protocol" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The monitor protocol. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="username" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The monitor username. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="password" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The monitor password. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="group" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The monitor group. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="version" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The monitor version. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ </xsd:complexType>
+ </xsd:element>
+
+ <xsd:element name="parameter">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The service url parameter ]]></xsd:documentation>
+ </xsd:annotation>
+ <xsd:complexType>
+ <xsd:attribute name="key" type="xsd:string" use="required">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The parameter key. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="value" type="xsd:string" use="required">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The parameter value. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="hide" type="xsd:boolean" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ Hide parameter. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ </xsd:complexType>
+ </xsd:element>
+
+ <xsd:complexType name="methodShared">
+ <xsd:attribute name="timeout" type="xsd:string" use="optional" default="0">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The method invoke timeout. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="retries" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The method retry times. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="actives" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The max active requests. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="connections" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The max connections. ]]></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:complexType>
+
+ <xsd:element name="method">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The service method config ]]></xsd:documentation>
+ </xsd:annotation>
+ <xsd:complexType>
+ <xsd:complexContent>
+ <xsd:extension base="methodShared">
+ <xsd:choice minOccurs="0" maxOccurs="unbounded">
+ <xsd:element ref="argument" minOccurs="0" maxOccurs="unbounded" />
+ <xsd:element ref="parameter" minOccurs="0" maxOccurs="unbounded" />
+ </xsd:choice>
+ <xsd:attribute name="name" type="xsd:string" use="required">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The method name (method.toString()). ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="stat" type="xsd:string" use="optional" default="-1">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The method parameter index for statistics. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="retry" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ Deprecated. Replace to retries. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="reliable" type="xsd:string" use="optional" default="false">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ Deprecated. Replace to napoli protocol. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="deprecated" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The method deprecated. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="sticky" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ Enable/Disable cluster sticky policy.Default false ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+
+ <xsd:attribute name="return" type="xsd:string" use="optional" >
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ Method result is return. default is true.]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="oninvoke" type="xsd:string" use="optional" >
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ Method invoke trigger.]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="onreturn" type="xsd:string" use="optional" >
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ Method return trigger. return attribute must be true.]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="onthrow" type="xsd:string" use="optional" >
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ Method on error trigger.return attribute must be true.]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ </xsd:extension>
+ </xsd:complexContent>
+ </xsd:complexType>
+ </xsd:element>
+
+ <xsd:element name="argument">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The service argument config ]]></xsd:documentation>
+ </xsd:annotation>
+ <xsd:complexType>
+ <xsd:attribute name="index" type="xsd:string" use="optional" >
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The argument index. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="type" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The argument type. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="callback" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The argument is callback. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ </xsd:complexType>
+ </xsd:element>
+
+ <xsd:complexType name="referenceShared">
+ <xsd:complexContent>
+ <xsd:extension base="methodShared">
+ <xsd:attribute name="id" type="xsd:ID">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The unique identifier for a bean. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="local" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ Use service local implemention. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="stub" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ Use service local implemention. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="mock" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ Use service mock implemention. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="proxy" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ Use proxy factory. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="cluster" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ Use cluster strategy. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="filter" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The filter. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="listener" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The listener. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="owner" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The owner. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="layer" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ layer info. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="application" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The service application. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="registry" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The service registry. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="monitor" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The service monitor. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="callbacks" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The callback instance limit peer connection.]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="onconnect" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The service client connected. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="ondisconnect" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The service client disconnected. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ </xsd:extension>
+ </xsd:complexContent>
+ </xsd:complexType>
+
+ <xsd:complexType name="consumerShared">
+ <xsd:complexContent>
+ <xsd:extension base="referenceShared">
+ <xsd:attribute name="check" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ Check dependency providers. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="generic" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ Generic service. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="injvm" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ Get reference in jvm first. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="sticky" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ Enable/Disable cluster sticky policy.Default false ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="reconnect" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ remoting reconnect timer. false represent close reconnect. integer represent interval(ms) .default true(2000ms).]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="lazy" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ lazy create connection. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ </xsd:extension>
+ </xsd:complexContent>
+ </xsd:complexType>
+
+ <xsd:element name="consumer">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ Service reference default config ]]></xsd:documentation>
+ </xsd:annotation>
+ <xsd:complexType>
+ <xsd:complexContent>
+ <xsd:extension base="consumerShared">
+ <xsd:sequence minOccurs="0" maxOccurs="unbounded">
+ <xsd:element ref="parameter" minOccurs="0" maxOccurs="unbounded" />
+ </xsd:sequence>
+ </xsd:extension>
+ </xsd:complexContent>
+ </xsd:complexType>
+ </xsd:element>
+
+ <xsd:element name="reference">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ Reference service config ]]></xsd:documentation>
+ </xsd:annotation>
+ <xsd:complexType>
+ <xsd:complexContent>
+ <xsd:extension base="consumerShared">
+ <xsd:choice minOccurs="0" maxOccurs="unbounded">
+ <xsd:element ref="method" minOccurs="0" maxOccurs="unbounded" />
+ <xsd:element ref="parameter" minOccurs="0" maxOccurs="unbounded" />
+ </xsd:choice>
+ <xsd:attribute name="interface" type="xsd:token" use="required">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The service interface class name. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="version" type="xsd:string" use="optional" default="0.0.0">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The service version. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="group" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The service group. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="url" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ Provider list url. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="client" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ Protocol transport client type. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="consumer" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ Deprecated. Replace to reference-default. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ </xsd:extension>
+ </xsd:complexContent>
+ </xsd:complexType>
+ </xsd:element>
+
+ <xsd:element name="protocol">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ Service provider config ]]></xsd:documentation>
+ </xsd:annotation>
+ <xsd:complexType>
+ <xsd:sequence minOccurs="0" maxOccurs="unbounded">
+ <xsd:element ref="parameter" minOccurs="0" maxOccurs="unbounded" />
+ </xsd:sequence>
+ <xsd:attribute name="id" type="xsd:ID">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The unique identifier for a bean. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="name" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The protocol name. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="host" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The service host. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="port" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The service port. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="threadpool" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The thread pool type. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="threads" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The thread pool size. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="iothreads" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The IO thread pool size. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="queues" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The thread pool queue size. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="accepts" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The accept connection size. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="codec" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The protocol codec. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="serialization" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The protocol serialization. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="charset" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The protocol charset. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="payload" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The max payload. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="buffer" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The buffer size. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="accesslog" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The protocol use accesslog. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="telnet" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The protocol use telnet commands. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="status" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The protocol check status. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="transporter" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The protocol transporter type. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="exchanger" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The protocol exchanger type. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="server" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The protocol server type. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="client" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The protocol client type. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="path" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The protocol context path. replace to "contextpath". ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="contextpath" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The protocol context path. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="register" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The protocol can be register to registry. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ </xsd:complexType>
+ </xsd:element>
+
+ <xsd:complexType name="serviceShared">
+ <xsd:complexContent>
+ <xsd:extension base="referenceShared">
+ <xsd:attribute name="version" type="xsd:string" use="optional" default="0.0.0">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The service version. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="group" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The service group. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="deprecated" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ whether the service is deprecated. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="delay" type="xsd:string" use="optional" default="0">
+ <xsd:annotation>
+ <xsd:documentation>
+ <![CDATA[ The service export delay millisecond. ]]>
+ </xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="weight" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation>
+ <![CDATA[ The service weight. ]]>
+ </xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="document" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation>
+ <![CDATA[ The service document. ]]>
+ </xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="dynamic" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ the service registered to the registry is dynamic(true) or static(false). ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="token" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The service use token. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="accesslog" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The service use accesslog. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="executes" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The service allow execute requests. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="protocol" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The service protocol. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ </xsd:extension>
+ </xsd:complexContent>
+ </xsd:complexType>
+
+ <xsd:element name="provider">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ Export service config ]]></xsd:documentation>
+ </xsd:annotation>
+ <xsd:complexType>
+ <xsd:complexContent>
+ <xsd:extension base="serviceShared">
+ <xsd:sequence minOccurs="0" maxOccurs="unbounded">
+ <xsd:element ref="parameter" minOccurs="0" maxOccurs="unbounded" />
+ </xsd:sequence>
+ <xsd:attribute name="host" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The service host. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="port" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The service port. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="threadpool" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The thread pool type. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="threads" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The thread pool size. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="iothreads" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The IO thread pool size. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="queues" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The thread pool queue size. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="accepts" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The accept connection size. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="codec" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The protocol codec. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="serialization" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The protocol serialization. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="charset" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The protocol charset. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="payload" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The max payload. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="buffer" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The buffer size. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="transporter" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The protocol transporter type. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="exchanger" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The protocol exchanger type. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="server" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The protocol server type. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="client" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The protocol client type. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="telnet" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The protocol use telnet commands. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="status" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The protocol check status. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="path" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The protocol context path. replace to "contextpath". ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="contextpath" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The protocol context path. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="wait" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The provider shutdown wait time. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="default" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ Deprecated. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ </xsd:extension>
+ </xsd:complexContent>
+ </xsd:complexType>
+ </xsd:element>
+
+ <xsd:element name="service">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ Export service config ]]></xsd:documentation>
+ </xsd:annotation>
+ <xsd:complexType>
+ <xsd:complexContent>
+ <xsd:extension base="serviceShared">
+ <xsd:choice minOccurs="0" maxOccurs="unbounded">
+ <xsd:element ref="method" minOccurs="0" maxOccurs="unbounded" />
+ <xsd:element ref="parameter" minOccurs="0" maxOccurs="unbounded" />
+ </xsd:choice>
+ <xsd:attribute name="ref" type="xsd:string" use="required">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ Refers to the named bean to be exported as a service in the service registry. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="interface" type="xsd:token" use="required">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ Defines the interface to advertise for this service in the service registry. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="path" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The service path. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="register" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ The service can be register to registry. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute name="provider" type="xsd:string" use="optional">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[ Deprecated. Replace to protocol. ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ </xsd:extension>
+ </xsd:complexContent>
+ </xsd:complexType>
+ </xsd:element>
+
+</xsd:schema>
\ No newline at end of file
diff --git a/dubbo-config/src/test/java/com/alibaba/dubbo/config/ConfigTest.java b/dubbo-config/src/test/java/com/alibaba/dubbo/config/ConfigTest.java
new file mode 100644
index 0000000..39519b8
--- /dev/null
+++ b/dubbo-config/src/test/java/com/alibaba/dubbo/config/ConfigTest.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.config;
+
+import java.util.List;
+
+import junit.framework.Assert;
+
+import org.junit.Test;
+import org.springframework.beans.factory.BeanCreationException;
+import org.springframework.context.support.ClassPathXmlApplicationContext;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.utils.NetUtils;
+import com.alibaba.dubbo.config.api.DemoService;
+import com.alibaba.dubbo.registry.RegistryService;
+import com.alibaba.dubbo.registry.support.SimpleRegistryExporter;
+import com.alibaba.dubbo.registry.support.SimpleRegistryService;
+import com.alibaba.dubbo.rpc.Exporter;
+
+/**
+ * ConfigTest
+ *
+ * @author william.liangf
+ */
+public class ConfigTest {
+
+ private DemoService refer(String url) {
+ ReferenceConfig<DemoService> reference = new ReferenceConfig<DemoService>();
+ reference.setApplication(new ApplicationConfig("consumer"));
+ reference.setRegistry(new RegistryConfig(RegistryConfig.NO_AVAILABLE));
+ reference.setInterface(DemoService.class);
+ reference.setUrl(url);
+ return reference.get();
+ }
+
+ @Test
+ public void testMultiProtocol() {
+ ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(ConfigTest.class.getPackage().getName().replace('.', '/') + "/multi-protocol.xml");
+ ctx.start();
+ DemoService demoService = refer("dubbo://127.0.0.1:20881");
+ String hello = demoService.sayName("hello");
+ Assert.assertEquals("say:hello", hello);
+ ctx.stop();
+ ctx.close();
+ }
+
+ @Test
+ public void testMultiProtocolDefault() {
+ ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(ConfigTest.class.getPackage().getName().replace('.', '/') + "/multi-protocol-default.xml");
+ ctx.start();
+ DemoService demoService = refer("rmi://127.0.0.1:10991");
+ String hello = demoService.sayName("hello");
+ Assert.assertEquals("say:hello", hello);
+ ctx.stop();
+ ctx.close();
+ }
+
+ @Test
+ public void testMultiProtocolError() {
+ try {
+ ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(ConfigTest.class.getPackage().getName().replace('.', '/') + "/multi-protocol-error.xml");
+ ctx.start();
+ ctx.stop();
+ ctx.close();
+ } catch (BeanCreationException e) {
+ Assert.assertTrue(e.getMessage().contains("Found multi-protocols"));
+ }
+ }
+
+ @Test
+ public void testMultiProtocolRegister() {
+ SimpleRegistryService registryService = new SimpleRegistryService();
+ Exporter<RegistryService> exporter = SimpleRegistryExporter.export(4547, registryService);
+ ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(ConfigTest.class.getPackage().getName().replace('.', '/') + "/multi-protocol-register.xml");
+ ctx.start();
+ try {
+ List<URL> urls = registryService.getRegistered().get("com.alibaba.dubbo.config.api.DemoService");
+ Assert.assertNotNull(urls);
+ Assert.assertEquals(1, urls.size());
+ Assert.assertEquals("dubbo://" + NetUtils.getLocalHost() + ":20824/com.alibaba.dubbo.config.api.DemoService", urls.get(0).toIdentityString());
+ } finally {
+ ctx.stop();
+ ctx.close();
+ exporter.unexport();
+ }
+ }
+
+ @Test
+ public void testMultiRegistry() {
+ SimpleRegistryService registryService1 = new SimpleRegistryService();
+ Exporter<RegistryService> exporter1 = SimpleRegistryExporter.export(4545, registryService1);
+ SimpleRegistryService registryService2 = new SimpleRegistryService();
+ Exporter<RegistryService> exporter2 = SimpleRegistryExporter.export(4546, registryService2);
+ ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(ConfigTest.class.getPackage().getName().replace('.', '/') + "/multi-registry.xml");
+ ctx.start();
+ try {
+ List<URL> urls1 = registryService1.getRegistered().get("com.alibaba.dubbo.config.api.DemoService");
+ Assert.assertNull(urls1);
+ List<URL> urls2 = registryService2.getRegistered().get("com.alibaba.dubbo.config.api.DemoService");
+ Assert.assertNotNull(urls2);
+ Assert.assertEquals(1, urls2.size());
+ Assert.assertEquals("dubbo://" + NetUtils.getLocalHost() + ":20880/com.alibaba.dubbo.config.api.DemoService", urls2.get(0).toIdentityString());
+ } finally {
+ ctx.stop();
+ ctx.close();
+ exporter1.unexport();
+ exporter2.unexport();
+ }
+ }
+
+ @Test
+ public void testDelayFixedTime() throws Exception {
+ SimpleRegistryService registryService = new SimpleRegistryService();
+ Exporter<RegistryService> exporter = SimpleRegistryExporter.export(4548, registryService);
+ ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(ConfigTest.class.getPackage().getName().replace('.', '/') + "/delay-fixed-time.xml");
+ ctx.start();
+ try {
+ List<URL> urls = registryService.getRegistered().get("com.alibaba.dubbo.config.api.DemoService");
+ Assert.assertNull(urls);
+ while (urls == null) {
+ urls = registryService.getRegistered().get("com.alibaba.dubbo.config.api.DemoService");
+ Thread.sleep(10);
+ }
+ Assert.assertNotNull(urls);
+ Assert.assertEquals(1, urls.size());
+ Assert.assertEquals("dubbo://" + NetUtils.getLocalHost() + ":20883/com.alibaba.dubbo.config.api.DemoService", urls.get(0).toIdentityString());
+ } finally {
+ ctx.stop();
+ ctx.close();
+ exporter.unexport();
+ }
+ }
+
+ @Test
+ public void testDelayOnInitialized() throws Exception {
+ SimpleRegistryService registryService = new SimpleRegistryService();
+ Exporter<RegistryService> exporter = SimpleRegistryExporter.export(4548, registryService);
+ ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(ConfigTest.class.getPackage().getName().replace('.', '/') + "/delay-on-initialized.xml");
+ ctx.start();
+ try {
+ List<URL> urls = registryService.getRegistered().get("com.alibaba.dubbo.config.api.DemoService");
+ Assert.assertNotNull(urls);
+ Assert.assertEquals(1, urls.size());
+ Assert.assertEquals("dubbo://" + NetUtils.getLocalHost() + ":20883/com.alibaba.dubbo.config.api.DemoService", urls.get(0).toIdentityString());
+ } finally {
+ ctx.stop();
+ ctx.close();
+ exporter.unexport();
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-config/src/test/java/com/alibaba/dubbo/config/api/DemoService.java b/dubbo-config/src/test/java/com/alibaba/dubbo/config/api/DemoService.java
new file mode 100644
index 0000000..6768ea2
--- /dev/null
+++ b/dubbo-config/src/test/java/com/alibaba/dubbo/config/api/DemoService.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.config.api;
+
+
+/**
+ * DemoService
+ *
+ * @author william.liangf
+ */
+public interface DemoService {
+ String sayName(String name);
+}
\ No newline at end of file
diff --git a/dubbo-config/src/test/java/com/alibaba/dubbo/config/provider/impl/DemoServiceImpl.java b/dubbo-config/src/test/java/com/alibaba/dubbo/config/provider/impl/DemoServiceImpl.java
new file mode 100644
index 0000000..9fc9b28
--- /dev/null
+++ b/dubbo-config/src/test/java/com/alibaba/dubbo/config/provider/impl/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.config.provider.impl;
+
+import com.alibaba.dubbo.config.api.DemoService;
+
+/**
+ * DemoServiceImpl
+ *
+ * @author william.liangf
+ */
+public class DemoServiceImpl implements DemoService {
+
+ public String sayName(String name) {
+ return "say:" + name;
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-config/src/test/java/com/alibaba/dubbo/config/support/MockProtocol.java b/dubbo-config/src/test/java/com/alibaba/dubbo/config/support/MockProtocol.java
new file mode 100644
index 0000000..3905a48
--- /dev/null
+++ b/dubbo-config/src/test/java/com/alibaba/dubbo/config/support/MockProtocol.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.config.support;
+
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.rpc.Exporter;
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.Protocol;
+import com.alibaba.dubbo.rpc.Result;
+import com.alibaba.dubbo.rpc.RpcException;
+
+/**
+ *
+ * @author haomin.liuhm
+ *
+ */
+@Extension("mockprotocol")
+public class MockProtocol implements Protocol {
+
+ /* (non-Javadoc)
+ * @see com.alibaba.dubbo.rpc.Protocol#getDefaultPort()
+ */
+ public int getDefaultPort() {
+
+ return 0;
+ }
+
+ /* (non-Javadoc)
+ * @see com.alibaba.dubbo.rpc.Protocol#export(com.alibaba.dubbo.rpc.Invoker)
+ */
+ public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
+ return null;
+ }
+
+ /* (non-Javadoc)
+ * @see com.alibaba.dubbo.rpc.Protocol#refer(java.lang.Class, com.alibaba.dubbo.common.URL)
+ */
+ public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {
+
+ final URL u = url;
+
+ return new Invoker<T>(){
+ public Class<T> getInterface(){
+ return null;
+ }
+ public URL getUrl(){
+ return u;
+ }
+ public boolean isAvailable(){
+ return true;
+ }
+ public Result invoke(Invocation invocation) throws RpcException{
+ return null;
+ }
+
+ public void destroy(){
+
+ }
+ };
+ }
+
+ /* (non-Javadoc)
+ * @see com.alibaba.dubbo.rpc.Protocol#destroy()
+ */
+ public void destroy() {
+
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-config/src/test/java/com/alibaba/dubbo/config/support/MockRegistry.java b/dubbo-config/src/test/java/com/alibaba/dubbo/config/support/MockRegistry.java
new file mode 100644
index 0000000..7116d73
--- /dev/null
+++ b/dubbo-config/src/test/java/com/alibaba/dubbo/config/support/MockRegistry.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.config.support;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.registry.NotifyListener;
+import com.alibaba.dubbo.registry.Registry;
+
+/**
+ * TODO Comment of MockRegistry
+ * @author haomin.liuhm
+ *
+ */
+public class MockRegistry implements Registry {
+
+ static URL subscribedUrl = new URL("null", "0.0.0.0", 0);
+
+ public static URL getSubscribedUrl(){
+ return subscribedUrl;
+ }
+
+ /*
+ * @see com.alibaba.dubbo.common.Node#getUrl()
+ */
+ public URL getUrl() {
+ return null;
+ }
+
+ /*
+ * @see com.alibaba.dubbo.common.Node#isAvailable()
+ */
+ public boolean isAvailable() {
+ return true;
+ }
+
+ /*
+ * @see com.alibaba.dubbo.common.Node#destroy()
+ */
+ public void destroy() {
+
+ }
+
+ /*
+ * @see com.alibaba.dubbo.registry.RegistryService#register(com.alibaba.dubbo.common.URL)
+ */
+ public void register(URL url) {
+
+ }
+
+ /*
+ * @see com.alibaba.dubbo.registry.RegistryService#unregister(com.alibaba.dubbo.common.URL)
+ */
+ public void unregister(URL url) {
+
+ }
+
+ /*
+ * @see com.alibaba.dubbo.registry.RegistryService#subscribe(com.alibaba.dubbo.common.URL, com.alibaba.dubbo.registry.NotifyListener)
+ */
+ public void subscribe(URL url, NotifyListener listener) {
+ this.subscribedUrl = url;
+ List<URL> urls = new ArrayList<URL>();
+
+ urls.add(url.setProtocol("mockprotocol")
+ .addParameter(Constants.METHODS_KEY, "sayHello"));
+
+ listener.notify(urls);
+ }
+
+ /*
+ * @see com.alibaba.dubbo.registry.RegistryService#unsubscribe(com.alibaba.dubbo.common.URL, com.alibaba.dubbo.registry.NotifyListener)
+ */
+ public void unsubscribe(URL url, NotifyListener listener) {
+
+ }
+
+ /*
+ * @see com.alibaba.dubbo.registry.RegistryService#lookup(com.alibaba.dubbo.common.URL)
+ */
+ public List<URL> lookup(URL url) {
+ return null;
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-config/src/test/java/com/alibaba/dubbo/config/support/MockRegistryFactory.java b/dubbo-config/src/test/java/com/alibaba/dubbo/config/support/MockRegistryFactory.java
new file mode 100644
index 0000000..421e4b0
--- /dev/null
+++ b/dubbo-config/src/test/java/com/alibaba/dubbo/config/support/MockRegistryFactory.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.config.support;
+
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.registry.Registry;
+import com.alibaba.dubbo.registry.RegistryFactory;
+
+/**
+ * TODO Comment of MockRegistryFactory
+ * @author haomin.liuhm
+ *
+ */
+@Extension("mockregistry")
+public class MockRegistryFactory implements RegistryFactory {
+
+ /*
+ * @see com.alibaba.dubbo.registry.RegistryFactory#getRegistry(com.alibaba.dubbo.common.URL)
+ */
+ public Registry getRegistry(URL url) {
+
+ return new MockRegistry();
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-config/src/test/java/com/alibaba/dubbo/config/support/RpcConfigGetSetProxy.java b/dubbo-config/src/test/java/com/alibaba/dubbo/config/support/RpcConfigGetSetProxy.java
new file mode 100644
index 0000000..9ddecaf
--- /dev/null
+++ b/dubbo-config/src/test/java/com/alibaba/dubbo/config/support/RpcConfigGetSetProxy.java
@@ -0,0 +1,169 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.config.support;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+import com.alibaba.dubbo.common.logger.Logger;
+import com.alibaba.dubbo.common.logger.LoggerFactory;
+import com.alibaba.dubbo.config.AbstractConfig;
+
+
+/**
+ * @author haomin.liuhm
+ *
+ */
+public class RpcConfigGetSetProxy {
+
+ private static final String RPC_CONFIG_BASECLASS = AbstractConfig.class.getName();
+ private static final Logger log = LoggerFactory.getLogger(RpcConfigGetSetProxy.class);
+
+
+ private Object proxiee = null;
+ private Class<?> proxieeClass = null;
+ private Boolean isOk = false;
+
+ public RpcConfigGetSetProxy(Object p){
+
+ if(p == null){
+ return;
+ }
+
+ if (!isKindOf(p.getClass(), RPC_CONFIG_BASECLASS)){
+ return;
+ }
+
+ proxiee = p;
+ //proxieeClass = c;
+ proxieeClass = p.getClass();
+ isOk = true;
+
+ }
+
+ public boolean isOk(){
+ return isOk;
+ }
+
+ public Object setValue(String key, Object value){
+
+ if (!isOk()){
+ return null;
+ }
+
+ Method m = findSetMethod(key, value, proxieeClass);
+ return invoke(m, value);
+ }
+
+ public Object getValue(String key){
+
+ if (!isOk()){
+ return null;
+ }
+
+ Method m = findGetMethod(key, proxieeClass);
+ return invoke(m, null);
+ }
+
+ public static boolean isKindOf(Class<?> c, String type){
+
+ // get the class def for obj and type
+
+ Class<?> tClass;
+ try {
+ tClass = Class.forName(type);
+ } catch (ClassNotFoundException e) {
+ return false;
+ }
+
+ // check against type and superclasses
+ while ( c != null ) {
+ if ( c == tClass ) return true;
+ c = c.getSuperclass();
+ }
+
+ return false;
+ }
+
+ private Object invoke(Method m, Object value) {
+
+ if (m == null){
+ return null;
+ }
+
+ try {
+ if(value == null){
+ return m.invoke(proxiee, (Object[])null);
+ }else{
+ return m.invoke(proxiee, value);
+ }
+ } catch (IllegalArgumentException e) {
+ log.error("IllegalArgumentException", e);
+ return null;
+ } catch (IllegalAccessException e) {
+ log.error("IllegalAccessException", e);
+ return null;
+ } catch (InvocationTargetException e) {
+ log.error("InvocationTargetException", e);
+ return null;
+ }
+ }
+
+ private Method findGetMethod(String key, Class<?> clazz){
+
+ Method m = findMethod(key, null, "get", clazz);
+ if (m != null){
+ return m;
+ }
+
+ return findMethod(key, null, "is", clazz);
+ }
+
+ private Method findSetMethod(String key, Object value, Class<?> clazz){
+
+ return findMethod(key, value, "set", clazz);
+ }
+
+ private Method getMethod(String methodName, Object value, Class<?> clazz){
+
+ try{
+ if (value == null){
+ return clazz.getMethod(methodName, (Class<?>[])null);
+ }else{
+ return clazz.getMethod(methodName, value.getClass());
+ }
+ }catch (SecurityException e) {
+ log.error("SecurityException: " + e.getMessage());
+ return null;
+ } catch (NoSuchMethodException e) {
+ log.error("NoSuchMethodException: " + e.getMessage());
+ return null;
+ }
+ }
+
+ private Method findMethod(String key, Object value, String prefix, Class<?> clazz){
+
+ if(key.length() < 2){
+ return null;
+ }
+
+ key = key.substring(0,1).toUpperCase() + key.substring(1);
+ String methodName = prefix + key;
+
+ return getMethod(methodName, value, clazz);
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-config/src/test/java/com/alibaba/dubbo/config/url/test/ExporterSideConfigUrlTest.java b/dubbo-config/src/test/java/com/alibaba/dubbo/config/url/test/ExporterSideConfigUrlTest.java
new file mode 100644
index 0000000..5df4864
--- /dev/null
+++ b/dubbo-config/src/test/java/com/alibaba/dubbo/config/url/test/ExporterSideConfigUrlTest.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.config.url.test;
+
+
+import java.io.UnsupportedEncodingException;
+import java.net.URLDecoder;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import com.alibaba.dubbo.common.logger.Logger;
+import com.alibaba.dubbo.common.logger.LoggerFactory;
+
+
+/**
+ * @author haomin.liuhm
+ *
+ */
+public class ExporterSideConfigUrlTest extends UrlTestBase {
+
+ private static final Logger log = LoggerFactory.getLogger(ExporterSideConfigUrlTest.class);
+
+ // ======================================================
+ // tests start
+ // ======================================================
+ @BeforeClass
+ public static void start(){
+
+
+ }
+
+
+ @Before
+ public void setUp(){
+
+ initServConf();
+
+ return;
+ }
+
+ @After()
+ public void teardown() {
+ }
+
+ @Test
+ public void exporterMethodConfigUrlTest(){
+
+ verifyExporterUrlGeneration(methodConfForService, methodConfForServiceTable);
+ }
+
+ @Test
+ public void exporterServiceConfigUrlTest(){
+
+ verifyExporterUrlGeneration(servConf, servConfTable);
+ }
+
+ @Test
+ public void exporterProviderConfigUrlTest(){
+
+ verifyExporterUrlGeneration(provConf, provConfTable);
+ }
+
+ @Test
+ public void exporterRegistryConfigUrlTest(){
+
+ //verifyExporterUrlGeneration(regConfForService, regConfForServiceTable);
+ }
+
+
+ protected <T> void verifyExporterUrlGeneration(T config, Object[][] dataTable) {
+
+ // 1. fill corresponding config with data
+ ////////////////////////////////////////////////////////////
+ fillConfigs(config, dataTable, TESTVALUE1);
+
+ // 2. export service and get url parameter string from db
+ ////////////////////////////////////////////////////////////
+ servConf.export();
+ String paramStringFromDb = getProviderParamString();
+ try {
+ paramStringFromDb = URLDecoder.decode(paramStringFromDb, "UTF-8");
+ } catch (UnsupportedEncodingException e) {
+ // impossible
+ }
+
+
+ assertUrlStringWithLocalTable(paramStringFromDb, dataTable, config.getClass().getName(), TESTVALUE1);
+
+
+ // 4. unexport service
+ ////////////////////////////////////////////////////////////
+ servConf.unexport();
+ }
+}
\ No newline at end of file
diff --git a/dubbo-config/src/test/java/com/alibaba/dubbo/config/url/test/InvokerSideConfigUrlTest.java b/dubbo-config/src/test/java/com/alibaba/dubbo/config/url/test/InvokerSideConfigUrlTest.java
new file mode 100644
index 0000000..f22f80e
--- /dev/null
+++ b/dubbo-config/src/test/java/com/alibaba/dubbo/config/url/test/InvokerSideConfigUrlTest.java
@@ -0,0 +1,236 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.config.url.test;
+
+import java.util.Arrays;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Ignore;
+import org.junit.Test;
+
+import com.alibaba.dubbo.common.logger.Logger;
+import com.alibaba.dubbo.common.logger.LoggerFactory;
+import com.alibaba.dubbo.config.ApplicationConfig;
+import com.alibaba.dubbo.config.ConsumerConfig;
+import com.alibaba.dubbo.config.MethodConfig;
+import com.alibaba.dubbo.config.ReferenceConfig;
+import com.alibaba.dubbo.config.RegistryConfig;
+import com.alibaba.dubbo.config.api.DemoService;
+import com.alibaba.dubbo.config.support.MockRegistry;
+
+
+/**
+ * @author haomin.liuhm
+ *
+ */
+public class InvokerSideConfigUrlTest extends UrlTestBase {
+ private static final Logger log = LoggerFactory.getLogger(InvokerSideConfigUrlTest.class);
+
+ // ======================================================
+ // invoker related data preparing
+ // ======================================================
+ private ApplicationConfig appConfForConsumer;
+ private ApplicationConfig appConfForReference;
+ private RegistryConfig regConfForConsumer;
+ private RegistryConfig regConfForReference;
+ private MethodConfig methodConfForReference;
+ private ConsumerConfig consumerConf;
+ private ReferenceConfig<DemoService> refConf;
+
+ private Object appConfForConsumerTable[][] = {
+ {"", "", "", "", "", "", "", "", "", ""},
+ };
+
+ private Object appConfForReferenceTable[][] = {
+ {"", "", "", "", "", "", "", "", "", ""},
+ };
+
+ private Object regConfForConsumerTable[][] = {
+// {"timeout", "registry.timeout", "int", 5000, 9000, "", "", "", "", ""},
+// {"file", "registry.file", "string", "", "regConfForServiceTable.log", "", "", "", "", ""},
+// {"wait", "registry.wait", "int", 0, 9000, "", "", "", "", ""},
+// {"transport", "registry.transporter", "string", "netty", "mina", "", "", "", "", ""},
+ {"subscribe", "subscribe", "boolean", true, false, "", "", "", "", ""},
+ {"dynamic", "dynamic", "boolean", true, false, "", "", "", "", ""},
+ };
+
+ private Object regConfForReferenceTable[][] = {
+ {"timeout", "registry.timeout", "int", 5000, 9000, "", "", "", "", ""},
+ {"file", "registry.file", "string", "", "regConfForServiceTable.log", "", "", "", "", ""},
+ {"wait", "registry.wait", "int", 0, 9000, "", "", "", "", ""},
+ {"transport", "registry.transporter", "string", "netty", "mina", "", "", "", "", ""},
+ {"subscribe", "subscribe", "boolean", true, false, "", "", "", "", ""},
+ {"dynamic", "dynamic", "boolean", true, false, "", "", "", "", ""},
+ };
+
+ private Object methodConfForReferenceTable[][] = {
+ {"actives", "eatTiger.actives", "int", 0, 90, "", "", "", "", ""},
+ {"executes", "eatTiger.executes", "int", 0, 90, "", "", "", "", ""},
+ {"deprecated", "eatTiger.deprecated", "boolean", false, true, "", "", "", "", ""},
+ {"async", "eatTiger.async", "boolean", false, true, "", "", "", "", ""},
+ {"timeout", "eatTiger.timeout", "int", 0, 90, "", "", "", "", ""},
+ };
+
+ private Object refConfTable[][] = {
+// {"version", "version", "string", "0.0.0", "1.2.3", "", "", "", "", ""},
+// {"group", "group", "string", "", "HaominTest", "", "", "", "", ""},
+
+// {"delay", "delay", "int", 0, 5, "", "", "", "", ""}, // not boolean
+ {"timeout", "timeout", "int", 5000, 3000, "", "", "", "", ""},
+ {"retries", "retries", "int", 2, 5, "", "", "", "", ""},
+ {"connections", "connections", "boolean", 100, 20, "", "", "", "", ""},
+ {"loadbalance", "loadbalance", "string", "random", "roundrobin", "leastactive", "", "", ""},
+ {"async", "async", "boolean", false, true, "", "", "", "", ""},
+ //excluded = true
+// {"generic", "generic", "boolean", false, true, "", "", "", "", ""},
+ {"check", "check", "boolean", false, true, "", "", "", "", ""},
+ //{"local", "local", "string", "false", "HelloServiceLocal", "true", "", "", "", ""},
+ //{"local", "local", "string", "false", "true", "", "", "", "", ""},
+ //{"mock", "mock", "string", "false", "dubbo.test.HelloServiceMock", "true", "", "", "", ""},
+ {"mock", "mock", "string", "false", "false", "", "", "", "", ""},
+ {"proxy", "proxy", "boolean", "javassist", "jdk", "", "", "", "", ""},
+ {"client", "client", "string", "netty", "mina", "", "", "", "", ""},
+ {"client", "client", "string", "netty", "mina", "", "", "", "", ""},
+ {"owner", "owner", "string", "", "haomin/ludvik", "", "", "", "", ""},
+ {"actives", "actives", "int", 0, 30, "", "", "", "", ""},
+ {"cluster", "cluster", "string", "failover", "failfast", "failsafe", "failback", "forking", "", ""},
+ //excluded = true
+// {"filter", "service.filter", "string", "default", "-generic", "", "", "", "", ""},
+ //excluded = true
+// {"listener", "exporter.listener", "string", "default", "-deprecated", "", "", "", "", ""},
+ //{"", "", "", "", "", "", "", "", "", ""},
+ };
+
+ private Object consumerConfTable[][] = {
+ {"timeout", "default.timeout", "int", 5000, 8000, "", "", "", "", ""},
+ {"retries", "default.retries", "int", 2, 5, "", "", "", "", ""},
+ {"loadbalance", "default.loadbalance", "string", "random", "leastactive", "", "", "", "", ""},
+ {"async", "default.async", "boolean", false, true, "", "", "", "", ""},
+ {"connections", "default.connections", "int", 100, 5, "", "", "", "", ""},
+// {"generic", "generic", "boolean", false, false, "", "", "", "", ""},
+ {"check", "check", "boolean", true, false, "", "", "", "", ""},
+ {"proxy", "proxy", "string", "javassist", "jdk", "javassist", "", "", "", ""},
+ {"owner", "owner", "string", "", "haomin", "", "", "", "", ""},
+ {"actives", "default.actives", "int", 0, 5, "", "", "", "", ""},
+ {"cluster", "default.cluster", "string", "failover", "forking", "", "", "", "", ""},
+ {"filter", "", "string", "", "", "", "", "", "", ""},
+ {"listener", "", "string", "", "", "", "", "", "", ""},
+// {"", "", "", "", "", "", "", "", "", ""},
+ };
+
+ // ======================================================
+ // test Start
+ // ======================================================
+
+ @BeforeClass
+ public static void start(){
+
+ //RegistryController.startRegistryIfAbsence(1);
+ }
+
+
+ @Before
+ public void setUp(){
+
+ initServConf();
+ initRefConf();
+
+ return;
+ }
+
+ @After()
+ public void teardown() {
+
+ //RegistryServer.reloadCache();
+ }
+
+
+ @Test
+ public void consumerConfUrlTest(){
+ verifyInvokerUrlGeneration(consumerConf, consumerConfTable);
+ }
+
+ @Test
+ public void refConfUrlTest(){
+ verifyInvokerUrlGeneration(refConf, refConfTable);
+ }
+
+ @Ignore
+ @Test //注册中心的参数不会在与consumer的query merge
+ public void regConfForConsumerUrlTest(){
+
+ verifyInvokerUrlGeneration(regConfForConsumer, regConfForConsumerTable);
+
+ }
+
+ // ======================================================
+ // private helper
+ // ======================================================
+ private void initRefConf(){
+
+ appConfForConsumer = new ApplicationConfig();
+ appConfForReference = new ApplicationConfig();
+ regConfForConsumer = new RegistryConfig();
+ regConfForReference = new RegistryConfig();
+ methodConfForReference = new MethodConfig();
+
+ refConf = new ReferenceConfig<DemoService>();
+ consumerConf = new ConsumerConfig();
+
+ refConf.setApplication(appConfForReference);
+ consumerConf.setApplication(appConfForConsumer);
+
+ refConf.setRegistry(regConfForReference);
+ consumerConf.setRegistry(regConfForConsumer);
+
+ refConf.setConsumer(consumerConf);
+
+ refConf.setMethods(Arrays.asList(new MethodConfig[]{methodConfForReference}));
+
+ methodConfForReference.setName("sayName");
+ regConfForReference.setAddress("127.0.0.1:9090");
+ regConfForReference.setProtocol("mockregistry");
+ appConfForReference.setName("ConfigTests");
+ refConf.setInterface("com.alibaba.dubbo.config.api.DemoService");
+ }
+
+ private <T> void verifyInvokerUrlGeneration(T config, Object[][] dataTable){
+ servConf.export();
+
+ fillConfigs(config, dataTable, TESTVALUE1);
+ refConf.get();
+
+ String subScribedUrlStr = getSubscribedUrlString();
+
+ System.out.println("url string=========:"+subScribedUrlStr);
+ String configName = config.getClass().getName();
+ int column = TESTVALUE1;
+
+ assertUrlStringWithLocalTable(subScribedUrlStr, dataTable, configName, column);
+
+ //重新refer会判断如果已经订阅过,不再重新订阅。
+ try {
+ refConf.destroy();
+ } catch (Exception e) {
+ }
+ }
+
+ private String getSubscribedUrlString() {
+ return MockRegistry.getSubscribedUrl().toString();
+ }
+}
\ No newline at end of file
diff --git a/dubbo-config/src/test/java/com/alibaba/dubbo/config/url/test/UrlTestBase.java b/dubbo-config/src/test/java/com/alibaba/dubbo/config/url/test/UrlTestBase.java
new file mode 100644
index 0000000..85ec90d
--- /dev/null
+++ b/dubbo-config/src/test/java/com/alibaba/dubbo/config/url/test/UrlTestBase.java
@@ -0,0 +1,218 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.config.url.test;
+
+
+import java.util.Arrays;
+
+import org.junit.Assert;
+
+import com.alibaba.dubbo.common.logger.Logger;
+import com.alibaba.dubbo.common.logger.LoggerFactory;
+import com.alibaba.dubbo.config.ApplicationConfig;
+import com.alibaba.dubbo.config.MethodConfig;
+import com.alibaba.dubbo.config.ProtocolConfig;
+import com.alibaba.dubbo.config.ProviderConfig;
+import com.alibaba.dubbo.config.RegistryConfig;
+import com.alibaba.dubbo.config.ServiceConfig;
+import com.alibaba.dubbo.config.api.DemoService;
+import com.alibaba.dubbo.config.provider.impl.DemoServiceImpl;
+import com.alibaba.dubbo.config.support.RpcConfigGetSetProxy;
+
+/**
+ * @author haomin.liuhm
+ *
+ */
+
+@SuppressWarnings("unused")
+public class UrlTestBase {
+
+ private static final Logger log = LoggerFactory.getLogger(UrlTestBase.class);
+
+ // ======================================================
+ // data column definition
+ // ======================================================
+ protected static final int KEY = 0;
+ protected static final int URL_KEY = 1;
+ private static final int TYPE = 2;
+ private static final int DEFAULT = 3;
+ protected static final int TESTVALUE1 = 4;
+ private static final int TESTVALUE2 = 5;
+ private static final int TESTVALUE3 = 6;
+ private static final int TESTVALUE4 = 7;
+ private static final int TESTVALUE5 = 8;
+ private static final int TESTVALUE6 = 9;
+ private static final int TESTVALUE7 = 10;
+ protected ApplicationConfig appConfForProvider;
+ protected ApplicationConfig appConfForService;
+ protected RegistryConfig regConfForProvider;
+ protected RegistryConfig regConfForService;
+ protected ProviderConfig provConf;
+ protected ProtocolConfig protoConfForProvider;
+ protected ProtocolConfig protoConfForService;
+ protected MethodConfig methodConfForService;
+ protected ServiceConfig<DemoService> servConf;
+ protected Object servConfTable[][] = {
+ {"proxy", "proxy", "string", "javassist", "jdk", "javassist", "", "", "", ""},
+ {"actives", "actives", "int", 0, 90, "", "", "", "", ""},
+ {"executes", "executes", "int", 0, 90, "", "", "", "", ""},
+ {"deprecated", "deprecated", "boolean", false, true, "", "", "", "", ""},
+ {"dynamic", "dynamic", "boolean", true, false, "", "", "", "", ""},
+ {"accesslog", "accesslog", "string", "", "haominTest", "", "", "", "", ""},
+ {"document", "document", "string", "", "http://b2b-doc.alibaba-inc.com/display/RC/dubbo_devguide.htm?testquery=你好你好", "", "", "", "", ""},
+ {"weight", "weight", "int", 0, 90, "", "", "", "", ""},
+
+ //{"filter", "service.filter", "string", "", "", "", "", "", "", ""},
+ //{"listener", "listener", "string", "", "", "", "", "", "", ""},
+
+ };
+
+ private Object appConfForProviderTable[][] = {
+ {"", "", "", "", "", "", "", "", "", ""},
+ };
+ private Object appConfForServiceTable[][] = {
+ {"", "", "", "", "", "", "", "", "", ""},
+ };
+ private Object regConfForProviderTable[][] = {
+ {"", "", "", "", "", "", "", "", "", ""},
+ };
+ protected Object regConfForServiceTable[][] = {
+ // {"timeout", "registry.timeout", "int", 5000, 9000, "", "", "", "", ""},
+ // {"file", "registry.file", "string", "", "regConfForServiceTable.log", "", "", "", "", ""},
+ // {"wait", "registry.wait", "int", 0, 9000, "", "", "", "", ""},
+ // {"transport", "registry.transporter", "string", "netty", "mina", "", "", "", "", ""},
+ // {"subscribe", "subscribe", "boolean", true, false, "", "", "", "", ""},
+ {"dynamic", "dynamic", "boolean", true, false, "", "", "", "", ""},
+ };
+ private Object protoConfForProviderTable[][] = {
+ {"", "", "", "", "", "", "", "", "", ""},
+ };
+ private Object protoConfForServiceTable[][] = {
+ {"", "", "", "", "", "", "", "", "", ""},
+ };
+ protected Object provConfTable[][] = {
+ {"cluster", "default.cluster", "string", "string", "failover", "failfast", "failsafe", "", "", ""},
+ {"async", "default.async", "boolean", false, true, "", "", "", "", ""},
+ {"loadbalance", "default.loadbalance", "string", "random", "leastactive", "", "", "", "", ""},
+ {"connections", "default.connections", "int", 0, 60, "", "", "", "", ""},
+ {"retries", "default.retries", "int", 2, 60, "", "", "", "", ""},
+ {"timeout", "default.timeout", "int", 5000, 60, "", "", "", "", ""},
+ //change by fengting listener 没有缺省值
+ //{"listener", "exporter.listener", "string", "", "", "", "", "", "", ""},
+ //{"filter", "service.filter", "string", "", "", "", "", "", "", ""},
+
+ };
+ protected Object methodConfForServiceTable[][] = {
+ {"actives", "sayName.actives", "int", 0, 90, "", "", "", "", ""},
+ {"executes", "sayName.executes", "int", 0, 90, "", "", "", "", ""},
+ {"deprecated", "sayName.deprecated", "boolean", false, true, "", "", "", "", ""},
+ {"async", "sayName.async", "boolean", false, true, "", "", "", "", ""},
+ {"timeout", "sayName.timeout", "int", 0, 90, "", "", "", "", ""},
+ };
+ protected DemoService demoService = new DemoServiceImpl();
+
+ // ======================================================
+ // data table manipulation utils
+ // ======================================================
+ protected String genParamString(Object urlKey, Object value) {
+
+ return (String)urlKey + "=" + value.toString();
+ }
+
+ protected <T> void fillConfigs(T conf, Object[][] table, int column) {
+
+ for(Object[] row : table){
+ fillConfig(conf, row, column);
+ }
+ }
+
+ protected <T> void fillConfig(T conf, Object[] row, int column) {
+
+ RpcConfigGetSetProxy proxy = new RpcConfigGetSetProxy(conf);
+ proxy.setValue((String)row[KEY], row[column]);
+
+ }
+
+ @SuppressWarnings("deprecation")
+ protected void initServConf() {
+
+ appConfForProvider = new ApplicationConfig();
+ appConfForService = new ApplicationConfig();
+ regConfForProvider = new RegistryConfig();
+ regConfForService = new RegistryConfig();
+ provConf = new ProviderConfig();
+ protoConfForProvider = new ProtocolConfig();
+ protoConfForService = new ProtocolConfig();
+ methodConfForService = new MethodConfig();
+ servConf = new ServiceConfig<DemoService>();
+
+ provConf.setApplication(appConfForProvider);
+ servConf.setApplication(appConfForService);
+
+ provConf.setRegistry(regConfForProvider);
+ servConf.setRegistry(regConfForService);
+
+ provConf.setProtocols(Arrays.asList(new ProtocolConfig[]{protoConfForProvider}));
+ servConf.setProtocols(Arrays.asList(new ProtocolConfig[]{protoConfForService}));
+
+ servConf.setMethods(Arrays.asList(new MethodConfig[]{methodConfForService}));
+ servConf.setProvider(provConf);
+
+ servConf.setRef(demoService);
+ servConf.setInterfaceClass(DemoService.class);
+
+ methodConfForService.setName("sayName");
+ regConfForService.setAddress("127.0.0.1:9090");
+ regConfForService.setProtocol("mockregistry");
+ appConfForService.setName("ConfigTests");
+ }
+
+ protected String getProviderParamString() {
+ return servConf.getExportedUrls().get(0).toString();
+ }
+
+ /**
+ * @param paramStringFromDb
+ * @param dataTable
+ * @param configName
+ * @param column
+ */
+ protected void assertUrlStringWithLocalTable(String paramStringFromDb,
+ Object[][] dataTable, String configName, int column) {
+ final String FAILLOG_HEADER = "The following config items are not found in URL: ";
+
+ log.warn("Verifying service url for " + configName + "... ");
+ log.warn("Consumer url string: " + paramStringFromDb);
+
+ String failLog = FAILLOG_HEADER;
+ for(Object[] row : dataTable){
+
+ String targetString = genParamString(row[URL_KEY], row[column]);
+
+ log.warn("Checking " + (String)row[KEY] + "for" + targetString);
+ if (paramStringFromDb.contains(targetString)){
+ log.warn((String)row[KEY] + " --> " + targetString + " OK!");
+ } else {
+ failLog += targetString + ", ";
+ }
+ }
+
+ if( !failLog.equals(FAILLOG_HEADER)){
+ Assert.fail(failLog);
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-config/src/test/resources/META-INF/services/com.alibaba.dubbo.registry.RegistryFactory b/dubbo-config/src/test/resources/META-INF/services/com.alibaba.dubbo.registry.RegistryFactory
new file mode 100644
index 0000000..6095da6
--- /dev/null
+++ b/dubbo-config/src/test/resources/META-INF/services/com.alibaba.dubbo.registry.RegistryFactory
@@ -0,0 +1 @@
+com.alibaba.dubbo.config.support.MockRegistryFactory
diff --git a/dubbo-config/src/test/resources/META-INF/services/com.alibaba.dubbo.rpc.Protocol b/dubbo-config/src/test/resources/META-INF/services/com.alibaba.dubbo.rpc.Protocol
new file mode 100644
index 0000000..53472a6
--- /dev/null
+++ b/dubbo-config/src/test/resources/META-INF/services/com.alibaba.dubbo.rpc.Protocol
@@ -0,0 +1 @@
+com.alibaba.dubbo.config.support.MockProtocol
\ No newline at end of file
diff --git a/dubbo-config/src/test/resources/com/alibaba/dubbo/config/delay-fixed-time.xml b/dubbo-config/src/test/resources/com/alibaba/dubbo/config/delay-fixed-time.xml
new file mode 100644
index 0000000..62d22b4
--- /dev/null
+++ b/dubbo-config/src/test/resources/com/alibaba/dubbo/config/delay-fixed-time.xml
@@ -0,0 +1,36 @@
+<!--
+ - Copyright 1999-2011 Alibaba Group.
+ -
+ - Licensed under the Apache License, Version 2.0 (the "License");
+ - you may not use this file except in compliance with the License.
+ - You may obtain a copy of the License at
+ -
+ - http://www.apache.org/licenses/LICENSE-2.0
+ -
+ - Unless required by applicable law or agreed to in writing, software
+ - distributed under the License is distributed on an "AS IS" BASIS,
+ - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ - See the License for the specific language governing permissions and
+ - limitations under the License.
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
+ xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
+ http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd
+ ">
+
+ <!-- 当前应用信息配置 -->
+ <dubbo:application name="demo-provider" />
+
+ <!-- 连接注册中心配置 -->
+ <dubbo:registry address="127.0.0.1:4548" />
+
+ <dubbo:protocol name="dubbo" port="20883" />
+
+ <!-- 暴露服务配置 -->
+ <dubbo:service interface="com.alibaba.dubbo.config.api.DemoService" ref="demoService" delay="300" />
+
+ <bean id="demoService" class="com.alibaba.dubbo.config.provider.impl.DemoServiceImpl" />
+
+</beans>
\ No newline at end of file
diff --git a/dubbo-config/src/test/resources/com/alibaba/dubbo/config/delay-on-initialized.xml b/dubbo-config/src/test/resources/com/alibaba/dubbo/config/delay-on-initialized.xml
new file mode 100644
index 0000000..0ad0f4e
--- /dev/null
+++ b/dubbo-config/src/test/resources/com/alibaba/dubbo/config/delay-on-initialized.xml
@@ -0,0 +1,36 @@
+<!--
+ - Copyright 1999-2011 Alibaba Group.
+ -
+ - Licensed under the Apache License, Version 2.0 (the "License");
+ - you may not use this file except in compliance with the License.
+ - You may obtain a copy of the License at
+ -
+ - http://www.apache.org/licenses/LICENSE-2.0
+ -
+ - Unless required by applicable law or agreed to in writing, software
+ - distributed under the License is distributed on an "AS IS" BASIS,
+ - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ - See the License for the specific language governing permissions and
+ - limitations under the License.
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
+ xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
+ http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd
+ ">
+
+ <!-- 当前应用信息配置 -->
+ <dubbo:application name="demo-provider" />
+
+ <!-- 连接注册中心配置 -->
+ <dubbo:registry address="127.0.0.1:4548" />
+
+ <dubbo:protocol name="dubbo" port="20883" />
+
+ <!-- 暴露服务配置 -->
+ <dubbo:service interface="com.alibaba.dubbo.config.api.DemoService" ref="demoService" delay="-1" />
+
+ <bean id="demoService" class="com.alibaba.dubbo.config.provider.impl.DemoServiceImpl" />
+
+</beans>
\ No newline at end of file
diff --git a/dubbo-config/src/test/resources/com/alibaba/dubbo/config/multi-protocol-default.xml b/dubbo-config/src/test/resources/com/alibaba/dubbo/config/multi-protocol-default.xml
new file mode 100644
index 0000000..85ae98c
--- /dev/null
+++ b/dubbo-config/src/test/resources/com/alibaba/dubbo/config/multi-protocol-default.xml
@@ -0,0 +1,41 @@
+<!--
+ - Copyright 1999-2011 Alibaba Group.
+ -
+ - Licensed under the Apache License, Version 2.0 (the "License");
+ - you may not use this file except in compliance with the License.
+ - You may obtain a copy of the License at
+ -
+ - http://www.apache.org/licenses/LICENSE-2.0
+ -
+ - Unless required by applicable law or agreed to in writing, software
+ - distributed under the License is distributed on an "AS IS" BASIS,
+ - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ - See the License for the specific language governing permissions and
+ - limitations under the License.
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
+ xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
+ http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd
+ ">
+
+ <!-- 当前应用信息配置 -->
+ <dubbo:application name="demo-provider" />
+
+ <!-- 连接注册中心配置 -->
+ <dubbo:registry address="N/A" />
+
+ <!-- 暴露服务协议配置 -->
+ <dubbo:protocol name="dubbo" port="20881" />
+
+ <dubbo:protocol name="rmi" port="10991" />
+
+ <dubbo:provider protocol="rmi" />
+
+ <!-- 暴露服务配置 -->
+ <dubbo:service interface="com.alibaba.dubbo.config.api.DemoService" ref="demoService" />
+
+ <bean id="demoService" class="com.alibaba.dubbo.config.provider.impl.DemoServiceImpl" />
+
+</beans>
\ No newline at end of file
diff --git a/dubbo-config/src/test/resources/com/alibaba/dubbo/config/multi-protocol-error.xml b/dubbo-config/src/test/resources/com/alibaba/dubbo/config/multi-protocol-error.xml
new file mode 100644
index 0000000..474750a
--- /dev/null
+++ b/dubbo-config/src/test/resources/com/alibaba/dubbo/config/multi-protocol-error.xml
@@ -0,0 +1,39 @@
+<!--
+ - Copyright 1999-2011 Alibaba Group.
+ -
+ - Licensed under the Apache License, Version 2.0 (the "License");
+ - you may not use this file except in compliance with the License.
+ - You may obtain a copy of the License at
+ -
+ - http://www.apache.org/licenses/LICENSE-2.0
+ -
+ - Unless required by applicable law or agreed to in writing, software
+ - distributed under the License is distributed on an "AS IS" BASIS,
+ - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ - See the License for the specific language governing permissions and
+ - limitations under the License.
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
+ xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
+ http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd
+ ">
+
+ <!-- 当前应用信息配置 -->
+ <dubbo:application name="demo-provider" />
+
+ <!-- 连接注册中心配置 -->
+ <dubbo:registry address="N/A" />
+
+ <!-- 暴露服务协议配置 -->
+ <dubbo:protocol name="dubbo" port="20881" />
+
+ <dubbo:protocol name="rmi" port="10991" />
+
+ <!-- 暴露服务配置 -->
+ <dubbo:service interface="com.alibaba.dubbo.config.api.DemoService" ref="demoService" />
+
+ <bean id="demoService" class="com.alibaba.dubbo.config.provider.impl.DemoServiceImpl" />
+
+</beans>
\ No newline at end of file
diff --git a/dubbo-config/src/test/resources/com/alibaba/dubbo/config/multi-protocol-register.xml b/dubbo-config/src/test/resources/com/alibaba/dubbo/config/multi-protocol-register.xml
new file mode 100644
index 0000000..4be9bde
--- /dev/null
+++ b/dubbo-config/src/test/resources/com/alibaba/dubbo/config/multi-protocol-register.xml
@@ -0,0 +1,39 @@
+<!--
+ - Copyright 1999-2011 Alibaba Group.
+ -
+ - Licensed under the Apache License, Version 2.0 (the "License");
+ - you may not use this file except in compliance with the License.
+ - You may obtain a copy of the License at
+ -
+ - http://www.apache.org/licenses/LICENSE-2.0
+ -
+ - Unless required by applicable law or agreed to in writing, software
+ - distributed under the License is distributed on an "AS IS" BASIS,
+ - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ - See the License for the specific language governing permissions and
+ - limitations under the License.
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
+ xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
+ http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd
+ ">
+
+ <!-- 当前应用信息配置 -->
+ <dubbo:application name="demo-provider" />
+
+ <!-- 连接注册中心配置 -->
+ <dubbo:registry address="127.0.0.1:4547" />
+
+ <!-- 暴露服务协议配置 -->
+ <dubbo:protocol name="dubbo" port="20824" />
+
+ <dubbo:protocol name="rmi" port="10924" register="false" />
+
+ <!-- 暴露服务配置 -->
+ <dubbo:service interface="com.alibaba.dubbo.config.api.DemoService" ref="demoService" protocol="dubbo,rmi" />
+
+ <bean id="demoService" class="com.alibaba.dubbo.config.provider.impl.DemoServiceImpl" />
+
+</beans>
\ No newline at end of file
diff --git a/dubbo-config/src/test/resources/com/alibaba/dubbo/config/multi-protocol.xml b/dubbo-config/src/test/resources/com/alibaba/dubbo/config/multi-protocol.xml
new file mode 100644
index 0000000..5b2638c
--- /dev/null
+++ b/dubbo-config/src/test/resources/com/alibaba/dubbo/config/multi-protocol.xml
@@ -0,0 +1,39 @@
+<!--
+ - Copyright 1999-2011 Alibaba Group.
+ -
+ - Licensed under the Apache License, Version 2.0 (the "License");
+ - you may not use this file except in compliance with the License.
+ - You may obtain a copy of the License at
+ -
+ - http://www.apache.org/licenses/LICENSE-2.0
+ -
+ - Unless required by applicable law or agreed to in writing, software
+ - distributed under the License is distributed on an "AS IS" BASIS,
+ - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ - See the License for the specific language governing permissions and
+ - limitations under the License.
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
+ xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
+ http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd
+ ">
+
+ <!-- 当前应用信息配置 -->
+ <dubbo:application name="demo-provider" />
+
+ <!-- 连接注册中心配置 -->
+ <dubbo:registry address="N/A" />
+
+ <!-- 暴露服务协议配置 -->
+ <dubbo:protocol name="dubbo" port="20881" />
+
+ <dubbo:protocol name="rmi" port="10991" />
+
+ <!-- 暴露服务配置 -->
+ <dubbo:service interface="com.alibaba.dubbo.config.api.DemoService" ref="demoService" protocol="dubbo" />
+
+ <bean id="demoService" class="com.alibaba.dubbo.config.provider.impl.DemoServiceImpl" />
+
+</beans>
\ No newline at end of file
diff --git a/dubbo-config/src/test/resources/com/alibaba/dubbo/config/multi-registry.xml b/dubbo-config/src/test/resources/com/alibaba/dubbo/config/multi-registry.xml
new file mode 100644
index 0000000..a7814c2
--- /dev/null
+++ b/dubbo-config/src/test/resources/com/alibaba/dubbo/config/multi-registry.xml
@@ -0,0 +1,36 @@
+<!--
+ - Copyright 1999-2011 Alibaba Group.
+ -
+ - Licensed under the Apache License, Version 2.0 (the "License");
+ - you may not use this file except in compliance with the License.
+ - You may obtain a copy of the License at
+ -
+ - http://www.apache.org/licenses/LICENSE-2.0
+ -
+ - Unless required by applicable law or agreed to in writing, software
+ - distributed under the License is distributed on an "AS IS" BASIS,
+ - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ - See the License for the specific language governing permissions and
+ - limitations under the License.
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
+ xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
+ http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd
+ ">
+
+ <!-- 当前应用信息配置 -->
+ <dubbo:application name="demo-provider" />
+
+ <!-- 连接注册中心配置 -->
+ <dubbo:registry id="reg1" address="127.0.0.1:4545" />
+
+ <dubbo:registry id="reg2" address="127.0.0.1:4546" />
+
+ <!-- 暴露服务配置 -->
+ <dubbo:service interface="com.alibaba.dubbo.config.api.DemoService" ref="demoService" registry="reg2" />
+
+ <bean id="demoService" class="com.alibaba.dubbo.config.provider.impl.DemoServiceImpl" />
+
+</beans>
\ No newline at end of file
diff --git a/dubbo-config/src/test/resources/log4j.xml b/dubbo-config/src/test/resources/log4j.xml
new file mode 100644
index 0000000..7d71634
--- /dev/null
+++ b/dubbo-config/src/test/resources/log4j.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ - Copyright 1999-2011 Alibaba Group.
+ -
+ - Licensed under the Apache License, Version 2.0 (the "License");
+ - you may not use this file except in compliance with the License.
+ - You may obtain a copy of the License at
+ -
+ - http://www.apache.org/licenses/LICENSE-2.0
+ -
+ - Unless required by applicable law or agreed to in writing, software
+ - distributed under the License is distributed on an "AS IS" BASIS,
+ - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ - See the License for the specific language governing permissions and
+ - limitations under the License.
+-->
+<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
+<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/" debug="false">
+ <appender name="CONSOLE" class="org.apache.log4j.ConsoleAppender">
+ <layout class="org.apache.log4j.PatternLayout">
+ <param name="ConversionPattern" value="[%d{dd/MM/yy hh:mm:ss:sss z}] %t %5p %c{2}: %m%n" />
+ </layout>
+ </appender>
+ <root>
+ <level value="INFO" />
+ <appender-ref ref="CONSOLE" />
+ </root>
+</log4j:configuration>
\ No newline at end of file
diff --git a/dubbo-container/pom.xml b/dubbo-container/pom.xml
new file mode 100644
index 0000000..8e8d246
--- /dev/null
+++ b/dubbo-container/pom.xml
@@ -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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>com.alibaba</groupId>
+ <artifactId>dubbo-parent</artifactId>
+ <version>2.0.8</version>
+ </parent>
+ <artifactId>dubbo-container</artifactId>
+ <packaging>jar</packaging>
+ <name>Dubbo Container Module</name>
+ <description>The container module of dubbo project</description>
+ <properties>
+ <skip_maven_deploy>false</skip_maven_deploy>
+ </properties>
+ <dependencies>
+ <dependency>
+ <groupId>com.alibaba</groupId>
+ <artifactId>dubbo-config</artifactId>
+ <version>${project.parent.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.mortbay.jetty</groupId>
+ <artifactId>jetty</artifactId>
+ </dependency>
+ </dependencies>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-jar-plugin</artifactId>
+ <configuration>
+ <archive>
+ <addMavenDescriptor>true</addMavenDescriptor>
+ <manifest>
+ <mainClass>com.alibaba.dubbo.container.Main</mainClass>
+ </manifest>
+ </archive>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+</project>
\ No newline at end of file
diff --git a/dubbo-container/src/main/java/com/alibaba/dubbo/container/Container.java b/dubbo-container/src/main/java/com/alibaba/dubbo/container/Container.java
new file mode 100644
index 0000000..f64c46a
--- /dev/null
+++ b/dubbo-container/src/main/java/com/alibaba/dubbo/container/Container.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.container;
+
+import com.alibaba.dubbo.common.Extension;
+
+/**
+ * Container. (SPI, Singleton, ThreadSafe)
+ *
+ * @author william.liangf
+ */
+@Extension("spring")
+public interface Container {
+
+ /**
+ * start.
+ */
+ void start();
+
+ /**
+ * stop.
+ */
+ void stop();
+
+}
\ No newline at end of file
diff --git a/dubbo-container/src/main/java/com/alibaba/dubbo/container/Main.java b/dubbo-container/src/main/java/com/alibaba/dubbo/container/Main.java
new file mode 100644
index 0000000..3fcd8b2
--- /dev/null
+++ b/dubbo-container/src/main/java/com/alibaba/dubbo/container/Main.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.container;
+
+import java.util.Arrays;
+
+import com.alibaba.dubbo.common.ExtensionLoader;
+import com.alibaba.dubbo.common.logger.Logger;
+import com.alibaba.dubbo.common.logger.LoggerFactory;
+
+/**
+ * Main. (API, Static, ThreadSafe)
+ *
+ * @author william.liangf
+ */
+public class Main {
+
+ private static final Logger logger = LoggerFactory.getLogger(Main.class);
+
+ public static void main(String[] args) {
+ final Container[] containers;
+ if(null == args || args.length == 0) {
+ containers = new Container[] {ExtensionLoader.getExtensionLoader(Container.class).getDefaultExtension()};
+ logger.info("Use default container type(" + ExtensionLoader.getExtensionLoader(Container.class).getDefaultExtensionName() + ") to run dubbo serivce.");
+ } else {
+ containers = new Container[args.length];
+ for (int i = 0; i < args.length; i ++) {
+ containers[i] = ExtensionLoader.getExtensionLoader(Container.class).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();
+ } catch (Throwable t) {
+ logger.error(t.getMessage(), t);
+ }
+ }
+ }
+ });
+ for (Container container : containers) {
+ container.start();
+ }
+ synchronized (Main.class) {
+ for (;;) {
+ try {
+ Main.class.wait();
+ } catch (Throwable e) {
+ }
+ }
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-container/src/main/java/com/alibaba/dubbo/container/jetty/JettyContainer.java b/dubbo-container/src/main/java/com/alibaba/dubbo/container/jetty/JettyContainer.java
new file mode 100644
index 0000000..3f99ac2
--- /dev/null
+++ b/dubbo-container/src/main/java/com/alibaba/dubbo/container/jetty/JettyContainer.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.container.jetty;
+
+import org.mortbay.jetty.Server;
+import org.mortbay.jetty.nio.SelectChannelConnector;
+import org.mortbay.jetty.servlet.ServletHandler;
+import org.mortbay.jetty.servlet.ServletHolder;
+
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.common.logger.Logger;
+import com.alibaba.dubbo.common.logger.LoggerFactory;
+import com.alibaba.dubbo.common.utils.NetUtils;
+import com.alibaba.dubbo.container.Container;
+import com.alibaba.dubbo.container.page.PageServlet;
+
+/**
+ * JettyContainer. (SPI, Singleton, ThreadSafe)
+ *
+ * @author william.liangf
+ */
+@Extension("jetty")
+public class JettyContainer implements Container {
+
+ private static final Logger logger = LoggerFactory.getLogger(JettyContainer.class);
+
+ public static final String JETTY_PORT = "jetty.port";
+
+ public static final int DEFAULT_JETTY_PORT = 8080;
+
+ private SelectChannelConnector connector;
+
+ public void start() {
+ String serverPort = System.getProperty(JETTY_PORT);
+ int port;
+ if (serverPort == null || serverPort.length() == 0) {
+ port = DEFAULT_JETTY_PORT;
+ } else {
+ port = Integer.parseInt(serverPort);
+ }
+ SelectChannelConnector connector = new SelectChannelConnector();
+ connector.setPort(port);
+ ServletHandler handler = new ServletHandler();
+ ServletHolder holder = handler.addServletWithMapping(PageServlet.class, "/*");
+ holder.setInitOrder(1);
+ 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);
+ }
+ logger.info("Dubbo jetty container started!");
+ }
+
+ public void stop() {
+ try {
+ if (connector != null) {
+ connector.close();
+ }
+ } catch (Throwable e) {
+ logger.error(e.getMessage(), e);
+ }
+ logger.info("Dubbo jetty container stopped!");
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-container/src/main/java/com/alibaba/dubbo/container/log4j/Log4jContainer.java b/dubbo-container/src/main/java/com/alibaba/dubbo/container/log4j/Log4jContainer.java
new file mode 100644
index 0000000..2196601
--- /dev/null
+++ b/dubbo-container/src/main/java/com/alibaba/dubbo/container/log4j/Log4jContainer.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.container.log4j;
+
+import java.util.Enumeration;
+import java.util.Properties;
+
+import org.apache.log4j.Appender;
+import org.apache.log4j.FileAppender;
+import org.apache.log4j.LogManager;
+import org.apache.log4j.PropertyConfigurator;
+
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.common.logger.Logger;
+import com.alibaba.dubbo.common.logger.LoggerFactory;
+import com.alibaba.dubbo.container.Container;
+
+/**
+ * Log4jContainer. (SPI, Singleton, ThreadSafe)
+ *
+ * @author william.liangf
+ */
+@Extension("log4j")
+public class Log4jContainer implements Container {
+
+ private static final Logger logger = LoggerFactory.getLogger(Log4jContainer.class);
+
+ public static final String LOG4J_FILE = "log4j.file";
+
+ public static final String LOG4J_LEVEL = "log4j.level";
+
+ public static final String LOG4J_SUBDIRECTORY = "log4j.subdirectory";
+
+ public static final String DEFAULT_LOG4J_FILE = System.getProperty("user.home") + "/dubbo.log";
+
+ public static final String DEFAULT_LOG4J_LEVEL = "ERROR";
+
+ @SuppressWarnings("unchecked")
+ public void start() {
+ String file = System.getProperty(LOG4J_FILE);
+ if (file == null || file.length() == 0) {
+ file = DEFAULT_LOG4J_FILE;
+ }
+ String level = System.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 = System.getProperty(LOG4J_SUBDIRECTORY);
+ if (subdirectory != null && subdirectory.length() > 0) {
+ Enumeration<org.apache.log4j.Logger> ls = LogManager.getCurrentLoggers();
+ while (ls.hasMoreElements()) {
+ modifyLogDirectory(ls.nextElement(), subdirectory);
+ }
+ }
+ logger.info("Dubbo log4j container started!");
+ }
+
+ public void stop() {
+ logger.info("Dubbo log4j container stopped!");
+ }
+
+ @SuppressWarnings("unchecked")
+ private static String[] modifyLogDirectory(org.apache.log4j.Logger l, String subdirectory) {
+ String[] params = new String[3];
+ if (l != null) {
+ params[2] = l.getLevel() == null ? null : l.getLevel().toString();
+ Enumeration<Appender> as = l.getAllAppenders();
+ while (as.hasMoreElements()) {
+ Appender a = as.nextElement();
+ if (a instanceof FileAppender) {
+ FileAppender fa = (FileAppender)a;
+ String file = fa.getFile();
+ if (file != null && file.length() > 0) {
+ int i = file.replace('\\', '/').lastIndexOf('/');
+ String path;
+ if (i == -1) {
+ path = subdirectory;
+ } else {
+ path = file.substring(0, i);
+ if (! path.endsWith(subdirectory)) {
+ path = path + "/" + subdirectory;
+ }
+ file = file.substring(i + 1);
+ }
+ params[0] = path;
+ params[1] = file;
+ fa.setFile(path + "/" + file);
+ fa.activateOptions();
+ }
+ }
+ }
+ }
+ return params;
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-container/src/main/java/com/alibaba/dubbo/container/page/Page.java b/dubbo-container/src/main/java/com/alibaba/dubbo/container/page/Page.java
new file mode 100644
index 0000000..73c2340
--- /dev/null
+++ b/dubbo-container/src/main/java/com/alibaba/dubbo/container/page/Page.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.container.page;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Page
+ *
+ * @author william.liangf
+ */
+public class Page {
+
+ private final String navigation;
+
+ private final String title;
+
+ private final List<String> columns;
+
+ private final List<List<String>> rows;
+
+ public Page(String navigation, String title,
+ String column, String row) {
+ this(navigation, title, column == null ? null : Arrays.asList(new String[]{column}), row == null ? null : stringToList(row));
+ }
+
+ private static List<List<String>> stringToList(String str) {
+ List<List<String>> rows = new ArrayList<List<String>>();
+ List<String> row = new ArrayList<String>();
+ row.add(str);
+ rows.add(row);
+ return rows;
+ }
+
+ public Page(String navigation, String title,
+ String[] columns, List<List<String>> rows) {
+ this(navigation, title, columns == null ? null : Arrays.asList(columns), rows);
+ }
+
+ public Page(String navigation, String title,
+ List<String> columns, List<List<String>> rows) {
+ this.navigation = navigation;
+ this.title = title;
+ this.columns = columns;
+ this.rows = rows;
+ }
+
+ public String getNavigation() {
+ return navigation;
+ }
+
+ public String getTitle() {
+ return title;
+ }
+
+ public List<String> getColumns() {
+ return columns;
+ }
+
+ public List<List<String>> getRows() {
+ return rows;
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-container/src/main/java/com/alibaba/dubbo/container/page/PageHandler.java b/dubbo-container/src/main/java/com/alibaba/dubbo/container/page/PageHandler.java
new file mode 100644
index 0000000..f6cc1bf
--- /dev/null
+++ b/dubbo-container/src/main/java/com/alibaba/dubbo/container/page/PageHandler.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.container.page;
+
+import com.alibaba.dubbo.common.URL;
+
+/**
+ * PageHandler
+ *
+ * @author william.liangf
+ */
+public interface PageHandler {
+
+ /**
+ * Handle the page.
+ *
+ * @param url
+ * @return the page.
+ */
+ Page handle(URL url);
+
+}
\ No newline at end of file
diff --git a/dubbo-container/src/main/java/com/alibaba/dubbo/container/page/PageServlet.java b/dubbo-container/src/main/java/com/alibaba/dubbo/container/page/PageServlet.java
new file mode 100644
index 0000000..b6f2abf
--- /dev/null
+++ b/dubbo-container/src/main/java/com/alibaba/dubbo/container/page/PageServlet.java
@@ -0,0 +1,199 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.Collection;
+import java.util.List;
+import java.util.Random;
+import java.util.Set;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import com.alibaba.dubbo.common.ExtensionLoader;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.logger.Logger;
+import com.alibaba.dubbo.common.logger.LoggerFactory;
+
+/**
+ * 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 String applicationName;
+
+ public void setApplicationName(String applicationName) {
+ this.applicationName = applicationName;
+ }
+
+ protected final void doGet(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 = "home";
+ isHtml = true;
+ } else {
+ if (uri.startsWith("/")) {
+ uri = uri.substring(1);
+ }
+ if (uri.endsWith(".html")) {
+ uri = uri.substring(0, uri.length() - ".html".length());
+ isHtml = true;
+ }
+ }
+ PageHandler pageFactory = ExtensionLoader.getExtensionLoader(PageHandler.class).getExtension(uri);
+ if (isHtml) {
+ writer.println("<html><head><title>" + applicationName + " service server</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: 800px;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;height: 25px; } a {color: #FFFFFF;cursor: pointer;text-decoration: underline; } a:hover {text-decoration: none; }</style>");
+ writer.println("</head><body>");
+ }
+ if (pageFactory != null) {
+ String query = request.getQueryString();
+ Page page = pageFactory.handle(URL.valueOf(request.getRequestURL().toString()
+ + (query == null || query.length() == 0 ? "" : "?" + query)));
+ if (page != null) {
+ if (isHtml) {
+ writeMenu(request, writer, page.getNavigation());
+ writeTable(writer, page.getTitle(), page.getColumns(),
+ page.getRows());
+ } else {
+ writer.println(page.getTitle());
+ }
+ }
+ } 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>");
+ Set<String> names = ExtensionLoader.getExtensionLoader(PageHandler.class).getSupportedExtensions();
+ for (String name : names) {
+ writer.println(" <th><a href=\"" + name + ".html\">" + name + "</a></th>");
+ }
+ writer.println(" </tr>");
+ writer.println("</thead>");
+ writer.println("<tbody>");
+ writer.println(" <tr>");
+ writer.println(" <td style=\"text-align: left\" colspan=\"" + names.size() + "\">");
+ writer.println(nav);
+ writer.println(" </td>");
+ writer.println(" </tr>");
+ writer.println("</tbody>");
+ writer.println("</table>");
+ writer.println("<br/>");
+ }
+
+ protected final void writeTable(PrintWriter writer, String title, List<String> columns,
+ List<List<String>> rows) {
+ int n = random.nextInt();
+ int c = (columns == null ? (rows == null || rows.size() == 0 ? 0 : rows.get(0).size())
+ : columns.size());
+ int r = (rows == null ? 0 : rows.size());
+ writer.println("<table>");
+ writer.println("<thead>");
+ writer.println(" <tr>");
+ writer.println(" <th colspan=\"" + c + "\">" + title + "</th>");
+ writer.println(" </tr>");
+ if (columns != null && columns.size() > 0) {
+ writer.println(" <tr>");
+ for (int i = 0; i < columns.size(); i++) {
+ String col = columns.get(i);
+ if (col.endsWith(":")) {
+ col += " <input type=\"text\" id=\"in_"
+ + n
+ + "_"
+ + i
+ + "\" onkeyup=\"for (var i = 0; i < "
+ + r
+ + "; i ++) { var m = true; for (var j = 0; j < "
+ + columns.size()
+ + "; j ++) { if (document.getElementById('in_"
+ + n
+ + "_' + j)) { var iv = document.getElementById('in_"
+ + n
+ + "_' + j).value; var tv = document.getElementById('td_"
+ + n
+ + "_' + i + '_' + j).innerHTML; if (iv.length > 0 && (tv.length < iv.length || tv.indexOf(iv) == -1)) { m = false; break; } } } document.getElementById('tr_"
+ + n
+ + "_' + i).style.display = (m ? '' : 'none');}\" sytle=\"width: 100%\" />";
+ }
+ writer.println(" <td>" + col + "</td>");
+ }
+ writer.println(" </tr>");
+ }
+ writer.println("</thead>");
+ if (rows != null && rows.size() > 0) {
+ writer.println("<tbody>");
+ int i = 0;
+ for (Collection<String> row : rows) {
+ writer.println(" <tr id=\"tr_" + n + "_" + i + "\">");
+ int j = 0;
+ for (String col : row) {
+ writer.println(" <td id=\"td_" + n + "_" + i + "_" + j
+ + "\" style=\"display: ;\">" + col + "</td>");
+ j++;
+ }
+ writer.println(" </tr>");
+ i++;
+ }
+ writer.println("</tbody>");
+ }
+ writer.println("</table>");
+ writer.println("<br/>");
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-container/src/main/java/com/alibaba/dubbo/container/page/pages/ConnectionPageHandler.java b/dubbo-container/src/main/java/com/alibaba/dubbo/container/page/pages/ConnectionPageHandler.java
new file mode 100644
index 0000000..ca9e078
--- /dev/null
+++ b/dubbo-container/src/main/java/com/alibaba/dubbo/container/page/pages/ConnectionPageHandler.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.page.pages;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.logger.Logger;
+import com.alibaba.dubbo.common.logger.LoggerFactory;
+import com.alibaba.dubbo.container.page.Page;
+import com.alibaba.dubbo.container.page.PageHandler;
+
+/**
+ * ConnectionPageHandler
+ *
+ * @author william.liangf
+ */
+@Extension("connection")
+public class ConnectionPageHandler implements PageHandler {
+
+ protected final Logger logger = LoggerFactory.getLogger(getClass());
+
+ public Page handle(URL url) {
+ List<List<String>> rows = new ArrayList<List<String>>();
+ /*TBServer tbServer = TBServerManager.getInstance().getServer();
+ Server server = tbServer.getServer();
+ try {
+ Field field = server.getClass().getDeclaredField("acceptor");
+ field.setAccessible(true);
+ IoAcceptor acceptor = (IoAcceptor) field.get(server);
+ for (SocketAddress address : acceptor.getManagedServiceAddresses()) {
+ for (IoSession session : acceptor.getManagedSessions(address)) {
+ List<String> row = new ArrayList<String>();
+ row.add(session.getRemoteAddress().toString().replace("<", "<").replace(">",
+ ">"));
+ rows.add(row);
+ }
+ }
+ } catch (Throwable t) {
+ logger.error(t.getMessage(), t);
+ }*/
+ return new Page("<a href=\"/\">Home</a> > Connection", "Connections (" + rows.size() + ")",
+ new String[] { "Client Address:" }, rows);
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-container/src/main/java/com/alibaba/dubbo/container/page/pages/HomePageHandler.java b/dubbo-container/src/main/java/com/alibaba/dubbo/container/page/pages/HomePageHandler.java
new file mode 100644
index 0000000..c89b853
--- /dev/null
+++ b/dubbo-container/src/main/java/com/alibaba/dubbo/container/page/pages/HomePageHandler.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.container.page.pages;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.common.ExtensionLoader;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.container.page.Page;
+import com.alibaba.dubbo.container.page.PageHandler;
+
+/**
+ * HomePageHandler
+ *
+ * @author william.liangf
+ */
+@Extension("home")
+public class HomePageHandler implements PageHandler {
+
+ public Page handle(URL url) {
+ Collection<String> informationProviders = ExtensionLoader.getExtensionLoader(PageHandler.class).getSupportedExtensions();
+ List<List<String>> rows = new ArrayList<List<String>>();
+ for (String uri : informationProviders) {
+ List<String> row = new ArrayList<String>();
+ row.add("<a href=\"" + uri + ".html\">" + uri + "</a>");
+ rows.add(row);
+ }
+ return new Page("Home", "Menus", new String[] {"Menu"}, rows);
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-container/src/main/java/com/alibaba/dubbo/container/page/pages/LogPageHandler.java b/dubbo-container/src/main/java/com/alibaba/dubbo/container/page/pages/LogPageHandler.java
new file mode 100644
index 0000000..3deff67
--- /dev/null
+++ b/dubbo-container/src/main/java/com/alibaba/dubbo/container/page/pages/LogPageHandler.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.container.page.pages;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.channels.FileChannel;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.Enumeration;
+import java.util.List;
+
+import org.apache.log4j.Appender;
+import org.apache.log4j.FileAppender;
+import org.apache.log4j.Level;
+import org.apache.log4j.LogManager;
+
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.container.page.Page;
+import com.alibaba.dubbo.container.page.PageHandler;
+
+/**
+ * LogPageHandler
+ *
+ * @author william.liangf
+ */
+@Extension("log")
+public class LogPageHandler implements PageHandler {
+
+ private static final int SHOW_LOG_LENGTH = 30000;
+
+ private File file;
+
+ @SuppressWarnings("unchecked")
+ public LogPageHandler() {
+ try {
+ org.apache.log4j.Logger logger = LogManager.getRootLogger();
+ if (logger != null) {
+ Enumeration<Appender> appenders = logger.getAllAppenders();
+ if (appenders != null) {
+ while (appenders.hasMoreElements()) {
+ Appender appender = appenders.nextElement();
+ if (appender instanceof FileAppender) {
+ FileAppender fileAppender = (FileAppender)appender;
+ String filename = fileAppender.getFile();
+ file = new File(filename);
+ break;
+ }
+ }
+ }
+ }
+ } catch (Throwable t) {
+ }
+ }
+
+ public Page handle(URL url) {
+ long size = 0;
+ String content = "";
+ String modified = "Not exist";
+ if (file != null && file.exists()) {
+ try {
+ FileInputStream fis = new FileInputStream(file);
+ FileChannel channel = fis.getChannel();
+ size = channel.size();
+ ByteBuffer bb;
+ if (size <= SHOW_LOG_LENGTH) {
+ bb = ByteBuffer.allocate((int) size);
+ channel.read(bb, 0);
+ } else {
+ int pos = (int) (size - SHOW_LOG_LENGTH);
+ bb = ByteBuffer.allocate(SHOW_LOG_LENGTH);
+ channel.read(bb, pos);
+ }
+ bb.flip();
+ content = new String(bb.array()).replace("<", "<")
+ .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("Home", "Log", new String[] {(file == null ? "" : file.getName()) + ", " + size + " bytes, " + modified + ", " + level}, rows);
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-container/src/main/java/com/alibaba/dubbo/container/page/pages/RegistryPageHandler.java b/dubbo-container/src/main/java/com/alibaba/dubbo/container/page/pages/RegistryPageHandler.java
new file mode 100644
index 0000000..71bb9a1
--- /dev/null
+++ b/dubbo-container/src/main/java/com/alibaba/dubbo/container/page/pages/RegistryPageHandler.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.container.page.pages;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.container.page.Page;
+import com.alibaba.dubbo.container.page.PageHandler;
+import com.alibaba.dubbo.registry.Registry;
+import com.alibaba.dubbo.registry.support.AbstractRegistryFactory;
+
+/**
+ * RegistryPageHandler
+ *
+ * @author william.liangf
+ */
+@Extension("registry")
+public class RegistryPageHandler implements PageHandler {
+
+ public Page handle(URL url) {
+ List<List<String>> rows = new ArrayList<List<String>>();
+ Collection<Registry> registries = AbstractRegistryFactory.getRegistries();
+ if (registries != null && registries.size() > 0) {
+ int i = 0;
+ for (Registry registry : registries) {
+ i ++;
+ String server = registry.getUrl().getAddress();
+ List<String> row = new ArrayList<String>();
+ row.add(String.valueOf(i));
+ row.add(server);
+ if (registry.isAvailable()) {
+ row.add("Connected");
+ } else {
+ row.add("");
+ }
+ rows.add(row);
+ }
+ }
+ return new Page("<a href=\"/\">Home</a> > Registry", "Registries (" + rows.size() + ")",
+ new String[] { "Server Group:", "Server Address:", "Is Connected" }, rows);
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-container/src/main/java/com/alibaba/dubbo/container/page/pages/ServicePageHandler.java b/dubbo-container/src/main/java/com/alibaba/dubbo/container/page/pages/ServicePageHandler.java
new file mode 100644
index 0000000..60d0ab3
--- /dev/null
+++ b/dubbo-container/src/main/java/com/alibaba/dubbo/container/page/pages/ServicePageHandler.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.container.page.pages;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Set;
+
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.container.page.Page;
+import com.alibaba.dubbo.container.page.PageHandler;
+import com.alibaba.dubbo.registry.Registry;
+import com.alibaba.dubbo.registry.support.AbstractRegistry;
+import com.alibaba.dubbo.registry.support.AbstractRegistryFactory;
+
+/**
+ * ServicePageHandler
+ *
+ * @author william.liangf
+ */
+@Extension("service")
+public class ServicePageHandler implements PageHandler {
+
+ public Page handle(URL url) {
+ List<List<String>> rows = new ArrayList<List<String>>();
+ Collection<Registry> registries = AbstractRegistryFactory.getRegistries();
+ if (registries != null && registries.size() > 0) {
+ Registry registry = registries.iterator().next();
+ if (registry instanceof AbstractRegistry) {
+ Set<String> services = ((AbstractRegistry) registry).getRegistered();
+ if (services != null && services.size() > 0) {
+ for (String u : services) {
+ List<String> row = new ArrayList<String>();
+ row.add(URL.valueOf(u).getServiceName().replace("<", "<").replace(">", ">"));
+ row.add(u.toString().replace("<", "<").replace(">", ">"));
+ rows.add(row);
+ }
+ }
+ }
+ }
+ return new Page("<a href=\"/\">Home</a> > Service", "Services (" + rows.size() + ")",
+ new String[] { "Service Type:", "URL:" }, rows);
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-container/src/main/java/com/alibaba/dubbo/container/page/pages/StatusPageHandler.java b/dubbo-container/src/main/java/com/alibaba/dubbo/container/page/pages/StatusPageHandler.java
new file mode 100644
index 0000000..76f3255
--- /dev/null
+++ b/dubbo-container/src/main/java/com/alibaba/dubbo/container/page/pages/StatusPageHandler.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.container.page.pages;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.common.ExtensionLoader;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.status.Status;
+import com.alibaba.dubbo.common.status.StatusChecker;
+import com.alibaba.dubbo.common.status.support.StatusUtils;
+import com.alibaba.dubbo.container.page.Page;
+import com.alibaba.dubbo.container.page.PageHandler;
+
+/**
+ * StatusPageHandler
+ *
+ * @author william.liangf
+ */
+@Extension("status")
+public class StatusPageHandler implements PageHandler {
+
+ public Page handle(URL url) {
+ List<List<String>> rows = new ArrayList<List<String>>();
+ Set<String> names = ExtensionLoader.getExtensionLoader(StatusChecker.class).getSupportedExtensions();
+ Map<String, Status> statuses = new HashMap<String, Status>();
+ for (String name : names) {
+ StatusChecker checker = ExtensionLoader.getExtensionLoader(StatusChecker.class).getExtension(name);
+ List<String> row = new ArrayList<String>();
+ row.add(name);
+ Status status = checker.check();
+ 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(), "", "");
+ }
+ 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("<a href=\"/\">Home</a> > Status (<a href=\"/status\" target=\"_blank\">All</a>)", "Status", new String[] {"Name", "Status", "Description"}, rows);
+ }
+
+ private String getLevelHtml(Status.Level level) {
+ return "<font color=\"" + getLevelColor(level) + "\">" + level.name() + "</font>";
+ }
+
+ private String getLevelColor(Status.Level level) {
+ if (level == Status.Level.OK) {
+ return "green";
+ } else if (level == Status.Level.ERROR) {
+ return "red";
+ } else if (level == Status.Level.WARN) {
+ return "yellow";
+ }
+ return "gray";
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-container/src/main/java/com/alibaba/dubbo/container/page/pages/SystemPageHandler.java b/dubbo-container/src/main/java/com/alibaba/dubbo/container/page/pages/SystemPageHandler.java
new file mode 100644
index 0000000..2dfced4
--- /dev/null
+++ b/dubbo-container/src/main/java/com/alibaba/dubbo/container/page/pages/SystemPageHandler.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.container.page.pages;
+
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.Locale;
+
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.container.page.Page;
+import com.alibaba.dubbo.container.page.PageHandler;
+
+/**
+ * SystemPageHandler
+ *
+ * @author william.liangf
+ */
+@Extension("system")
+public class SystemPageHandler implements PageHandler {
+
+ public Page handle(URL url) {
+ List<List<String>> rows = new ArrayList<List<String>>();
+ List<String> 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("Time");
+ row.add(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS Z").format(new Date()));
+ rows.add(row);
+ row = new ArrayList<String>();
+ row.add("Locale");
+ row.add(Locale.getDefault().toString() + "/" + System.getProperty("file.encoding"));
+ rows.add(row);
+ return new Page("<a href=\"/\">Home</a> > System", "System", new String[] {
+ "Name", "Value" }, rows);
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-container/src/main/java/com/alibaba/dubbo/container/spring/SpringContainer.java b/dubbo-container/src/main/java/com/alibaba/dubbo/container/spring/SpringContainer.java
new file mode 100644
index 0000000..5dae0d8
--- /dev/null
+++ b/dubbo-container/src/main/java/com/alibaba/dubbo/container/spring/SpringContainer.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.container.spring;
+
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.support.ClassPathXmlApplicationContext;
+
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.common.logger.Logger;
+import com.alibaba.dubbo.common.logger.LoggerFactory;
+import com.alibaba.dubbo.container.Container;
+
+/**
+ * SpringContainer. (SPI, Singleton, ThreadSafe)
+ *
+ * @author william.liangf
+ */
+@Extension("spring")
+public class SpringContainer implements Container {
+
+ private static final Logger logger = LoggerFactory.getLogger(SpringContainer.class);
+
+ public static final String SPRING_CONFIG = "spring.config";
+
+ public static final String DEFAULT_SPRING_CONFIG = "classpath*:META-INF/spring/*.xml";
+
+ private ClassPathXmlApplicationContext context;
+
+ public ApplicationContext getApplicationContext() {
+ return context;
+ }
+
+ public void start() {
+ String configPath = System.getProperty(SPRING_CONFIG);
+ if (configPath == null || configPath.length() == 0) {
+ configPath = DEFAULT_SPRING_CONFIG;
+ }
+ context = new ClassPathXmlApplicationContext(configPath.split("[,\\s]+"));
+ context.start();
+ logger.info("Dubbo spring container started!");
+ }
+
+ public void stop() {
+ try {
+ context.stop();
+ } catch (Throwable e) {
+ logger.error(e.getMessage(), e);
+ }
+ try {
+ context.close();
+ } catch (Throwable e) {
+ logger.error(e.getMessage(), e);
+ }
+ logger.info("Dubbo spring container stopped!");
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-container/src/main/resources/META-INF/services/com.alibaba.dubbo.container.Container b/dubbo-container/src/main/resources/META-INF/services/com.alibaba.dubbo.container.Container
new file mode 100644
index 0000000..cbfb8cb
--- /dev/null
+++ b/dubbo-container/src/main/resources/META-INF/services/com.alibaba.dubbo.container.Container
@@ -0,0 +1,3 @@
+com.alibaba.dubbo.container.spring.SpringContainer
+com.alibaba.dubbo.container.jetty.JettyContainer
+com.alibaba.dubbo.container.log4j.Log4jContainer
diff --git a/dubbo-container/src/main/resources/META-INF/services/com.alibaba.dubbo.container.page.PageHandler b/dubbo-container/src/main/resources/META-INF/services/com.alibaba.dubbo.container.page.PageHandler
new file mode 100644
index 0000000..10d3fe7
--- /dev/null
+++ b/dubbo-container/src/main/resources/META-INF/services/com.alibaba.dubbo.container.page.PageHandler
@@ -0,0 +1,7 @@
+com.alibaba.dubbo.container.page.pages.ConnectionPageHandler
+com.alibaba.dubbo.container.page.pages.HomePageHandler
+com.alibaba.dubbo.container.page.pages.LogPageHandler
+com.alibaba.dubbo.container.page.pages.RegistryPageHandler
+com.alibaba.dubbo.container.page.pages.ServicePageHandler
+com.alibaba.dubbo.container.page.pages.StatusPageHandler
+com.alibaba.dubbo.container.page.pages.SystemPageHandler
\ No newline at end of file
diff --git a/dubbo-container/src/test/java/com/alibaba/dubbo/container/standalone/SpringContainerTest.java b/dubbo-container/src/test/java/com/alibaba/dubbo/container/standalone/SpringContainerTest.java
new file mode 100644
index 0000000..d49751e
--- /dev/null
+++ b/dubbo-container/src/test/java/com/alibaba/dubbo/container/standalone/SpringContainerTest.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.standalone;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+import com.alibaba.dubbo.container.spring.SpringContainer;
+
+/**
+ * StandaloneContainerTest
+ *
+ * @author william.liangf
+ */
+public class SpringContainerTest {
+
+ @Test
+ public void testContainer() {
+ SpringContainer container = new SpringContainer();
+ container.start();
+ Assert.assertEquals(SpringContainer.class, container.getApplicationContext().getBean("container").getClass());
+ container.stop();
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-container/src/test/resources/META-INF/spring/test.xml b/dubbo-container/src/test/resources/META-INF/spring/test.xml
new file mode 100644
index 0000000..4ddff13
--- /dev/null
+++ b/dubbo-container/src/test/resources/META-INF/spring/test.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ - Copyright 1999-2011 Alibaba Group.
+ -
+ - Licensed under the Apache License, Version 2.0 (the "License");
+ - you may not use this file except in compliance with the License.
+ - You may obtain a copy of the License at
+ -
+ - http://www.apache.org/licenses/LICENSE-2.0
+ -
+ - Unless required by applicable law or agreed to in writing, software
+ - distributed under the License is distributed on an "AS IS" BASIS,
+ - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ - See the License for the specific language governing permissions and
+ - limitations under the License.
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
+
+ <bean id="container" class="com.alibaba.dubbo.container.spring.SpringContainer" />
+
+</beans>
\ No newline at end of file
diff --git a/dubbo-monitor-simple/pom.xml b/dubbo-monitor-simple/pom.xml
new file mode 100644
index 0000000..5f0670b
--- /dev/null
+++ b/dubbo-monitor-simple/pom.xml
@@ -0,0 +1,42 @@
+<!--
+ - Copyright 1999-2011 Alibaba Group.
+ -
+ - Licensed under the Apache License, Version 2.0 (the "License");
+ - you may not use this file except in compliance with the License.
+ - You may obtain a copy of the License at
+ -
+ - http://www.apache.org/licenses/LICENSE-2.0
+ -
+ - Unless required by applicable law or agreed to in writing, software
+ - distributed under the License is distributed on an "AS IS" BASIS,
+ - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ - See the License for the specific language governing permissions and
+ - limitations under the License.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>com.alibaba</groupId>
+ <artifactId>dubbo-parent</artifactId>
+ <version>2.0.8</version>
+ </parent>
+ <artifactId>dubbo-monitor-simple</artifactId>
+ <packaging>jar</packaging>
+ <name>Dubbo Default Monitor Module</name>
+ <description>The default monitor module of dubbo project</description>
+ <properties>
+ <skip_maven_deploy>false</skip_maven_deploy>
+ </properties>
+ <dependencies>
+ <dependency>
+ <groupId>com.alibaba</groupId>
+ <artifactId>dubbo-monitor</artifactId>
+ <version>${project.parent.version}</version>
+ </dependency>
+ <!--<dependency>
+ <groupId>jfree</groupId>
+ <artifactId>jfreechart</artifactId>
+ </dependency>-->
+ </dependencies>
+</project>
\ No newline at end of file
diff --git a/dubbo-monitor-simple/src/main/java/com/alibaba/dubbo/monitor/dubbo/DubboMonitor.java b/dubbo-monitor-simple/src/main/java/com/alibaba/dubbo/monitor/dubbo/DubboMonitor.java
new file mode 100644
index 0000000..420f2ba
--- /dev/null
+++ b/dubbo-monitor-simple/src/main/java/com/alibaba/dubbo/monitor/dubbo/DubboMonitor.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.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("DubboMonitorSenderTimer", 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());
+ }
+ 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.SUCCESS, String.valueOf(success),
+ MonitorService.FAILURE, String.valueOf(failure),
+ MonitorService.INPUT, String.valueOf(input),
+ MonitorService.OUTPUT, String.valueOf(output),
+ MonitorService.ELAPSED, String.valueOf(elapsed),
+ MonitorService.CONCURRENT, String.valueOf(concurrent),
+ MonitorService.MAX_INPUT, String.valueOf(maxInput),
+ MonitorService.MAX_OUTPUT, String.valueOf(maxOutput),
+ MonitorService.MAX_ELAPSED, String.valueOf(maxElapsed),
+ MonitorService.MAX_CONCURRENT, String.valueOf(maxConcurrent));
+ monitorService.count(url);
+
+ // 减掉已统计数据
+ long[] current;
+ long[] update = new long[LENGTH];
+ do {
+ current = reference.get();
+ if (current == null) {
+ update[0] = 0;
+ update[1] = 0;
+ update[2] = 0;
+ update[3] = 0;
+ update[4] = 0;
+ update[5] = 0;
+ } else {
+ update[0] = current[0] - success;
+ update[1] = current[1] - failure;
+ update[2] = current[2] - input;
+ update[3] = current[3] - output;
+ update[4] = current[4] - output;
+ update[5] = current[5] - output;
+ }
+ } while (! reference.compareAndSet(current, update));
+ }
+ }
+
+ public void count(URL url) {
+ // 读写统计变量
+ int success = url.getParameter(MonitorService.SUCCESS, 0);
+ int failure = url.getParameter(MonitorService.FAILURE, 0);
+ int input = url.getParameter(MonitorService.INPUT, 0);
+ int output = url.getParameter(MonitorService.OUTPUT, 0);
+ int elapsed = url.getParameter(MonitorService.ELAPSED, 0);
+ int concurrent = url.getParameter(MonitorService.CONCURRENT, 0);
+ // 初始化原子引用
+ Statistics statistics = new Statistics(url);
+ AtomicReference<long[]> reference = statisticsMap.get(statistics);
+ if (reference == null) {
+ statisticsMap.putIfAbsent(statistics, new AtomicReference<long[]>());
+ reference = statisticsMap.get(statistics);
+ }
+ // CompareAndSet并发加入统计数据
+ long[] current;
+ long[] update = new long[LENGTH];
+ do {
+ current = reference.get();
+ if (current == null) {
+ update[0] = success;
+ update[1] = failure;
+ update[2] = input;
+ update[3] = output;
+ update[4] = elapsed;
+ update[5] = concurrent;
+ update[6] = input;
+ update[7] = output;
+ update[8] = elapsed;
+ update[9] = concurrent;
+ } else {
+ update[0] = current[0] + success;
+ update[1] = current[1] + failure;
+ update[2] = current[2] + input;
+ update[3] = current[3] + output;
+ update[4] = current[4] + elapsed;
+ update[5] = current[5] + concurrent;
+ 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-simple/src/main/java/com/alibaba/dubbo/monitor/dubbo/DubboMonitorFactroy.java b/dubbo-monitor-simple/src/main/java/com/alibaba/dubbo/monitor/dubbo/DubboMonitorFactroy.java
new file mode 100644
index 0000000..b51507c
--- /dev/null
+++ b/dubbo-monitor-simple/src/main/java/com/alibaba/dubbo/monitor/dubbo/DubboMonitorFactroy.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.monitor.dubbo;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.monitor.Monitor;
+import com.alibaba.dubbo.monitor.MonitorService;
+import com.alibaba.dubbo.monitor.support.AbstractMonitorFactroy;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.Protocol;
+import com.alibaba.dubbo.rpc.ProxyFactory;
+
+/**
+ * DefaultMonitorFactroy
+ *
+ * @author william.liangf
+ */
+@Extension("dubbo")
+public class DubboMonitorFactroy extends AbstractMonitorFactroy {
+
+ private Protocol protocol;
+
+ private ProxyFactory proxyFactory;
+
+ public void setProtocol(Protocol protocol) {
+ this.protocol = protocol;
+ }
+
+ public void setProxyFactory(ProxyFactory proxyFactory) {
+ this.proxyFactory = proxyFactory;
+ }
+
+ @Override
+ protected Monitor createMonitor(URL url) {
+ url = url.setProtocol(url.getParameter(Constants.PROTOCOL_KEY, "dubbo"));
+ String filter = url.getParameter(Constants.REFERENCE_FILTER_KEY);
+ if (filter == null || filter.length() == 0) {
+ filter = "";
+ } else {
+ filter = filter + ",";
+ }
+ url = url.addParameter(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-simple/src/main/java/com/alibaba/dubbo/monitor/dubbo/Statistics.java b/dubbo-monitor-simple/src/main/java/com/alibaba/dubbo/monitor/dubbo/Statistics.java
new file mode 100644
index 0000000..2437a27
--- /dev/null
+++ b/dubbo-monitor-simple/src/main/java/com/alibaba/dubbo/monitor/dubbo/Statistics.java
@@ -0,0 +1,200 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.monitor.dubbo;
+
+import java.io.Serializable;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.monitor.MonitorService;
+
+/**
+ * Statistics. (SPI, Prototype, ThreadSafe)
+ *
+ * @author william.liangf
+ */
+public class Statistics implements Serializable {
+
+ private static final long serialVersionUID = -6921183057683641441L;
+
+ private URL url;
+
+ private String application;
+
+ private String service;
+
+ private String method;
+
+ private String group;
+
+ private String version;
+
+ private String client;
+
+ private String server;
+
+ public Statistics(URL url) {
+ this.url = url;
+ this.application = url.getParameter(MonitorService.APPLICATION);
+ this.service = url.getParameter(MonitorService.INTERFACE);
+ this.method = url.getParameter(MonitorService.METHOD);
+ this.group = url.getParameter(MonitorService.GROUP);
+ this.version = url.getParameter(MonitorService.VERSION);
+ this.client = url.getParameter(MonitorService.CLIENT);
+ this.server = url.getParameter(MonitorService.SERVER);
+ }
+
+ public URL getUrl() {
+ return url;
+ }
+
+ public void setUrl(URL url) {
+ this.url = url;
+ }
+
+ public String getApplication() {
+ return application;
+ }
+
+ public Statistics setApplication(String application) {
+ this.application = application;
+ return this;
+ }
+
+ public String getService() {
+ return service;
+ }
+
+ public Statistics setService(String service) {
+ this.service = service;
+ return this;
+ }
+
+ public String getGroup() {
+ return group;
+ }
+
+ public void setGroup(String group) {
+ this.group = group;
+ }
+
+ public String getVersion() {
+ return version;
+ }
+
+ public void setVersion(String version) {
+ this.version = version;
+ }
+
+ public String getMethod() {
+ return method;
+ }
+
+ public Statistics setMethod(String method) {
+ this.method = method;
+ return this;
+ }
+
+ public String getClient() {
+ return client;
+ }
+
+ public Statistics setClient(String client) {
+ this.client = client;
+ return this;
+ }
+
+ public String getServer() {
+ return server;
+ }
+
+ public Statistics setServer(String server) {
+ this.server = server;
+ return this;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((application == null) ? 0 : application.hashCode());
+ result = prime * result + ((client == null) ? 0 : client.hashCode());
+ result = prime * result + ((group == null) ? 0 : group.hashCode());
+ result = prime * result + ((method == null) ? 0 : method.hashCode());
+ result = prime * result + ((server == null) ? 0 : server.hashCode());
+ result = prime * result + ((service == null) ? 0 : service.hashCode());
+ result = prime * result + ((url == null) ? 0 : url.hashCode());
+ result = prime * result + ((version == null) ? 0 : version.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ Statistics other = (Statistics) obj;
+ if (application == null) {
+ if (other.application != null)
+ return false;
+ } else if (!application.equals(other.application))
+ return false;
+ if (client == null) {
+ if (other.client != null)
+ return false;
+ } else if (!client.equals(other.client))
+ return false;
+ if (group == null) {
+ if (other.group != null)
+ return false;
+ } else if (!group.equals(other.group))
+ return false;
+ if (method == null) {
+ if (other.method != null)
+ return false;
+ } else if (!method.equals(other.method))
+ return false;
+ if (server == null) {
+ if (other.server != null)
+ return false;
+ } else if (!server.equals(other.server))
+ return false;
+ if (service == null) {
+ if (other.service != null)
+ return false;
+ } else if (!service.equals(other.service))
+ return false;
+ if (url == null) {
+ if (other.url != null)
+ return false;
+ } else if (!url.equals(other.url))
+ return false;
+ if (version == null) {
+ if (other.version != null)
+ return false;
+ } else if (!version.equals(other.version))
+ return false;
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ return url.toString();
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-monitor-simple/src/main/resources/META-INF/services/com.alibaba.dubbo.monitor.MonitorFactory b/dubbo-monitor-simple/src/main/resources/META-INF/services/com.alibaba.dubbo.monitor.MonitorFactory
new file mode 100644
index 0000000..94cedc1
--- /dev/null
+++ b/dubbo-monitor-simple/src/main/resources/META-INF/services/com.alibaba.dubbo.monitor.MonitorFactory
@@ -0,0 +1 @@
+com.alibaba.dubbo.monitor.dubbo.DubboMonitorFactroy
\ No newline at end of file
diff --git a/dubbo-monitor-simple/src/test/java/com/alibaba/dubbo/monitor/dubbo/DubboMonitorTest.java b/dubbo-monitor-simple/src/test/java/com/alibaba/dubbo/monitor/dubbo/DubboMonitorTest.java
new file mode 100644
index 0000000..3ab3807
--- /dev/null
+++ b/dubbo-monitor-simple/src/test/java/com/alibaba/dubbo/monitor/dubbo/DubboMonitorTest.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.monitor.dubbo;
+
+import junit.framework.Assert;
+
+import org.junit.Test;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.monitor.MonitorService;
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.Result;
+import com.alibaba.dubbo.rpc.RpcException;
+
+/**
+ * DubboMonitorTest
+ *
+ * @author william.liangf
+ */
+public class DubboMonitorTest {
+
+ private volatile URL lastStatistics;
+
+ private final Invoker<MonitorService> monitorInvoker = new Invoker<MonitorService>() {
+ public Class<MonitorService> getInterface() {
+ return MonitorService.class;
+ }
+ public URL getUrl() {
+ return URL.valueOf("dubbo://127.0.0.1:7070?interval=1");
+ }
+ public boolean isAvailable() {
+ return false;
+ }
+ public Result invoke(Invocation invocation) throws RpcException {
+ return null;
+ }
+ public void destroy() {
+ }
+ };
+
+ private final MonitorService monitorService = new MonitorService() {
+
+ public void count(URL statistics) {
+ DubboMonitorTest.this.lastStatistics = statistics;
+ }
+
+ };
+
+ @Test
+ public void testCount() throws Exception {
+ DubboMonitor monitor = new DubboMonitor(monitorInvoker, monitorService);
+ URL statistics = new URL("dubbo", "10.20.153.10", 0)
+ .addParameter(MonitorService.APPLICATION, "morgan")
+ .addParameter(MonitorService.INTERFACE, "MemberService")
+ .addParameter(MonitorService.METHOD, "findPerson")
+ .addParameter(MonitorService.CLIENT, "10.20.153.11")
+ .addParameter(MonitorService.SUCCESS, 1)
+ .addParameter(MonitorService.FAILURE, 0)
+ .addParameter(MonitorService.ELAPSED, 3)
+ .addParameter(MonitorService.MAX_ELAPSED, 3)
+ .addParameter(MonitorService.CONCURRENT, 1)
+ .addParameter(MonitorService.MAX_CONCURRENT, 1);
+ monitor.count(statistics);
+ while (lastStatistics == null) {
+ Thread.sleep(10);
+ }
+ Assert.assertEquals(statistics, lastStatistics);
+ monitor.destroy();
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-monitor/pom.xml b/dubbo-monitor/pom.xml
new file mode 100644
index 0000000..3357b03
--- /dev/null
+++ b/dubbo-monitor/pom.xml
@@ -0,0 +1,38 @@
+<!--
+ - Copyright 1999-2011 Alibaba Group.
+ -
+ - Licensed under the Apache License, Version 2.0 (the "License");
+ - you may not use this file except in compliance with the License.
+ - You may obtain a copy of the License at
+ -
+ - http://www.apache.org/licenses/LICENSE-2.0
+ -
+ - Unless required by applicable law or agreed to in writing, software
+ - distributed under the License is distributed on an "AS IS" BASIS,
+ - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ - See the License for the specific language governing permissions and
+ - limitations under the License.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>com.alibaba</groupId>
+ <artifactId>dubbo-parent</artifactId>
+ <version>2.0.8</version>
+ </parent>
+ <artifactId>dubbo-monitor</artifactId>
+ <packaging>jar</packaging>
+ <name>Dubbo Monitor Module</name>
+ <description>The monitor module of dubbo project</description>
+ <properties>
+ <skip_maven_deploy>false</skip_maven_deploy>
+ </properties>
+ <dependencies>
+ <dependency>
+ <groupId>com.alibaba</groupId>
+ <artifactId>dubbo-rpc</artifactId>
+ <version>${project.parent.version}</version>
+ </dependency>
+ </dependencies>
+</project>
\ No newline at end of file
diff --git a/dubbo-monitor/src/main/java/com/alibaba/dubbo/monitor/Monitor.java b/dubbo-monitor/src/main/java/com/alibaba/dubbo/monitor/Monitor.java
new file mode 100644
index 0000000..0c12a53
--- /dev/null
+++ b/dubbo-monitor/src/main/java/com/alibaba/dubbo/monitor/Monitor.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.monitor;
+
+import com.alibaba.dubbo.common.Node;
+
+/**
+ * Monitor. (SPI, Prototype, ThreadSafe)
+ *
+ * @see com.alibaba.dubbo.monitor.MonitorFactory#getMonitor(com.alibaba.dubbo.common.URL)
+ * @author william.liangf
+ */
+public interface Monitor extends Node, MonitorService {
+
+}
\ No newline at end of file
diff --git a/dubbo-monitor/src/main/java/com/alibaba/dubbo/monitor/MonitorFactory.java b/dubbo-monitor/src/main/java/com/alibaba/dubbo/monitor/MonitorFactory.java
new file mode 100644
index 0000000..53d5acf
--- /dev/null
+++ b/dubbo-monitor/src/main/java/com/alibaba/dubbo/monitor/MonitorFactory.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.monitor;
+
+import com.alibaba.dubbo.common.Adaptive;
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.common.URL;
+
+/**
+ * MonitorFactory. (SPI, Singleton, ThreadSafe)
+ *
+ * @author william.liangf
+ */
+@Extension("dubbo")
+public interface MonitorFactory {
+
+ /**
+ * Create monitor.
+ *
+ * @param url
+ * @return
+ */
+ @Adaptive("protocol")
+ Monitor getMonitor(URL url);
+
+}
\ No newline at end of file
diff --git a/dubbo-monitor/src/main/java/com/alibaba/dubbo/monitor/MonitorService.java b/dubbo-monitor/src/main/java/com/alibaba/dubbo/monitor/MonitorService.java
new file mode 100644
index 0000000..747df70
--- /dev/null
+++ b/dubbo-monitor/src/main/java/com/alibaba/dubbo/monitor/MonitorService.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.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 CLIENT = "client";
+
+ String SERVER = "server";
+
+ String SUCCESS = "success";
+
+ String FAILURE = "failure";
+
+ String INPUT = "input";
+
+ String OUTPUT = "output";
+
+ String ELAPSED = "elapsed";
+
+ String CONCURRENT = "concurrent";
+
+ String MAX_INPUT = "max.input";
+
+ String MAX_OUTPUT = "max.output";
+
+ String MAX_ELAPSED = "max.elapsed";
+
+ String MAX_CONCURRENT = "max.concurrent";
+
+ /**
+ * count.
+ *
+ * @param statistics
+ */
+ void count(URL statistics);
+
+}
\ No newline at end of file
diff --git a/dubbo-monitor/src/main/java/com/alibaba/dubbo/monitor/support/AbstractMonitorFactroy.java b/dubbo-monitor/src/main/java/com/alibaba/dubbo/monitor/support/AbstractMonitorFactroy.java
new file mode 100644
index 0000000..82198da
--- /dev/null
+++ b/dubbo-monitor/src/main/java/com/alibaba/dubbo/monitor/support/AbstractMonitorFactroy.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.monitor.support;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.locks.ReentrantLock;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.monitor.Monitor;
+import com.alibaba.dubbo.monitor.MonitorFactory;
+
+/**
+ * AbstractMonitorFactroy. (SPI, Singleton, ThreadSafe)
+ *
+ * @author william.liangf
+ */
+public abstract class AbstractMonitorFactroy implements MonitorFactory {
+
+ // 注册中心获取过程锁
+ private static final ReentrantLock LOCK = new ReentrantLock();
+
+ // 注册中心集合 Map<RegistryAddress, Registry>
+ private static final Map<String, Monitor> MONITORS = new ConcurrentHashMap<String, Monitor>();
+
+ public static Collection<Monitor> getMonitors() {
+ return Collections.unmodifiableCollection(MONITORS.values());
+ }
+
+ public Monitor getMonitor(URL url) {
+ LOCK.lock();
+ try {
+ String uri = url.toIdentityString();
+ Monitor monitor = MONITORS.get(uri);
+ if (monitor != null) {
+ return monitor;
+ }
+ monitor = createMonitor(url);
+ if (monitor == null) {
+ throw new IllegalStateException("Can not create monitor " + url);
+ }
+ MONITORS.put(uri, monitor);
+ return monitor;
+ } finally {
+ // 释放锁
+ LOCK.unlock();
+ }
+ }
+
+ protected abstract Monitor createMonitor(URL url);
+
+}
\ No newline at end of file
diff --git a/dubbo-monitor/src/main/java/com/alibaba/dubbo/monitor/support/MonitorFilter.java b/dubbo-monitor/src/main/java/com/alibaba/dubbo/monitor/support/MonitorFilter.java
new file mode 100644
index 0000000..f8d2ba5
--- /dev/null
+++ b/dubbo-monitor/src/main/java/com/alibaba/dubbo/monitor/support/MonitorFilter.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.monitor.support;
+
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.logger.Logger;
+import com.alibaba.dubbo.common.logger.LoggerFactory;
+import com.alibaba.dubbo.common.utils.NetUtils;
+import com.alibaba.dubbo.monitor.Monitor;
+import com.alibaba.dubbo.monitor.MonitorFactory;
+import com.alibaba.dubbo.monitor.MonitorService;
+import com.alibaba.dubbo.rpc.Filter;
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.Result;
+import com.alibaba.dubbo.rpc.RpcContext;
+import com.alibaba.dubbo.rpc.RpcException;
+
+/**
+ * MonitorFilter. (SPI, Singleton, ThreadSafe)
+ *
+ * @author william.liangf
+ */
+@Extension("monitor")
+public class MonitorFilter implements Filter {
+
+ private static final Logger logger = LoggerFactory.getLogger(MonitorFilter.class);
+
+ private final ConcurrentMap<String, AtomicInteger> concurrents = new ConcurrentHashMap<String, AtomicInteger>();
+
+ private MonitorFactory monitorFactory;
+
+ public void setMonitorFactory(MonitorFactory monitorFactory) {
+ this.monitorFactory = monitorFactory;
+ }
+
+ // 调用过程拦截
+ public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
+ if (invoker.getUrl().hasParameter(Constants.MONITOR_KEY)) {
+ RpcContext context = RpcContext.getContext(); // 提供方必须在invoke()之前获取context信息
+ long start = System.currentTimeMillis(); // 记录起始时间戮
+ getConcurrent(invoker, invocation).incrementAndGet(); // 并发计数
+ try {
+ Result result = invoker.invoke(invocation); // 让调用链往下执行
+ collect(invoker, invocation, context, start, false);
+ return result;
+ } catch (RpcException e) {
+ collect(invoker, invocation, context, start, true);
+ throw e;
+ } finally {
+ getConcurrent(invoker, invocation).decrementAndGet(); // 并发计数
+ }
+ } else {
+ return invoker.invoke(invocation);
+ }
+ }
+
+ // 信息采集
+ private void collect(Invoker<?> invoker, Invocation invocation, RpcContext context, long start, boolean error) {
+ try {
+ // ---- 服务信息获取 ----
+ long elapsed = System.currentTimeMillis() - start; // 计算调用耗时
+ int concurrent = getConcurrent(invoker, invocation).get(); // 当前并发数
+ String application = invoker.getUrl().getParameter(Constants.APPLICATION_KEY);
+ String service = invoker.getInterface().getName(); // 获取服务名称
+ String method = invocation.getMethodName(); // 获取方法名
+ URL url = URL.valueOf(invoker.getUrl().getParameterAndDecoded(Constants.MONITOR_KEY));
+ Monitor monitor = monitorFactory.getMonitor(url);
+ // ---- 服务提供方监控 ----
+ String server = context.getLocalAddressString(); // 本地提供方地址
+ if (invoker.getUrl().getAddress().equals(server)) {
+ monitor.count(new URL(invoker.getUrl().getProtocol(), context.getRemoteHost(), 0, service + "/" + method)
+ .addParameters(MonitorService.APPLICATION, application,
+ MonitorService.INTERFACE, service,
+ MonitorService.METHOD, method,
+ MonitorService.SERVER, NetUtils.getLocalHost() + ":" + context.getLocalPort(),
+ error ? MonitorService.FAILURE : MonitorService.SUCCESS, String.valueOf(1),
+ MonitorService.ELAPSED, String.valueOf(elapsed),
+ MonitorService.CONCURRENT, String.valueOf(concurrent)));
+ }
+ // ---- 服务消费方监控 ----
+ context = RpcContext.getContext(); // 消费方必须在invoke()之后获取context信息
+ server = context.getRemoteAddressString(); // 远程提供方地址
+ if (invoker.getUrl().getAddress().equals(server)) {
+ monitor.count(new URL(invoker.getUrl().getProtocol(), context.getRemoteHost(), context.getRemotePort(), service + "/" + method)
+ .addParameters(MonitorService.APPLICATION, application,
+ MonitorService.INTERFACE, service,
+ MonitorService.METHOD, method,
+ MonitorService.CLIENT, NetUtils.getLocalHost(),
+ error ? MonitorService.FAILURE : MonitorService.SUCCESS, String.valueOf(1),
+ MonitorService.ELAPSED, String.valueOf(elapsed),
+ MonitorService.CONCURRENT, String.valueOf(concurrent)));
+ }
+ } catch (Throwable t) {
+ logger.error("Failed to monitor count service " + invoker.getUrl() + ", cause: " + t.getMessage(), t);
+ }
+ }
+
+ // 获取并发计数器
+ private AtomicInteger getConcurrent(Invoker<?> invoker, Invocation invocation) {
+ String key = invoker.getInterface().getName() + "." + invocation.getMethodName();
+ AtomicInteger concurrent = concurrents.get(key);
+ if (concurrent == null) {
+ concurrents.putIfAbsent(key, new AtomicInteger());
+ concurrent = concurrents.get(key);
+ }
+ return concurrent;
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-monitor/src/main/java/com/alibaba/dubbo/monitor/support/SimpleMonitorService.java b/dubbo-monitor/src/main/java/com/alibaba/dubbo/monitor/support/SimpleMonitorService.java
new file mode 100644
index 0000000..7a82e20
--- /dev/null
+++ b/dubbo-monitor/src/main/java/com/alibaba/dubbo/monitor/support/SimpleMonitorService.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.monitor.support;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.logger.Logger;
+import com.alibaba.dubbo.common.logger.LoggerFactory;
+import com.alibaba.dubbo.monitor.MonitorService;
+
+/**
+ * SimpleMonitorService
+ *
+ * @author william.liangf
+ */
+public class SimpleMonitorService implements MonitorService {
+
+ private static final Logger logger = LoggerFactory.getLogger(SimpleMonitorService.class);
+
+ public void count(URL statistics) {
+ logger.info(statistics.toString());
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-monitor/src/main/resources/META-INF/services/com.alibaba.dubbo.rpc.Filter b/dubbo-monitor/src/main/resources/META-INF/services/com.alibaba.dubbo.rpc.Filter
new file mode 100644
index 0000000..de66615
--- /dev/null
+++ b/dubbo-monitor/src/main/resources/META-INF/services/com.alibaba.dubbo.rpc.Filter
@@ -0,0 +1 @@
+com.alibaba.dubbo.monitor.support.MonitorFilter
\ No newline at end of file
diff --git a/dubbo-monitor/src/test/java/com/alibaba/dubbo/monitor/support/MonitorFilterTest.java b/dubbo-monitor/src/test/java/com/alibaba/dubbo/monitor/support/MonitorFilterTest.java
new file mode 100644
index 0000000..fe3ee02
--- /dev/null
+++ b/dubbo-monitor/src/test/java/com/alibaba/dubbo/monitor/support/MonitorFilterTest.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.monitor.support;
+
+import java.io.UnsupportedEncodingException;
+import java.net.URLEncoder;
+
+import junit.framework.Assert;
+
+import org.junit.Test;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.utils.NetUtils;
+import com.alibaba.dubbo.monitor.Monitor;
+import com.alibaba.dubbo.monitor.MonitorFactory;
+import com.alibaba.dubbo.monitor.MonitorService;
+import com.alibaba.dubbo.monitor.support.MonitorFilter;
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.Result;
+import com.alibaba.dubbo.rpc.RpcContext;
+import com.alibaba.dubbo.rpc.RpcException;
+import com.alibaba.dubbo.rpc.RpcInvocation;
+
+/**
+ * MonitorFilterTest
+ *
+ * @author william.liangf
+ */
+public class MonitorFilterTest {
+
+ private volatile URL lastStatistics;
+
+ private volatile Invocation lastInvocation;
+
+ private final Invoker<MonitorService> serviceInvoker = new Invoker<MonitorService>() {
+ public Class<MonitorService> getInterface() {
+ return MonitorService.class;
+ }
+ public URL getUrl() {
+ try {
+ return URL.valueOf("dubbo://" + NetUtils.getLocalHost() + ":20880?" + Constants.APPLICATION_KEY + "=abc&" + Constants.MONITOR_KEY + "=" + URLEncoder.encode("dubbo://" + NetUtils.getLocalHost() + ":7070", "UTF-8"));
+ } catch (UnsupportedEncodingException e) {
+ throw new IllegalStateException(e.getMessage(), e);
+ }
+ }
+ public boolean isAvailable() {
+ return false;
+ }
+ public Result invoke(Invocation invocation) throws RpcException {
+ lastInvocation = invocation;
+ return null;
+ }
+ public void destroy() {
+ }
+ };
+
+ private MonitorFactory monitorFactory = new MonitorFactory() {
+ public Monitor getMonitor(final URL url) {
+ return new Monitor() {
+ public URL getUrl() {
+ return url;
+ }
+ public boolean isAvailable() {
+ return true;
+ }
+ public void destroy() {
+ }
+ public void count(URL statistics) {
+ MonitorFilterTest.this.lastStatistics = statistics;
+ }
+ };
+ }
+ };
+
+ @Test
+ public void testFilter() throws Exception {
+ MonitorFilter monitorFilter = new MonitorFilter();
+ monitorFilter.setMonitorFactory(monitorFactory);
+ Invocation invocation = new RpcInvocation("aaa", new Class<?>[0], new Object[0]);
+ RpcContext.getContext().setRemoteAddress(NetUtils.getLocalHost(), 20880).setLocalAddress(NetUtils.getLocalHost(), 2345);
+ monitorFilter.invoke(serviceInvoker, invocation);
+ while (lastStatistics == null) {
+ Thread.sleep(10);
+ }
+ Assert.assertEquals("abc", lastStatistics.getParameter(MonitorService.APPLICATION));
+ Assert.assertEquals(MonitorService.class.getName(), lastStatistics.getParameter(MonitorService.INTERFACE));
+ Assert.assertEquals("aaa", lastStatistics.getParameter(MonitorService.METHOD));
+ Assert.assertEquals(NetUtils.getLocalHost() + ":20880", lastStatistics.getAddress());
+ Assert.assertEquals(NetUtils.getLocalHost(), lastStatistics.getParameter(MonitorService.CLIENT));
+ Assert.assertEquals(null, lastStatistics.getParameter(MonitorService.SERVER));
+ Assert.assertEquals(1, lastStatistics.getParameter(MonitorService.SUCCESS, 0));
+ Assert.assertEquals(0, lastStatistics.getParameter(MonitorService.FAILURE, 0));
+ Assert.assertEquals(1, lastStatistics.getParameter(MonitorService.CONCURRENT, 0));
+ Assert.assertEquals(invocation, lastInvocation);
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-registry-multicast/pom.xml b/dubbo-registry-multicast/pom.xml
new file mode 100644
index 0000000..557d189
--- /dev/null
+++ b/dubbo-registry-multicast/pom.xml
@@ -0,0 +1,38 @@
+<!--
+ - Copyright 1999-2011 Alibaba Group.
+ -
+ - Licensed under the Apache License, Version 2.0 (the "License");
+ - you may not use this file except in compliance with the License.
+ - You may obtain a copy of the License at
+ -
+ - http://www.apache.org/licenses/LICENSE-2.0
+ -
+ - Unless required by applicable law or agreed to in writing, software
+ - distributed under the License is distributed on an "AS IS" BASIS,
+ - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ - See the License for the specific language governing permissions and
+ - limitations under the License.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>com.alibaba</groupId>
+ <artifactId>dubbo-parent</artifactId>
+ <version>2.0.8</version>
+ </parent>
+ <artifactId>dubbo-registry-multicast</artifactId>
+ <packaging>jar</packaging>
+ <name>Dubbo Multicast Registry Module</name>
+ <description>The multicast registry module of dubbo project</description>
+ <properties>
+ <skip_maven_deploy>false</skip_maven_deploy>
+ </properties>
+ <dependencies>
+ <dependency>
+ <groupId>com.alibaba</groupId>
+ <artifactId>dubbo-registry</artifactId>
+ <version>${project.parent.version}</version>
+ </dependency>
+ </dependencies>
+</project>
\ No newline at end of file
diff --git a/dubbo-registry-multicast/src/main/java/com/alibaba/dubbo/registry/multicast/MulticastRegistry.java b/dubbo-registry-multicast/src/main/java/com/alibaba/dubbo/registry/multicast/MulticastRegistry.java
new file mode 100644
index 0000000..6beba96
--- /dev/null
+++ b/dubbo-registry-multicast/src/main/java/com/alibaba/dubbo/registry/multicast/MulticastRegistry.java
@@ -0,0 +1,193 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.registry.multicast;
+
+import java.io.IOException;
+import java.net.DatagramPacket;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.MulticastSocket;
+import java.util.Arrays;
+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.common.utils.StringUtils;
+import com.alibaba.dubbo.registry.NotifyListener;
+import com.alibaba.dubbo.registry.support.CacheRegistry;
+
+/**
+ * MulticastRegistry
+ *
+ * @author william.liangf
+ */
+public class MulticastRegistry extends CacheRegistry {
+
+ // 日志输出
+ private static final Logger logger = LoggerFactory.getLogger(MulticastRegistry.class);
+
+ private static final String REGISTER = "register";
+
+ private static final String UNREGISTER = "unregister";
+
+ private static final String SUBSCRIBE = "subscribe";
+
+ private static final String UNSUBSCRIBE = "unsubscribe";
+
+ private final InetAddress mutilcastAddress;
+
+ private final MulticastSocket mutilcastSocket;
+
+ 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());
+ 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 (true) {
+ 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();
+ }
+ if (logger.isInfoEnabled()) {
+ logger.info("Receive multicast message: " + msg + " from " + recv.getSocketAddress());
+ }
+ MulticastRegistry.this.receive(msg, (InetSocketAddress) recv.getSocketAddress());
+ Arrays.fill(buf, (byte)0);
+ } catch (IOException e) {
+ logger.error(e.getMessage(), e);
+ }
+ }
+ }
+ }, "MulticastRegistryReceiver");
+ thread.setDaemon(true);
+ thread.start();
+ } catch (IOException e) {
+ throw new IllegalStateException(e.getMessage(), e);
+ }
+ }
+
+ private static boolean isMulticastAddress(String ip) {
+ int i = ip.indexOf('.');
+ if (i > 0) {
+ String prefix = ip.substring(0, i);
+ if (StringUtils.isInteger(prefix)) {
+ int p = Integer.parseInt(prefix);
+ return p >= 224 && p <= 239;
+ }
+ }
+ return false;
+ }
+
+ private void receive(String msg, InetSocketAddress remoteAddress) {
+ if (msg.startsWith(REGISTER)) {
+ URL url = URL.valueOf(msg.substring(REGISTER.length()).trim());
+ registered(url);
+ } else if (msg.startsWith(UNREGISTER)) {
+ URL url = URL.valueOf(msg.substring(UNREGISTER.length()).trim());
+ unregistered(url);
+ } else if (msg.startsWith(SUBSCRIBE)) {
+ URL url = URL.valueOf(msg.substring(SUBSCRIBE.length()).trim());
+ List<URL> urls = lookup(url);
+ if (urls != null && urls.size() > 0) {
+ for (URL u : urls) {
+ unicast(REGISTER + " " + u.toFullString(), url.getHost());
+ }
+ }
+ } else if (msg.startsWith(UNSUBSCRIBE)) {
+ }
+ }
+
+ private void broadcast(String msg) {
+ if (logger.isInfoEnabled()) {
+ logger.info("Send broadcast message: " + msg + " to " + mutilcastAddress + ":" + mutilcastSocket.getLocalPort());
+ }
+ try {
+ byte[] data = (msg + "\n").getBytes();
+ DatagramPacket hi = new DatagramPacket(data, data.length, mutilcastAddress, mutilcastSocket.getLocalPort());
+ mutilcastSocket.send(hi);
+ } catch (Exception e) {
+ throw new IllegalStateException(e.getMessage(), e);
+ }
+ }
+
+ private void unicast(String msg, String host) {
+ if (logger.isInfoEnabled()) {
+ logger.info("Send unicast message: " + msg + " to " + host + ":" + mutilcastSocket.getLocalPort());
+ }
+ try {
+ byte[] data = (msg + "\n").getBytes();
+ DatagramPacket hi = new DatagramPacket(data, data.length, InetAddress.getByName(host), mutilcastSocket.getLocalPort());
+ mutilcastSocket.send(hi);
+ } catch (Exception e) {
+ throw new IllegalStateException(e.getMessage(), e);
+ }
+ }
+
+ protected void doRegister(URL url) {
+ broadcast(REGISTER + " " + url.toFullString());
+ }
+
+ protected void doUnregister(URL url) {
+ broadcast(UNREGISTER + " " + url.toFullString());
+ }
+
+ protected void doSubscribe(URL url, NotifyListener listener) {
+ url = url.setProtocol("multicast").setHost(mutilcastAddress.getHostAddress()).setPort(mutilcastSocket.getLocalPort());
+ broadcast(SUBSCRIBE + " " + url.toFullString());
+ synchronized (listener) {
+ try {
+ listener.wait(5000);
+ } catch (InterruptedException e) {
+ }
+ }
+ }
+
+ protected void doUnsubscribe(URL url, NotifyListener listener) {
+ url = url.setProtocol("multicast").setHost(mutilcastAddress.getHostAddress()).setPort(mutilcastSocket.getLocalPort());
+ broadcast(UNSUBSCRIBE + " " + url.toFullString());
+ }
+
+ public boolean isAvailable() {
+ try {
+ return mutilcastSocket.isConnected();
+ } catch (Throwable t) {
+ return false;
+ }
+ }
+
+ public void destroy() {
+ super.destroy();
+ try {
+ mutilcastSocket.close();
+ } catch (Throwable t) {
+ logger.warn(t.getMessage(), t);
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-registry-multicast/src/main/java/com/alibaba/dubbo/registry/multicast/MulticastRegistryFactory.java b/dubbo-registry-multicast/src/main/java/com/alibaba/dubbo/registry/multicast/MulticastRegistryFactory.java
new file mode 100644
index 0000000..eb065e2
--- /dev/null
+++ b/dubbo-registry-multicast/src/main/java/com/alibaba/dubbo/registry/multicast/MulticastRegistryFactory.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.registry.multicast;
+
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.registry.Registry;
+import com.alibaba.dubbo.registry.support.AbstractRegistryFactory;
+
+/**
+ * MulticastRegistryLocator
+ *
+ * @author william.liangf
+ */
+@Extension("multicast")
+public class MulticastRegistryFactory extends AbstractRegistryFactory {
+
+ public Registry createRegistry(URL url) {
+ return new MulticastRegistry(url);
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-registry-multicast/src/main/resources/META-INF/services/com.alibaba.dubbo.registry.RegistryFactory b/dubbo-registry-multicast/src/main/resources/META-INF/services/com.alibaba.dubbo.registry.RegistryFactory
new file mode 100644
index 0000000..e37727e
--- /dev/null
+++ b/dubbo-registry-multicast/src/main/resources/META-INF/services/com.alibaba.dubbo.registry.RegistryFactory
@@ -0,0 +1 @@
+com.alibaba.dubbo.registry.multicast.MulticastRegistryFactory
\ No newline at end of file
diff --git a/dubbo-registry-multicast/src/test/java/com/alibaba/dubbo/registry/multicast/MulticastRegistryTest.java b/dubbo-registry-multicast/src/test/java/com/alibaba/dubbo/registry/multicast/MulticastRegistryTest.java
new file mode 100644
index 0000000..3bd8cbe
--- /dev/null
+++ b/dubbo-registry-multicast/src/test/java/com/alibaba/dubbo/registry/multicast/MulticastRegistryTest.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.registry.multicast;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.utils.NetUtils;
+import com.alibaba.dubbo.registry.NotifyListener;
+
+/**
+ * MulticastRegistryTest
+ *
+ * @author tony.chenl
+ */
+public class MulticastRegistryTest {
+
+ String service = "com.alibaba.dubbo.test.injvmServie";
+ URL registryUrl = URL.valueOf("multicast://239.255.255.255/");
+ URL serviceUrl = URL.valueOf("dubbo://" + NetUtils.getLocalHost() + "/" + service
+ + "?methods=test1,test2");
+ URL consumerUrl = URL.valueOf("subscribe://" + NetUtils.getLocalHost() + "/" + service + "?arg1=1&arg2=2");
+ MulticastRegistry registry = new MulticastRegistry(registryUrl);
+
+ /**
+ * @throws java.lang.Exception
+ */
+ @BeforeClass
+ public static void setUpBeforeClass() throws Exception {
+ }
+
+ /**
+ * @throws java.lang.Exception
+ */
+ @Before
+ public void setUp() throws Exception {
+ registry.register(serviceUrl);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testUrlerror() {
+ URL errorUrl = URL.valueOf("multicast://mullticast/");
+ new MulticastRegistry(errorUrl);
+ }
+
+ /**
+ * Test method for {@link com.alibaba.dubbo.registry.support.injvm.InjvmRegistry#register(java.util.Map)}.
+ */
+ @Test
+ public void testRegister() {
+ Set<String> registered = null;
+ // clear first
+ registered = registry.getRegistered();
+
+ for (int i = 0; i < 2; i++) {
+ registry.register(serviceUrl);
+ registered = registry.getRegistered();
+ assertTrue(registered.contains(serviceUrl.toFullString()));
+ }
+ // confirm only 1 regist success;
+ registered = registry.getRegistered();
+ assertEquals(1, registered.size());
+ }
+
+ /**
+ * Test method for
+ * {@link com.alibaba.dubbo.registry.support.injvm.InjvmRegistry#subscribe(java.util.Map, com.alibaba.dubbo.registry.support.NotifyListener)}
+ * .
+ */
+ @Test
+ public void testSubscribe() {
+ // verify lisener.
+ final AtomicReference<URL> args = new AtomicReference<URL>();
+ registry.subscribe(consumerUrl, new NotifyListener() {
+
+ public void notify(List<URL> urls) {
+ // FIXME assertEquals(MulticastRegistry.this.service, service);
+ args.set(urls.get(0));
+ }
+ });
+ assertEquals(serviceUrl.toFullString(), args.get().toFullString());
+ Map<String, Set<NotifyListener>> arg = registry.getSubscribed();
+ assertEquals(consumerUrl.toFullString(), arg.keySet().iterator().next());
+
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-registry-simple/pom.xml b/dubbo-registry-simple/pom.xml
new file mode 100644
index 0000000..d8b4a08
--- /dev/null
+++ b/dubbo-registry-simple/pom.xml
@@ -0,0 +1,56 @@
+<!--
+ - Copyright 1999-2011 Alibaba Group.
+ -
+ - Licensed under the Apache License, Version 2.0 (the "License");
+ - you may not use this file except in compliance with the License.
+ - You may obtain a copy of the License at
+ -
+ - http://www.apache.org/licenses/LICENSE-2.0
+ -
+ - Unless required by applicable law or agreed to in writing, software
+ - distributed under the License is distributed on an "AS IS" BASIS,
+ - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ - See the License for the specific language governing permissions and
+ - limitations under the License.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>com.alibaba</groupId>
+ <artifactId>dubbo-parent</artifactId>
+ <version>2.0.8</version>
+ </parent>
+ <artifactId>dubbo-registry-simple</artifactId>
+ <packaging>jar</packaging>
+ <name>Dubbo Default Registry Module</name>
+ <description>The default registry module of dubbo project</description>
+ <properties>
+ <skip_maven_deploy>false</skip_maven_deploy>
+ </properties>
+ <dependencies>
+ <dependency>
+ <groupId>com.alibaba</groupId>
+ <artifactId>dubbo-registry</artifactId>
+ <version>${project.parent.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>com.alibaba</groupId>
+ <artifactId>dubbo-rpc-default</artifactId>
+ <version>${project.parent.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>com.alibaba</groupId>
+ <artifactId>dubbo-remoting-netty</artifactId>
+ <version>${project.parent.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.bsf</groupId>
+ <artifactId>bsf-api</artifactId>
+ <scope>test</scope>
+ <optional>true</optional>
+ </dependency>
+ </dependencies>
+</project>
\ No newline at end of file
diff --git a/dubbo-registry-simple/src/main/java/com/alibaba/dubbo/registry/dubbo/DubboRegistry.java b/dubbo-registry-simple/src/main/java/com/alibaba/dubbo/registry/dubbo/DubboRegistry.java
new file mode 100644
index 0000000..24db831
--- /dev/null
+++ b/dubbo-registry-simple/src/main/java/com/alibaba/dubbo/registry/dubbo/DubboRegistry.java
@@ -0,0 +1,211 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
+import 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.ConcurrentHashSet;
+import com.alibaba.dubbo.common.utils.NamedThreadFactory;
+import com.alibaba.dubbo.common.utils.NetUtils;
+import com.alibaba.dubbo.registry.NotifyListener;
+import com.alibaba.dubbo.registry.RegistryService;
+import com.alibaba.dubbo.registry.support.FailbackRegistry;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.RpcConstants;
+
+/**
+ * DubboRegistry
+ *
+ * @author william.liangf
+ */
+public class DubboRegistry extends FailbackRegistry {
+
+ private final static Logger logger = LoggerFactory.getLogger(DubboRegistry.class);
+
+ // 重连检测周期3秒(单位毫秒)
+ private static final int RECONNECT_PERIOD_DEFAULT = 3 * 1000;
+
+ // 定时任务执行器
+ private final ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(1, new NamedThreadFactory("DubboRegistryReconnectTimer", true));
+
+ // 重连定时器,定时检查连接是否可用,不可用时,无限次重连
+ private final ScheduledFuture<?> reconnectFuture;
+
+ // 客户端获取过程锁,锁定客户端实例的创建过程,防止重复的客户端
+ private final ReentrantLock clientLock = new ReentrantLock();
+
+ private final Set<String> registered = new ConcurrentHashSet<String>();
+
+ private final ConcurrentMap<String, NotifyListener> subscribed = new ConcurrentHashMap<String, NotifyListener>();
+
+ private final Invoker<RegistryService> registryInvoker;
+
+ private final RegistryService registryService;
+
+ public DubboRegistry(Invoker<RegistryService> registryInvoker, RegistryService registryService) {
+ super(registryInvoker.getUrl());
+ this.registryInvoker = registryInvoker;
+ this.registryService = registryService;
+ // 启动重连定时器
+ int reconnectPeriod = registryInvoker.getUrl().getParameter(RpcConstants.REGISTRY_RECONNECT_PERIOD_KEY, RECONNECT_PERIOD_DEFAULT);
+ reconnectFuture = scheduledExecutorService.scheduleWithFixedDelay(new Runnable() {
+ public void run() {
+ // 检测并连接注册中心
+ try {
+ connect();
+ } catch (Throwable t) { // 防御性容错
+ logger.error("Unexpected error occur at reconnect, cause: " + t.getMessage(), t);
+ }
+ }
+ }, reconnectPeriod, reconnectPeriod, TimeUnit.MILLISECONDS);
+ }
+
+ protected final void connect() {
+ try {
+ // 检查是否已连接
+ if (isAvailable()) {
+ return;
+ }
+ if (logger.isInfoEnabled()) {
+ logger.info("Reconnect to registry " + getUrl());
+ }
+ clientLock.lock();
+ try {
+ // 双重检查是否已连接
+ if (isAvailable()) {
+ return;
+ }
+ recover();
+ } finally {
+ clientLock.unlock();
+ }
+ } catch (Throwable t) { // 忽略所有异常,等待下次重试
+ if (getUrl().getParameter(Constants.CHECK_KEY, true)) {
+ if (t instanceof RuntimeException) {
+ throw (RuntimeException) t;
+ }
+ throw new RuntimeException(t.getMessage(), t);
+ }
+ logger.error("Failed to connect to registry " + getUrl().getAddress() + " from provider/consumer " + NetUtils.getLocalHost() + " use dubbo " + Version.getVersion() + ", cause: " + t.getMessage(), t);
+ }
+ }
+
+ protected final void recover() throws Exception {
+ // register
+ Set<String> recoverRegistered = new HashSet<String>(registered);
+ if (! recoverRegistered.isEmpty()) {
+ if (logger.isInfoEnabled()) {
+ logger.info("Recover register services " + recoverRegistered);
+ }
+ for (String url : recoverRegistered) {
+ register(URL.valueOf(url));
+ }
+ }
+ // subscribe
+ Map<String, NotifyListener> recoverSubscribed = new HashMap<String, NotifyListener>(subscribed);
+ if (recoverSubscribed.size() > 0) {
+ if (logger.isInfoEnabled()) {
+ logger.info("Recover subscribe services " + recoverSubscribed);
+ }
+ for (Map.Entry<String, NotifyListener> entry : recoverSubscribed.entrySet()) {
+ String url = entry.getKey();
+ subscribe(URL.valueOf(url), entry.getValue());
+ }
+ }
+ }
+
+ 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();
+ }
+
+ public List<URL> lookup(URL url) {
+ if (url == null) {
+ throw new IllegalArgumentException("lookup url == null");
+ }
+ if (logger.isInfoEnabled()){
+ logger.info("Lookup: " + url);
+ }
+ return registryService.lookup(url);
+ }
+
+ public void register(URL url) {
+ registered.add(url.toFullString());
+ super.register(url);
+ }
+
+ protected void doRegister(URL url) {
+ registryService.register(url);
+ }
+
+ public void unregister(URL url) {
+ registered.remove(url.toFullString());
+ super.unregister(url);
+ }
+
+ protected void doUnregister(URL url) {
+ registryService.unregister(url);
+ }
+
+ public void subscribe(URL url, NotifyListener listener) {
+ subscribed.put(url.toFullString(), listener);
+ super.subscribe(url, listener);
+ }
+
+ protected void doSubscribe(URL url, NotifyListener listener) {
+ registryService.subscribe(url, listener);
+ }
+
+ public void unsubscribe(URL url, NotifyListener listener) {
+ subscribed.remove(url.toFullString());
+ super.unsubscribe(url, listener);
+ }
+
+ protected void doUnsubscribe(URL url, NotifyListener listener) {
+ registryService.unsubscribe(url, listener);
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-registry-simple/src/main/java/com/alibaba/dubbo/registry/dubbo/DubboRegistryFactory.java b/dubbo-registry-simple/src/main/java/com/alibaba/dubbo/registry/dubbo/DubboRegistryFactory.java
new file mode 100644
index 0000000..e7aa22a
--- /dev/null
+++ b/dubbo-registry-simple/src/main/java/com/alibaba/dubbo/registry/dubbo/DubboRegistryFactory.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.dubbo;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.bytecode.Wrapper;
+import com.alibaba.dubbo.common.utils.StringUtils;
+import com.alibaba.dubbo.registry.Registry;
+import com.alibaba.dubbo.registry.RegistryService;
+import com.alibaba.dubbo.registry.support.AbstractRegistryFactory;
+import com.alibaba.dubbo.registry.support.RegistryDirectory;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.Protocol;
+import com.alibaba.dubbo.rpc.ProxyFactory;
+import com.alibaba.dubbo.rpc.RpcConstants;
+import com.alibaba.dubbo.rpc.cluster.Cluster;
+
+/**
+ * DubboRegistryFactory
+ *
+ * @author william.liangf
+ */
+@Extension("dubbo")
+public class DubboRegistryFactory extends AbstractRegistryFactory {
+
+ private Protocol protocol;
+
+ public void setProtocol(Protocol protocol) {
+ this.protocol = protocol;
+ }
+
+ private ProxyFactory proxyFactory;
+
+ public void setProxyFactory(ProxyFactory proxyFactory) {
+ this.proxyFactory = proxyFactory;
+ }
+
+ private Cluster cluster;
+
+ public void setCluster(Cluster cluster) {
+ this.cluster = cluster;
+ }
+
+ public Registry createRegistry(URL url) {
+ url = getRegistryURL(url);
+ List<URL> urls = new ArrayList<URL>();
+ urls.add(url.removeParameter(Constants.BACKUP_KEY));
+ String backup = url.getParameter(Constants.BACKUP_KEY);
+ if (backup != null && backup.length() > 0) {
+ String[] addresses = Constants.COMMA_SPLIT_PATTERN.split(backup);
+ for (String address : addresses) {
+ urls.add(url.setAddress(address));
+ }
+ }
+ RegistryDirectory<RegistryService> directory = new RegistryDirectory<RegistryService>(RegistryService.class, url.addParameter(Constants.INTERFACE_KEY, RegistryService.class.getName()).addParameterAndEncoded(RpcConstants.REFER_KEY, url.toParameterString()));
+ Invoker<RegistryService> registryInvoker = cluster.merge(directory);
+ RegistryService registryService = proxyFactory.getProxy(registryInvoker);
+ DubboRegistry registry = new DubboRegistry(registryInvoker, registryService);
+ directory.setRegistry(registry);
+ directory.setProtocol(protocol);
+ directory.notify(urls);
+ registryService.subscribe(url, directory);
+ return registry;
+ }
+
+ private static URL getRegistryURL(URL url) {
+ return url.setPath(RegistryService.class.getName())
+ .removeParameter(RpcConstants.EXPORT_KEY).removeParameter(RpcConstants.REFER_KEY)
+ .addParameter(Constants.INTERFACE_KEY, RegistryService.class.getName())
+ .addParameter(RpcConstants.CLUSTER_STICKY_KEY, "true")
+ .addParameter(RpcConstants.LAZY_CONNECT_KEY, "true")
+ .addParameter(Constants.RECONNECT_KEY, "false")
+ .addParameterIfAbsent(Constants.TIMEOUT_KEY, "10000")
+ .addParameterIfAbsent(RpcConstants.CALLBACK_INSTANCES_LIMIT_KEY, "10000")
+ .addParameterIfAbsent(Constants.CONNECT_TIMEOUT_KEY, "10000")
+ .addParameter(Constants.METHODS_KEY, StringUtils.join(new HashSet<String>(Arrays.asList(Wrapper.getWrapper(RegistryService.class).getDeclaredMethodNames())), ","))
+ //.addParameter(Constants.STUB_KEY, RegistryServiceStub.class.getName())
+ //.addParameter(RpcConstants.STUB_EVENT_KEY, Boolean.TRUE.toString()) //for event dispatch
+ //.addParameter(RpcConstants.ON_DISCONNECT_KEY, "disconnect")
+ .addParameter("subscribe.1.callback", "true")
+ .addParameter("unsubscribe.1.callback", "false");
+ }
+}
\ No newline at end of file
diff --git a/dubbo-registry-simple/src/main/resources/META-INF/services/com.alibaba.dubbo.registry.RegistryFactory b/dubbo-registry-simple/src/main/resources/META-INF/services/com.alibaba.dubbo.registry.RegistryFactory
new file mode 100644
index 0000000..403c36f
--- /dev/null
+++ b/dubbo-registry-simple/src/main/resources/META-INF/services/com.alibaba.dubbo.registry.RegistryFactory
@@ -0,0 +1 @@
+com.alibaba.dubbo.registry.dubbo.DubboRegistryFactory
\ No newline at end of file
diff --git a/dubbo-registry-simple/src/test/java/com/alibaba/dubbo/registry/dubbo/DemoService.java b/dubbo-registry-simple/src/test/java/com/alibaba/dubbo/registry/dubbo/DemoService.java
new file mode 100644
index 0000000..eda1c74
--- /dev/null
+++ b/dubbo-registry-simple/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-simple/src/test/java/com/alibaba/dubbo/registry/dubbo/MockChannel.java b/dubbo-registry-simple/src/test/java/com/alibaba/dubbo/registry/dubbo/MockChannel.java
new file mode 100644
index 0000000..19e1b5b
--- /dev/null
+++ b/dubbo-registry-simple/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-simple/src/test/java/com/alibaba/dubbo/registry/dubbo/MockedClient.java b/dubbo-registry-simple/src/test/java/com/alibaba/dubbo/registry/dubbo/MockedClient.java
new file mode 100644
index 0000000..6257d06
--- /dev/null
+++ b/dubbo-registry-simple/src/test/java/com/alibaba/dubbo/registry/dubbo/MockedClient.java
@@ -0,0 +1,263 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.registry.dubbo;
+
+import java.net.InetSocketAddress;
+import java.util.Map;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.remoting.Channel;
+import com.alibaba.dubbo.remoting.ChannelHandler;
+import com.alibaba.dubbo.remoting.Codec;
+import com.alibaba.dubbo.remoting.RemotingException;
+import com.alibaba.dubbo.remoting.exchange.ExchangeClient;
+import com.alibaba.dubbo.remoting.exchange.ExchangeHandler;
+import com.alibaba.dubbo.remoting.exchange.ResponseCallback;
+import com.alibaba.dubbo.remoting.exchange.ResponseFuture;
+import com.alibaba.dubbo.remoting.exchange.support.Replier;
+
+/**
+ * MockedClient
+ *
+ * @author william.liangf
+ */
+public class MockedClient implements ExchangeClient {
+
+ //private String host;
+
+ //private int port;
+
+ private boolean connected;
+
+ private Object received;
+
+ private Object sent;
+
+ private Object invoked;
+
+ private Replier<?> handler;
+
+ private InetSocketAddress address;
+
+ private boolean closed = false;
+
+ //private ChannelListener listener;
+
+ public MockedClient(String host, int port, boolean connected) {
+ this(host, port, connected, null);
+ }
+
+ public MockedClient(String host, int port, boolean connected, Object received) {
+ this.address = new InetSocketAddress(host, port);
+ this.connected = connected;
+ this.received = received;
+ }
+
+ public void open() {
+ }
+
+ public void close() {
+ this.closed = true;
+ }
+
+ public void send(Object msg) throws RemotingException {
+ this.sent = msg;
+ }
+
+ public ResponseFuture request(Object msg) throws RemotingException {
+ return request(msg, 0);
+ }
+
+ public ResponseFuture request(Object msg, int timeout) throws RemotingException {
+ this.invoked = msg;
+ return new ResponseFuture() {
+ public Object get() throws RemotingException {
+ return received;
+ }
+ public Object get(int timeoutInMillis) throws RemotingException {
+ return received;
+ }
+ public boolean isDone() {
+ return true;
+ }
+ public void setCallback(ResponseCallback callback) {
+ }
+ };
+ }
+
+ public void registerHandler(Replier<?> handler) {
+ this.handler = handler;
+ }
+
+ public void unregisterHandler(Replier<?> handler) {
+ //this.handler = null;
+ }
+
+ public void addChannelListener(ChannelHandler listener) {
+ //this.listener = listener;
+ }
+
+ public void removeChannelListener(ChannelHandler listener) {
+ //this.listener = null;
+ }
+
+ public boolean isConnected() {
+ return connected;
+ }
+
+ public Object getSent() {
+ return sent;
+ }
+
+ public Replier<?> getHandler() {
+ return handler;
+ }
+
+ public Object getInvoked() {
+ return invoked;
+ }
+
+ public InetSocketAddress getRemoteAddress() {
+ return address;
+ }
+
+ public String getName() {
+ return "mocked";
+ }
+
+ public InetSocketAddress getLocalAddress() {
+ return null;
+ }
+
+ public void setTimeout(int timeout) {
+ }
+
+ public int getTimeout() {
+ return 0;
+ }
+
+ public void close(int timeout) {
+ }
+
+ public boolean isOpen() {
+ return false;
+ }
+
+ public Codec getCodec() {
+ return null;
+ }
+
+ public void setCodec(Codec codec) {
+ }
+
+ public void setHost(String host) {
+ }
+
+ public String getHost() {
+ return null;
+ }
+
+ public void setPort(int port) {
+ }
+
+ public int getPort() {
+ return 0;
+ }
+
+ public void setThreadCount(int threadCount) {
+ }
+
+ public int getThreadCount() {
+ return 0;
+ }
+
+ public URL getUrl() {
+ return null;
+ }
+
+ public Replier<?> getReceiver() {
+ return null;
+ }
+
+ public ChannelHandler getChannelHandler() {
+ return null;
+ }
+
+ public void reset(Map<String, String> parameters) {
+ }
+
+ public Channel getChannel() {
+ return this;
+ }
+
+ public ExchangeHandler getExchangeHandler() {
+ return null;
+ }
+
+ public void reconnect() throws RemotingException {
+ }
+
+ public Object getAttribute(String key) {
+ return null;
+ }
+
+ public void setAttribute(String key, Object value) {
+
+ }
+
+ public boolean hasAttribute(String key) {
+ return false;
+ }
+
+ public boolean isClosed() {
+ return closed;
+ }
+
+ public void removeAttribute(String key) {
+
+ }
+ /**
+ * @return the received
+ */
+ public Object getReceived() {
+ return received;
+ }
+
+ /**
+ * @param received the received to set
+ */
+ public void setReceived(Object received) {
+ this.received = received;
+ }
+
+ /**
+ * @param connected the connected to set
+ */
+ public void setConnected(boolean connected) {
+ this.connected = connected;
+ }
+
+ public void send(Object message, boolean sent) throws RemotingException {
+ }
+
+ public void reset(URL url) {
+ }
+
+ @Deprecated
+ public void reset(com.alibaba.dubbo.common.Parameters parameters) {
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-registry-simple/src/test/java/com/alibaba/dubbo/registry/dubbo/RegistryDirectoryTest.java b/dubbo-registry-simple/src/test/java/com/alibaba/dubbo/registry/dubbo/RegistryDirectoryTest.java
new file mode 100644
index 0000000..baa5967
--- /dev/null
+++ b/dubbo-registry-simple/src/test/java/com/alibaba/dubbo/registry/dubbo/RegistryDirectoryTest.java
@@ -0,0 +1,606 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.registry.dubbo;
+
+import static org.junit.Assert.fail;
+
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.CountDownLatch;
+
+import javax.script.ScriptEngineManager;
+
+import junit.framework.Assert;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.ExtensionLoader;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.registry.NotifyListener;
+import com.alibaba.dubbo.registry.Registry;
+import com.alibaba.dubbo.registry.RegistryFactory;
+import com.alibaba.dubbo.registry.support.RegistryDirectory;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.Protocol;
+import com.alibaba.dubbo.rpc.RpcConstants;
+import com.alibaba.dubbo.rpc.RpcException;
+import com.alibaba.dubbo.rpc.RpcInvocation;
+import com.alibaba.dubbo.rpc.cluster.Router;
+import com.alibaba.dubbo.rpc.cluster.loadbalance.LeastActiveLoadBalance;
+import com.alibaba.dubbo.rpc.cluster.loadbalance.RoundRobinLoadBalance;
+import com.alibaba.dubbo.rpc.cluster.router.ScriptRouter;
+import com.alibaba.dubbo.rpc.cluster.router.ScriptRouterFactory;
+
+@SuppressWarnings({ "rawtypes", "unchecked" })
+public class RegistryDirectoryTest {
+
+ RegistryFactory registryFactory = ExtensionLoader.getExtensionLoader(RegistryFactory.class).getAdaptiveExtension();
+ Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
+ String service = DemoService.class.getName();
+ RpcInvocation invocation = new RpcInvocation();
+
+ URL noMeaningUrl = URL.valueOf("notsupport:/" + service+"?interface="+service);
+ URL SERVICEURL = URL.valueOf("dubbo://127.0.0.1:9091/" + service + "?lazy=true");
+ URL SERVICEURL2 = URL.valueOf("dubbo://127.0.0.1:9092/" + service + "?lazy=true");
+ URL SERVICEURL3 = URL.valueOf("dubbo://127.0.0.1:9093/" + service + "?lazy=true");
+ URL SERVICEURL_DUBBO_NOPATH = URL.valueOf("dubbo://127.0.0.1:9092" + "?lazy=true");
+
+ @Before
+ public void setUp() {
+ }
+
+ private RegistryDirectory getRegistryDirectory(URL url) {
+ RegistryDirectory registryDirectory = new RegistryDirectory(URL.class, url);
+ registryDirectory.setProtocol(protocol);
+ // asert empty
+ List invokers = registryDirectory.list(invocation);
+ Assert.assertEquals(0, invokers.size());
+ Assert.assertEquals(false, registryDirectory.isAvailable());
+ return registryDirectory;
+ }
+
+ private RegistryDirectory getRegistryDirectory() {
+ return getRegistryDirectory(noMeaningUrl);
+ }
+
+ @Test
+ public void test_Constructor_WithErrorParam() {
+ try {
+ new RegistryDirectory(null, null);
+ fail();
+ } catch (IllegalArgumentException e) {
+
+ }
+ try {
+ // null url
+ new RegistryDirectory(null, noMeaningUrl);
+ fail();
+ } catch (IllegalArgumentException e) {
+
+ }
+ try {
+ // no servicekey
+ new RegistryDirectory(RegistryDirectoryTest.class, URL.valueOf("dubbo://10.20.30.40:9090"));
+ fail();
+ } catch (IllegalArgumentException e) {
+
+ }
+ }
+
+ @Test
+ public void test_Constructor_CheckStatus() throws Exception {
+ URL url = URL.valueOf("notsupported://10.20.30.40/" + service + "?a=b").addParameterAndEncoded(RpcConstants.REFER_KEY,
+ "foo=bar");
+ RegistryDirectory reg = getRegistryDirectory(url);
+ Field field = reg.getClass().getDeclaredField("queryMap");
+ field.setAccessible(true);
+ Map<String, String> queryMap = (Map<String, String>) field.get(reg);
+ Assert.assertEquals("bar", queryMap.get("foo"));
+ Assert.assertEquals(url.removeParameter(RpcConstants.REFER_KEY), reg.getUrl());
+ }
+
+ @Test
+ public void testNotified_Normal() {
+ RegistryDirectory registryDirectory = getRegistryDirectory();
+ test_Notified2invokers(registryDirectory);
+ test_Notified1invokers(registryDirectory);
+ test_Notified3invokers(registryDirectory);
+ testforbid(registryDirectory);
+ }
+
+ @Test
+ public void testNotified_WithError() {
+ RegistryDirectory registryDirectory = getRegistryDirectory();
+ List<URL> serviceUrls = new ArrayList<URL>();
+ // ignore error log
+ URL badurl = URL.valueOf("notsupported://127.0.0.1/" + service);
+ serviceUrls.add(badurl);
+ serviceUrls.add(SERVICEURL);
+
+ registryDirectory.notify(serviceUrls);
+ Assert.assertEquals(true, registryDirectory.isAvailable());
+ List invokers = registryDirectory.list(invocation);
+ Assert.assertEquals(1, invokers.size());
+ }
+
+ @Test
+ public void testNotified_WithDuplicateUrls() {
+ List<URL> serviceUrls = new ArrayList<URL>();
+ // ignore error log
+ serviceUrls.add(SERVICEURL);
+ serviceUrls.add(SERVICEURL);
+
+ RegistryDirectory registryDirectory = getRegistryDirectory();
+ registryDirectory.notify(serviceUrls);
+ List invokers = registryDirectory.list(invocation);
+ Assert.assertEquals(1, invokers.size());
+ }
+
+ // forbid
+ private void testforbid(RegistryDirectory registryDirectory) {
+ invocation = new RpcInvocation();
+ List<URL> serviceUrls = new ArrayList<URL>();
+ registryDirectory.notify(serviceUrls);
+ Assert.assertEquals("invokers size=0 ,then the registry directory is not available", false,
+ registryDirectory.isAvailable());
+ try {
+ registryDirectory.list(invocation);
+ fail("forbid must throw RpcException");
+ } catch (RpcException e) {
+ Assert.assertEquals(RpcException.FORBIDDEN_EXCEPTION, e.getCode());
+ }
+ }
+
+ //测试调用和registry url的path无关
+ @Test
+ public void test_NotifiedDubbo1() {
+ URL errorPathUrl = URL.valueOf("notsupport:/" + "xxx"+"?interface="+service);
+ RegistryDirectory registryDirectory = getRegistryDirectory(errorPathUrl);
+ List<URL> serviceUrls = new ArrayList<URL>();
+ URL Dubbo1URL = URL.valueOf("dubbo://127.0.0.1:9098?lazy=true");
+ serviceUrls.add(Dubbo1URL.addParameter("methods", "getXXX"));
+ registryDirectory.notify(serviceUrls);
+ Assert.assertEquals(true, registryDirectory.isAvailable());
+
+ invocation = new RpcInvocation();
+
+ List<Invoker<DemoService>> invokers = registryDirectory.list(invocation);
+ Assert.assertEquals(1, invokers.size());
+
+ invocation.setMethodName("getXXX");
+ invokers = registryDirectory.list(invocation);
+ Assert.assertEquals(1, invokers.size());
+ Assert.assertEquals(DemoService.class.getName(), invokers.get(0).getUrl().getPath());
+ }
+
+ // notify one invoker
+ private void test_Notified1invokers(RegistryDirectory registryDirectory) {
+
+ List<URL> serviceUrls = new ArrayList<URL>();
+ serviceUrls.add(SERVICEURL.addParameter("methods", "getXXX1"));// .addParameter("refer.autodestroy", "true")
+ registryDirectory.notify(serviceUrls);
+ Assert.assertEquals(true, registryDirectory.isAvailable());
+
+ invocation = new RpcInvocation();
+
+ List invokers = registryDirectory.list(invocation);
+ Assert.assertEquals(1, invokers.size());
+
+ invocation.setMethodName("getXXX");
+ invokers = registryDirectory.list(invocation);
+ Assert.assertEquals(1, invokers.size());
+
+ invocation.setMethodName("getXXX1");
+ invokers = registryDirectory.list(invocation);
+ Assert.assertEquals(1, invokers.size());
+
+ invocation.setMethodName("getXXX2");
+ invokers = registryDirectory.list(invocation);
+ Assert.assertEquals(1, invokers.size());
+ }
+
+ // 两个invoker===================================
+ private void test_Notified2invokers(RegistryDirectory registryDirectory) {
+ List<URL> serviceUrls = new ArrayList<URL>();
+ serviceUrls.add(SERVICEURL.addParameter("methods", "getXXX1"));
+ serviceUrls.add(SERVICEURL2.addParameter("methods", "getXXX1,getXXX2"));
+ serviceUrls.add(SERVICEURL2.addParameter("methods", "getXXX1,getXXX2"));
+
+ registryDirectory.notify(serviceUrls);
+ Assert.assertEquals(true, registryDirectory.isAvailable());
+
+ invocation = new RpcInvocation();
+
+ List invokers = registryDirectory.list(invocation);
+ Assert.assertEquals(2, invokers.size());
+
+ invocation.setMethodName("getXXX");
+ invokers = registryDirectory.list(invocation);
+ Assert.assertEquals(2, invokers.size());
+
+ invocation.setMethodName("getXXX1");
+ invokers = registryDirectory.list(invocation);
+ Assert.assertEquals(2, invokers.size());
+
+ invocation.setMethodName("getXXX2");
+ invokers = registryDirectory.list(invocation);
+ Assert.assertEquals(1, invokers.size());
+ }
+
+ // 通知成3个invoker===================================
+ private void test_Notified3invokers(RegistryDirectory registryDirectory) {
+ List<URL> serviceUrls = new ArrayList<URL>();
+ serviceUrls.add(SERVICEURL.addParameter("methods", "getXXX1"));
+ serviceUrls.add(SERVICEURL2.addParameter("methods", "getXXX1,getXXX2"));
+ serviceUrls.add(SERVICEURL3.addParameter("methods", "getXXX1,getXXX2,getXXX3"));
+
+ registryDirectory.notify(serviceUrls);
+ Assert.assertEquals(true, registryDirectory.isAvailable());
+
+ invocation = new RpcInvocation();
+
+ List invokers = registryDirectory.list(invocation);
+ Assert.assertEquals(3, invokers.size());
+
+ invocation.setMethodName("getXXX");
+ invokers = registryDirectory.list(invocation);
+ Assert.assertEquals(3, invokers.size());
+
+ invocation.setMethodName("getXXX1");
+ invokers = registryDirectory.list(invocation);
+ Assert.assertEquals(3, invokers.size());
+
+ invocation.setMethodName("getXXX2");
+ invokers = registryDirectory.list(invocation);
+ Assert.assertEquals(2, invokers.size());
+
+ invocation.setMethodName("getXXX3");
+ invokers = registryDirectory.list(invocation);
+ Assert.assertEquals(1, invokers.size());
+ }
+
+ @Test
+ public void testParametersMerge() {
+ RegistryDirectory registryDirectory = getRegistryDirectory();
+ URL regurl = noMeaningUrl.addParameter("test", "reg").addParameterAndEncoded(RpcConstants.REFER_KEY,
+ "key=query&"
+ + Constants.LOADBALANCE_KEY
+ + "="
+ + LeastActiveLoadBalance.NAME);
+ RegistryDirectory<RegistryDirectoryTest> registryDirectory2 = new RegistryDirectory(
+ RegistryDirectoryTest.class,
+ regurl);
+ registryDirectory2.setProtocol(protocol);
+
+ List<URL> serviceUrls = new ArrayList<URL>();
+ // 检验注册中心的参数需要被清除
+ {
+ serviceUrls.clear();
+ serviceUrls.add(SERVICEURL.addParameter("methods", "getXXX1"));
+ registryDirectory.notify(serviceUrls);
+
+ invocation = new RpcInvocation();
+ List invokers = registryDirectory.list(invocation);
+
+ Invoker invoker = (Invoker) invokers.get(0);
+ URL url = invoker.getUrl();
+ Assert.assertEquals(null, url.getParameter("key"));
+ }
+ // 检验服务提供方的参数需要merge
+ {
+ serviceUrls.clear();
+ serviceUrls.add(SERVICEURL.addParameter("methods", "getXXX2").addParameter("key", "provider"));
+
+ registryDirectory.notify(serviceUrls);
+ invocation = new RpcInvocation();
+ List invokers = registryDirectory.list(invocation);
+
+ Invoker invoker = (Invoker) invokers.get(0);
+ URL url = invoker.getUrl();
+ Assert.assertEquals("provider", url.getParameter("key"));
+ }
+ // 检验服务query的参数需要与providermerge 。
+ {
+ serviceUrls.clear();
+ serviceUrls.add(SERVICEURL.addParameter("methods", "getXXX3").addParameter("key", "provider"));
+
+ registryDirectory2.notify(serviceUrls);
+ invocation = new RpcInvocation();
+ List invokers = registryDirectory2.list(invocation);
+
+ Invoker invoker = (Invoker) invokers.get(0);
+ URL url = invoker.getUrl();
+ Assert.assertEquals("query", url.getParameter("key"));
+ }
+
+ {
+ serviceUrls.clear();
+ serviceUrls.add(SERVICEURL.addParameter("methods", "getXXX1"));
+ registryDirectory.notify(serviceUrls);
+
+ invocation = new RpcInvocation();
+ List invokers = registryDirectory.list(invocation);
+
+ Invoker invoker = (Invoker) invokers.get(0);
+ URL url = invoker.getUrl();
+ Assert.assertEquals(false, url.getParameter(Constants.CHECK_KEY, false));
+ }
+ {
+ serviceUrls.clear();
+ serviceUrls.add(SERVICEURL.addParameter(Constants.LOADBALANCE_KEY, RoundRobinLoadBalance.NAME));
+ registryDirectory2.notify(serviceUrls);
+
+ invocation = new RpcInvocation();
+ invocation.setMethodName("get");
+ List invokers = registryDirectory2.list(invocation);
+
+ Invoker invoker = (Invoker) invokers.get(0);
+ URL url = invoker.getUrl();
+ Assert.assertEquals(LeastActiveLoadBalance.NAME, url.getMethodParameter("get", Constants.LOADBALANCE_KEY));
+ }
+ }
+
+ /**
+ * When destroying, RegistryDirectory should: 1. be disconnected from Registry 2. destroy all invokers
+ */
+ @Test
+ public void testDestroy() {
+ RegistryDirectory registryDirectory = getRegistryDirectory();
+
+ List<URL> serviceUrls = new ArrayList<URL>();
+ serviceUrls.add(SERVICEURL.addParameter("methods", "getXXX1"));
+ serviceUrls.add(SERVICEURL2.addParameter("methods", "getXXX1,getXXX2"));
+ serviceUrls.add(SERVICEURL3.addParameter("methods", "getXXX1,getXXX2,getXXX3"));
+
+ registryDirectory.notify(serviceUrls);
+ List<Invoker> invokers = registryDirectory.list(invocation);
+ Assert.assertEquals(true, registryDirectory.isAvailable());
+ Assert.assertEquals(true, invokers.get(0).isAvailable());
+
+ registryDirectory.destroy();
+ Assert.assertEquals(false, registryDirectory.isAvailable());
+ Assert.assertEquals(false, invokers.get(0).isAvailable());
+ registryDirectory.destroy();
+
+ Map<String, List<Invoker<RegistryDirectoryTest>>> methodInvokerMap = registryDirectory.getMethodInvokerMap();
+ Map<String, Invoker<RegistryDirectoryTest>> urlInvokerMap = registryDirectory.getUrlInvokerMap();
+
+ Assert.assertTrue(methodInvokerMap == null);
+ Assert.assertEquals(0, urlInvokerMap.size());
+ // List<U> urls = mockRegistry.getSubscribedUrls();
+
+ RpcInvocation inv = new RpcInvocation();
+ try {
+ registryDirectory.list(inv);
+ fail();
+ } catch (RpcException e) {
+ Assert.assertTrue(e.getMessage().contains("already destroyed"));
+ }
+ }
+
+ @Test
+ public void testDestroy_WithDestroyRegistry() {
+ RegistryDirectory registryDirectory = getRegistryDirectory();
+ CountDownLatch latch = new CountDownLatch(1);
+ registryDirectory.setRegistry(new MockRegistry(latch));
+ registryDirectory.destroy();
+ Assert.assertEquals(0, latch.getCount());
+ }
+
+ @Test
+ public void testDestroy_WithDestroyRegistry_WithError() {
+ RegistryDirectory registryDirectory = getRegistryDirectory();
+ registryDirectory.setRegistry(new MockRegistry(true));
+ registryDirectory.destroy();
+ }
+
+ @Test
+ public void testDubbo1UrlWithGenericInvocation() {
+
+ RegistryDirectory registryDirectory = getRegistryDirectory();
+
+ List<URL> serviceUrls = new ArrayList<URL>();
+ URL serviceURL = SERVICEURL_DUBBO_NOPATH.addParameter("methods", "getXXX1,getXXX2,getXXX3");
+ serviceUrls.add(serviceURL);
+
+ registryDirectory.notify(serviceUrls);
+
+ // Object $invoke(String method, String[] parameterTypes, Object[] args) throws GenericException;
+ invocation = new RpcInvocation(Constants.$INVOKE, new Class[] { String.class, String[].class, Object[].class },
+ new Object[] { "getXXX1", "", new Object[] {} });
+
+ List<Invoker> invokers = registryDirectory.list(invocation);
+
+ Assert.assertEquals(1, invokers.size());
+ Assert.assertEquals(serviceURL.setPath(service), invokers.get(0).getUrl());
+
+ }
+
+ enum Param {
+ MORGAN,
+ };
+
+ /**
+ * When the first arg of a method is String or Enum, Registry server can do parameter-value-based routing.
+ */
+ @Test
+ public void testParmeterRoute() {
+ RegistryDirectory registryDirectory = getRegistryDirectory();
+ List<URL> serviceUrls = new ArrayList<URL>();
+ serviceUrls.add(SERVICEURL.addParameter("methods", "getXXX1.napoli"));
+ serviceUrls.add(SERVICEURL2.addParameter("methods", "getXXX1.MORGAN,getXXX2"));
+ serviceUrls.add(SERVICEURL3.addParameter("methods", "getXXX1.morgan,getXXX2,getXXX3"));
+
+ registryDirectory.notify(serviceUrls);
+
+ invocation = new RpcInvocation(
+ Constants.$INVOKE,
+ new Class[] { String.class, String[].class, Object[].class },
+ new Object[] { "getXXX1", new String[] { "Enum" }, new Object[] { Param.MORGAN } });
+
+ List invokers = registryDirectory.list(invocation);
+ Assert.assertEquals(1, invokers.size());
+ }
+
+ /**
+ * Empty notify cause forbidden, non-empty notify cancels forbidden state
+ */
+ @Test
+ public void testEmptyNotifyCauseForbidden() {
+ RegistryDirectory registryDirectory = getRegistryDirectory();
+ List invokers = null;
+
+ List<URL> serviceUrls = new ArrayList<URL>();
+ registryDirectory.notify(serviceUrls);
+
+ RpcInvocation inv = new RpcInvocation();
+ try {
+ invokers = registryDirectory.list(inv);
+ } catch (RpcException e) {
+ Assert.assertEquals(RpcException.FORBIDDEN_EXCEPTION, e.getCode());
+ Assert.assertEquals(false, registryDirectory.isAvailable());
+ }
+
+ serviceUrls.add(SERVICEURL.addParameter("methods", "getXXX1"));
+ serviceUrls.add(SERVICEURL2.addParameter("methods", "getXXX1,getXXX2"));
+ serviceUrls.add(SERVICEURL3.addParameter("methods", "getXXX1,getXXX2,getXXX3"));
+
+ registryDirectory.notify(serviceUrls);
+ inv.setMethodName("getXXX2");
+ invokers = registryDirectory.list(inv);
+ Assert.assertEquals(true, registryDirectory.isAvailable());
+ Assert.assertEquals(2, invokers.size());
+ }
+
+ private static boolean isScriptUnsupported = new ScriptEngineManager().getEngineByName("javascript") == null;
+
+ /**
+ * 1. notify twice, the second time notified router rules should completely replace the former one. 2. notify with
+ * no router url, do nothing to current routers 3. notify with only one router url, with router=clean, clear all
+ * current routers
+ */
+ @Test
+ public void testNotifyRouterUrls() {
+ if (isScriptUnsupported) return;
+ RegistryDirectory registryDirectory = getRegistryDirectory();
+ URL routerurl = URL.valueOf(RpcConstants.ROUTE_PROTOCOL + "://127.0.0.1:9096/");
+ URL routerurl2 = URL.valueOf(RpcConstants.ROUTE_PROTOCOL + "://127.0.0.1:9097/");
+
+ List<URL> serviceUrls = new ArrayList<URL>();
+ // without ROUTER_KEY, the first router should not be created.
+ serviceUrls.add(routerurl.addParameter(RpcConstants.TYPE_KEY, "javascript").addParameter(RpcConstants.ROUTER_KEY,
+ "notsupported").addParameter(RpcConstants.RULE_KEY,
+ "function test1(){}"));
+ serviceUrls.add(routerurl2.addParameter(RpcConstants.TYPE_KEY, "javascript").addParameter(RpcConstants.ROUTER_KEY,
+ ScriptRouterFactory.NAME).addParameter(RpcConstants.RULE_KEY,
+ "function test1(){}"));
+
+ registryDirectory.notify(serviceUrls);
+ List<Router> routers = registryDirectory.getRouters();
+ Assert.assertEquals(1, routers.size());
+ Assert.assertEquals(ScriptRouter.class, routers.get(0).getClass());
+
+ registryDirectory.notify(new ArrayList<URL>());
+ routers = registryDirectory.getRouters();
+ Assert.assertEquals(1, routers.size());
+ Assert.assertEquals(ScriptRouter.class, routers.get(0).getClass());
+
+ serviceUrls.clear();
+ serviceUrls.add(routerurl.addParameter(RpcConstants.ROUTER_KEY, RpcConstants.ROUTER_TYPE_CLEAR));
+ registryDirectory.notify(serviceUrls);
+ routers = registryDirectory.getRouters();
+ Assert.assertEquals(0, routers.size());
+ }
+
+ @Test
+ public void testNotifyRouterUrls_Clean() {
+ if (isScriptUnsupported) return;
+ RegistryDirectory registryDirectory = getRegistryDirectory();
+ URL routerurl = URL.valueOf(RpcConstants.ROUTE_PROTOCOL + "://127.0.0.1:9096/").addParameter(RpcConstants.ROUTER_KEY,
+ "javascript").addParameter(RpcConstants.RULE_KEY,
+ "function test1(){}").addParameter(RpcConstants.ROUTER_KEY,
+ "script"); // FIX
+ // BAD
+
+ List<URL> serviceUrls = new ArrayList<URL>();
+ // without ROUTER_KEY, the first router should not be created.
+ serviceUrls.add(routerurl);
+ registryDirectory.notify(serviceUrls);
+ List routers = registryDirectory.getRouters();
+ Assert.assertEquals(1, routers.size());
+
+ serviceUrls.clear();
+ serviceUrls.add(routerurl.addParameter(RpcConstants.ROUTER_KEY, RpcConstants.ROUTER_TYPE_CLEAR));
+ registryDirectory.notify(serviceUrls);
+ routers = registryDirectory.getRouters();
+ Assert.assertEquals(0, routers.size());
+ }
+
+ private static interface DemoService {
+ }
+
+ private static class MockRegistry implements Registry {
+
+ CountDownLatch latch;
+ boolean destroyWithError;
+
+ public MockRegistry(CountDownLatch latch){
+ this.latch = latch;
+ }
+
+ public MockRegistry(boolean destroyWithError){
+ this.destroyWithError = destroyWithError;
+ }
+
+ public void register(URL url) {
+
+ }
+
+ public void unregister(URL url) {
+
+ }
+
+ public void subscribe(URL url, NotifyListener listener) {
+
+ }
+
+ public void unsubscribe(URL url, NotifyListener listener) {
+ 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-simple/src/test/java/com/alibaba/dubbo/registry/dubbo/RegistryProtocolTest.java b/dubbo-registry-simple/src/test/java/com/alibaba/dubbo/registry/dubbo/RegistryProtocolTest.java
new file mode 100644
index 0000000..17af703
--- /dev/null
+++ b/dubbo-registry-simple/src/test/java/com/alibaba/dubbo/registry/dubbo/RegistryProtocolTest.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.registry.dubbo;
+
+import static org.junit.Assert.assertEquals;
+
+import org.junit.Test;
+
+import com.alibaba.dubbo.common.ExtensionLoader;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.registry.RegistryFactory;
+import com.alibaba.dubbo.registry.support.RegistryProtocol;
+import com.alibaba.dubbo.registry.support.SimpleRegistryExporter;
+import com.alibaba.dubbo.remoting.exchange.ExchangeClient;
+import com.alibaba.dubbo.rpc.Exporter;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.Protocol;
+import com.alibaba.dubbo.rpc.RpcConstants;
+import com.alibaba.dubbo.rpc.cluster.support.FailfastCluster;
+import com.alibaba.dubbo.rpc.protocol.dubbo.DubboInvoker;
+import com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol;
+
+/**
+ * RegistryProtocolTest
+ *
+ * @author tony.chenl
+ */
+public class RegistryProtocolTest {
+
+ static {
+ SimpleRegistryExporter.exportIfAbsent(9090);
+ }
+
+ String service = "com.alibaba.dubbo.registry.protocol.DemoService:1.0.0";
+ String serviceUrl = "dubbo://127.0.0.1:9453/" + service + "?notify=true&methods=test1,test2";
+ URL registryUrl = URL.valueOf("dubbo://127.0.0.1:9090/");
+
+ @Test
+ public void testDefaultPort() {
+ RegistryProtocol registryProtocol = new RegistryProtocol();
+ assertEquals(9090, registryProtocol.getDefaultPort());
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testExportUrlNull() {
+ RegistryProtocol registryProtocol = new RegistryProtocol();
+ registryProtocol.setCluster(new FailfastCluster());
+
+ Protocol dubboProtocol = new DubboProtocol();
+ 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 = new DubboProtocol();
+ registryProtocol.setProtocol(dubboProtocol);
+ registryUrl = registryUrl.addParameter(RpcConstants.EXPORT_KEY, serviceUrl);
+ DubboInvoker<DemoService> invoker = new DubboInvoker<DemoService>(DemoService.class,
+ registryUrl, new ExchangeClient[] { new MockedClient("10.20.20.20", 2222, true) });
+ Exporter<DemoService> exporter = registryProtocol.export(invoker);
+ assertEquals(invoker, exporter.getInvoker());
+ exporter.unexport();
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-registry-simple/src/test/java/com/alibaba/dubbo/registry/dubbo/RegistryStatusCheckerTest.java b/dubbo-registry-simple/src/test/java/com/alibaba/dubbo/registry/dubbo/RegistryStatusCheckerTest.java
new file mode 100644
index 0000000..591f2e7
--- /dev/null
+++ b/dubbo-registry-simple/src/test/java/com/alibaba/dubbo/registry/dubbo/RegistryStatusCheckerTest.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.registry.dubbo;
+
+import static org.junit.Assert.assertEquals;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import com.alibaba.dubbo.common.ExtensionLoader;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.status.Status;
+import com.alibaba.dubbo.registry.RegistryFactory;
+import com.alibaba.dubbo.registry.support.AbstractRegistryFactory;
+import com.alibaba.dubbo.registry.support.RegistryStatusChecker;
+import com.alibaba.dubbo.registry.support.SimpleRegistryExporter;
+
+/**
+ * StatusTest
+ *
+ * @author tony.chenl
+ */
+public class RegistryStatusCheckerTest {
+
+ static {
+ SimpleRegistryExporter.exportIfAbsent(9090);
+ SimpleRegistryExporter.exportIfAbsent(9091);
+ }
+ URL registryUrl = URL.valueOf("dubbo://cat:cat@127.0.0.1:9090/");
+ URL registryUrl2 = URL.valueOf("dubbo://cat:cat@127.0.0.1:9091");
+
+ @Before
+ public void setUp() {
+ AbstractRegistryFactory.destroyAll();
+ }
+
+ @Test
+ public void testCheckUnknown() {
+ assertEquals(Status.Level.UNKNOWN, new RegistryStatusChecker().check().getLevel());
+ }
+
+ @Test
+ public void testCheckOK() {
+ ExtensionLoader.getExtensionLoader(RegistryFactory.class).getAdaptiveExtension().getRegistry(registryUrl);
+ ExtensionLoader.getExtensionLoader(RegistryFactory.class).getAdaptiveExtension().getRegistry(registryUrl2);
+ StringBuilder buf = new StringBuilder();
+ buf.append(registryUrl.getAddress());
+ buf.append("(connected)");
+ buf.append(",");
+ buf.append(registryUrl2.getAddress());
+ buf.append("(connected)");
+ assertEquals(Status.Level.OK, new RegistryStatusChecker().check().getLevel());
+ assertEquals(buf.toString(), new RegistryStatusChecker().check().getMessage());
+ }
+}
\ No newline at end of file
diff --git a/dubbo-registry-simple/src/test/resources/log4j.xml b/dubbo-registry-simple/src/test/resources/log4j.xml
new file mode 100644
index 0000000..e31a4ac
--- /dev/null
+++ b/dubbo-registry-simple/src/test/resources/log4j.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.
+-->
+<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-zookeeper/pom.xml b/dubbo-registry-zookeeper/pom.xml
new file mode 100644
index 0000000..b09d89d
--- /dev/null
+++ b/dubbo-registry-zookeeper/pom.xml
@@ -0,0 +1,42 @@
+<!--
+ - Copyright 1999-2011 Alibaba Group.
+ -
+ - Licensed under the Apache License, Version 2.0 (the "License");
+ - you may not use this file except in compliance with the License.
+ - You may obtain a copy of the License at
+ -
+ - http://www.apache.org/licenses/LICENSE-2.0
+ -
+ - Unless required by applicable law or agreed to in writing, software
+ - distributed under the License is distributed on an "AS IS" BASIS,
+ - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ - See the License for the specific language governing permissions and
+ - limitations under the License.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>com.alibaba</groupId>
+ <artifactId>dubbo-parent</artifactId>
+ <version>2.0.8</version>
+ </parent>
+ <artifactId>dubbo-registry-zookeeper</artifactId>
+ <packaging>jar</packaging>
+ <name>Dubbo Zookeeper Registry Module</name>
+ <description>The zookeeper registry module of dubbo project</description>
+ <properties>
+ <skip_maven_deploy>false</skip_maven_deploy>
+ </properties>
+ <dependencies>
+ <dependency>
+ <groupId>com.alibaba</groupId>
+ <artifactId>dubbo-registry</artifactId>
+ <version>${project.parent.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.zookeeper</groupId>
+ <artifactId>zookeeper</artifactId>
+ </dependency>
+ </dependencies>
+</project>
\ No newline at end of file
diff --git a/dubbo-registry-zookeeper/src/main/java/com/alibaba/dubbo/registry/zookeeper/ZookeeperRegistry.java b/dubbo-registry-zookeeper/src/main/java/com/alibaba/dubbo/registry/zookeeper/ZookeeperRegistry.java
new file mode 100644
index 0000000..e022aee
--- /dev/null
+++ b/dubbo-registry-zookeeper/src/main/java/com/alibaba/dubbo/registry/zookeeper/ZookeeperRegistry.java
@@ -0,0 +1,301 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.registry.zookeeper;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.locks.ReentrantLock;
+
+import org.apache.zookeeper.CreateMode;
+import org.apache.zookeeper.KeeperException;
+import org.apache.zookeeper.WatchedEvent;
+import org.apache.zookeeper.Watcher;
+import org.apache.zookeeper.Watcher.Event.EventType;
+import org.apache.zookeeper.Watcher.Event.KeeperState;
+import org.apache.zookeeper.ZooDefs.Ids;
+import org.apache.zookeeper.ZooKeeper;
+
+import 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 String SEPARATOR = "/";
+
+ private final String root;
+
+ private final boolean auth;
+
+ private final ReentrantLock zookeeperLock = new ReentrantLock();
+
+ private final Set<String> failedWatched = new ConcurrentHashSet<String>();
+
+ private volatile ZooKeeper zookeeper;
+
+ public ZookeeperRegistry(URL url) {
+ super(url);
+ this.auth = url.getUsername() != null && url.getUsername().length() > 0
+ && url.getPassword() != null && url.getPassword().length() > 0;
+ String group = url.getParameter(Constants.GROUP_KEY);
+ if (group != null && group.length() > 0) {
+ group = SEPARATOR + group;
+ this.root = group;
+ } else {
+ this.root = "";
+ }
+ initZookeeper();
+ }
+
+ @Override
+ protected void doRetry() {
+ initZookeeper();
+ if (failedWatched.size() > 0) {
+ Set<String> failed = new HashSet<String>(failedWatched);
+ if (failed.size() > 0) {
+ if (logger.isInfoEnabled()) {
+ logger.info("Retry watch " + failed);
+ }
+ for (String service : failed) {
+ try {
+ zookeeper.getChildren(service, true);
+ failedWatched.remove(service);
+ } catch (Throwable t) {
+ logger.warn("Failed to retry register " + failed + ", waiting for again, cause: " + t.getMessage(), t);
+ }
+ }
+ }
+ }
+ }
+
+ private List<String> watch(String service) {
+ try {
+ ZooKeeper zk = ZookeeperRegistry.this.zookeeper;
+ if (zk != null) {
+ List<String> result = zookeeper.getChildren(service, true);
+ failedWatched.remove(service);
+ return result;
+ }
+ } catch (Throwable e) {
+ logger.warn(e.getMessage(), e);
+ }
+ failedWatched.add(service);
+ return new ArrayList<String>(0);
+ }
+
+ private void recover() {
+ for (String url : new HashSet<String>(getRegistered())) {
+ try {
+ register(URL.valueOf(url));
+ } catch (Throwable e) {
+ logger.warn(e.getMessage(), e);
+ }
+ }
+ for (String url : new HashSet<String>(getSubscribed().keySet())) {
+ try {
+ watch(toServicePath(URL.valueOf(url)));
+ } catch (Throwable e) {
+ logger.warn(e.getMessage(), e);
+ }
+ }
+ }
+
+ private void initZookeeper() {
+ ZooKeeper zk = this.zookeeper;
+ if (zk == null || zk.getState() == null || ! zk.getState().isAlive()) {
+ zookeeperLock.lock();
+ try {
+ zk = this.zookeeper;
+ if (zk == null || zk.getState() == null || ! zk.getState().isAlive()) {
+ this.zookeeper = createZookeeper();
+ recover();
+ }
+ if (zk != null) {
+ zk.close();
+ }
+ } catch (Exception e) {
+ throw new IllegalStateException(e.getMessage(), e);
+ } finally {
+ zookeeperLock.unlock();
+ }
+ }
+ }
+
+ private ZooKeeper createZookeeper() throws Exception {
+ URL url = getUrl();
+ String address = url.getAddress();
+ String backup = url.getParameter(Constants.BACKUP_KEY);
+ if (backup != null && backup.length() > 0) {
+ address = address + "," + backup;
+ }
+ ZooKeeper zk = new ZooKeeper(address, url.getPositiveParameter(
+ Constants.TIMEOUT_KEY, Integer.MAX_VALUE), new Watcher() {
+ public void process(WatchedEvent event) {
+ try {
+ if (event.getState() == KeeperState.Expired) {
+ initZookeeper();
+ } else if (event.getState() == KeeperState.SyncConnected
+ && event.getType() == EventType.None) {
+ recover();
+ }
+ if (event.getType() != EventType.NodeChildrenChanged) {
+ return;
+ }
+ String path = event.getPath();
+ if (path == null || path.length() == 0) {
+ return;
+ }
+ List<String> providers = watch(path);
+ if (providers == null || providers.size() == 0) {
+ return;
+ }
+ String service = path;
+ int i = service.lastIndexOf(SEPARATOR);
+ if (i >= 0) {
+ service = service.substring(i + 1);
+ }
+ service = URL.decode(service);
+ for (Map.Entry<String, Set<NotifyListener>> entry : getSubscribed().entrySet()) {
+ String key = entry.getKey();
+ URL subscribe = URL.valueOf(key);
+ String subscribeService = subscribe.getServiceName();
+ if (service.equals(subscribeService)) {
+ List<URL> list = toUrls(subscribe, providers);
+ if (logger.isInfoEnabled()) {
+ logger.info("Zookeeper service changed, service: " + service + ", urls: " + list);
+ }
+ for (NotifyListener listener : entry.getValue()) {
+ ZookeeperRegistry.this.notify(subscribe, listener, list);
+ }
+ }
+ }
+ } catch (Throwable e) {
+ logger.error(e.getMessage(), e);
+ }
+ }
+ });
+ if (auth) {
+ zk.addAuthInfo(url.getUsername(), url.getPassword().getBytes());
+ }
+ if (root != null && root.length() > 0 && zk.exists(root, false) == null) {
+ zk.create(root, new byte[0], auth ? Ids.CREATOR_ALL_ACL : Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
+ }
+ return zk;
+ }
+
+ public boolean isAvailable() {
+ return zookeeper.getState().isAlive();
+ }
+
+ public void destroy() {
+ super.destroy();
+ try {
+ zookeeper.close();
+ } catch (Exception e) {
+ logger.warn(e.getMessage(), e);
+ }
+ }
+
+ public List<URL> lookup(URL url) {
+ if (url == null) {
+ throw new IllegalArgumentException("lookup url == null");
+ }
+ try {
+ String service = toServicePath(url);
+ List<String> providers = zookeeper.getChildren(service, true);
+ return toUrls(url, providers);
+ } catch (Throwable e) {
+ throw new RpcException("Failed to lookup " + url + ", cause: " + e.getMessage(), e);
+ }
+ }
+
+ protected void doRegister(URL url) {
+ try {
+ String service = toServicePath(url);
+ String provider = service + toProviderPath(url);
+ if (zookeeper.exists(service, false) == null) {
+ zookeeper.create(service, new byte[0], auth ? Ids.CREATOR_ALL_ACL : Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
+ }
+ if (zookeeper.exists(provider, false) == null) {
+ zookeeper.create(provider, new byte[0], auth ? Ids.CREATOR_ALL_ACL : Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL);
+ }
+ } catch (Throwable e) {
+ throw new RpcException("Failed to register " + url + ", cause: " + e.getMessage(), e);
+ }
+ }
+
+ protected void doUnregister(URL url) {
+ try {
+ String service = toServicePath(url);
+ String provider = service + toProviderPath(url);
+ zookeeper.delete(provider, -1);
+ } catch (Throwable e) {
+ throw new RpcException("Failed to unregister " + url + ", cause: " + e.getMessage(), e);
+ }
+ }
+
+ protected void doSubscribe(URL url, NotifyListener listener) {
+ try {
+ String service = toServicePath(url);
+ List<String> providers = zookeeper.getChildren(service, true);
+ List<URL> urls = toUrls(url, providers);
+ if (urls != null && urls.size() > 0) {
+ notify(url, listener, urls);
+ }
+ } catch (Throwable e) {
+ throw new RpcException("Failed to subscribe " + url + ", cause: " + e.getMessage(), e);
+ }
+ }
+
+ protected void doUnsubscribe(URL url, NotifyListener listener) {
+ }
+
+ private String toServicePath(URL url) {
+ return root + SEPARATOR + URL.encode(url.getServiceName());
+ }
+
+ private String toProviderPath(URL url) {
+ return SEPARATOR + URL.encode(url.toFullString());
+ }
+
+ private List<URL> toUrls(URL consumer, List<String> providers) throws KeeperException, InterruptedException {
+ List<URL> urls = new ArrayList<URL>();
+ for (String provider : providers) {
+ URL url = URL.valueOf(URL.decode(provider));
+ if (UrlUtils.isMatch(consumer, url)) {
+ urls.add(url);
+ }
+ }
+ return urls;
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-registry-zookeeper/src/main/java/com/alibaba/dubbo/registry/zookeeper/ZookeeperRegistryFactory.java b/dubbo-registry-zookeeper/src/main/java/com/alibaba/dubbo/registry/zookeeper/ZookeeperRegistryFactory.java
new file mode 100644
index 0000000..cd4daeb
--- /dev/null
+++ b/dubbo-registry-zookeeper/src/main/java/com/alibaba/dubbo/registry/zookeeper/ZookeeperRegistryFactory.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.registry.zookeeper;
+
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.registry.Registry;
+import com.alibaba.dubbo.registry.support.AbstractRegistryFactory;
+
+/**
+ * ZookeeperRegistryFactory.
+ *
+ * @author william.liangf
+ */
+@Extension("zookeeper")
+public class ZookeeperRegistryFactory extends AbstractRegistryFactory {
+
+ public Registry createRegistry(URL url) {
+ return new ZookeeperRegistry(url);
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-registry-zookeeper/src/main/resources/META-INF/services/com.alibaba.dubbo.registry.RegistryFactory b/dubbo-registry-zookeeper/src/main/resources/META-INF/services/com.alibaba.dubbo.registry.RegistryFactory
new file mode 100644
index 0000000..b538326
--- /dev/null
+++ b/dubbo-registry-zookeeper/src/main/resources/META-INF/services/com.alibaba.dubbo.registry.RegistryFactory
@@ -0,0 +1 @@
+com.alibaba.dubbo.registry.zookeeper.ZookeeperRegistryFactory
\ No newline at end of file
diff --git a/dubbo-registry-zookeeper/src/test/java/com/alibaba/dubbo/registry/zookeeper/ZookeeperRegistryTest.java b/dubbo-registry-zookeeper/src/test/java/com/alibaba/dubbo/registry/zookeeper/ZookeeperRegistryTest.java
new file mode 100644
index 0000000..0d57bcb
--- /dev/null
+++ b/dubbo-registry-zookeeper/src/test/java/com/alibaba/dubbo/registry/zookeeper/ZookeeperRegistryTest.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.zookeeper;
+
+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 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..25b449c
--- /dev/null
+++ b/dubbo-registry/pom.xml
@@ -0,0 +1,38 @@
+<!--
+ - Copyright 1999-2011 Alibaba Group.
+ -
+ - Licensed under the Apache License, Version 2.0 (the "License");
+ - you may not use this file except in compliance with the License.
+ - You may obtain a copy of the License at
+ -
+ - http://www.apache.org/licenses/LICENSE-2.0
+ -
+ - Unless required by applicable law or agreed to in writing, software
+ - distributed under the License is distributed on an "AS IS" BASIS,
+ - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ - See the License for the specific language governing permissions and
+ - limitations under the License.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>com.alibaba</groupId>
+ <artifactId>dubbo-parent</artifactId>
+ <version>2.0.8</version>
+ </parent>
+ <artifactId>dubbo-registry</artifactId>
+ <packaging>jar</packaging>
+ <name>Dubbo Registry Module</name>
+ <description>The registry module of dubbo project</description>
+ <properties>
+ <skip_maven_deploy>false</skip_maven_deploy>
+ </properties>
+ <dependencies>
+ <dependency>
+ <groupId>com.alibaba</groupId>
+ <artifactId>dubbo-cluster</artifactId>
+ <version>${project.parent.version}</version>
+ </dependency>
+ </dependencies>
+</project>
\ No newline at end of file
diff --git a/dubbo-registry/src/main/java/com/alibaba/dubbo/registry/NotifyListener.java b/dubbo-registry/src/main/java/com/alibaba/dubbo/registry/NotifyListener.java
new file mode 100644
index 0000000..5e88dbb
--- /dev/null
+++ b/dubbo-registry/src/main/java/com/alibaba/dubbo/registry/NotifyListener.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.registry;
+
+import java.util.List;
+import java.util.Map;
+
+import com.alibaba.dubbo.common.URL;
+
+/**
+ * NotifyListener. (API, Prototype, ThreadSafe)
+ *
+ * @see com.alibaba.dubbo.registry.Registry#subscribe(URL, NotifyListener)
+ * @author william.liangf
+ */
+public interface NotifyListener {
+
+ /**
+ * 当收到服务变更通知时触发
+ * @param urls 含义同{@link Registry#register(String, Map)}的urls参数。
+ */
+ void notify(List<URL> urls);
+
+}
\ No newline at end of file
diff --git a/dubbo-registry/src/main/java/com/alibaba/dubbo/registry/Registry.java b/dubbo-registry/src/main/java/com/alibaba/dubbo/registry/Registry.java
new file mode 100644
index 0000000..7211b75
--- /dev/null
+++ b/dubbo-registry/src/main/java/com/alibaba/dubbo/registry/Registry.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.registry;
+
+import com.alibaba.dubbo.common.Node;
+import com.alibaba.dubbo.common.URL;
+
+/**
+ * Registry. (SPI, Prototype, ThreadSafe)
+ *
+ * @see com.alibaba.dubbo.registry.RegistryFactory#getRegistry(URL)
+ * @see com.alibaba.dubbo.registry.support.AbstractRegistry
+ * @author william.liangf
+ */
+public interface Registry extends Node, RegistryService {
+}
\ No newline at end of file
diff --git a/dubbo-registry/src/main/java/com/alibaba/dubbo/registry/RegistryFactory.java b/dubbo-registry/src/main/java/com/alibaba/dubbo/registry/RegistryFactory.java
new file mode 100644
index 0000000..2ab1105
--- /dev/null
+++ b/dubbo-registry/src/main/java/com/alibaba/dubbo/registry/RegistryFactory.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.registry;
+
+import com.alibaba.dubbo.common.Adaptive;
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.common.URL;
+
+/**
+ * RegistryFactory. (SPI, Singleton, ThreadSafe)
+ *
+ * NOTE: RegistryFactory should <strong>NOT</strong> have default implement.
+ *
+ * @see com.alibaba.dubbo.registry.support.AbstractRegistryFactory
+ * @author william.liangf
+ */
+@Extension("dubbo")
+public interface RegistryFactory {
+
+ /**
+ * get registry.
+ *
+ * @param url registry url
+ * @return registry
+ */
+ @Adaptive({"protocol"})
+ Registry getRegistry(URL url);
+
+}
\ No newline at end of file
diff --git a/dubbo-registry/src/main/java/com/alibaba/dubbo/registry/RegistryService.java b/dubbo-registry/src/main/java/com/alibaba/dubbo/registry/RegistryService.java
new file mode 100644
index 0000000..911711b
--- /dev/null
+++ b/dubbo-registry/src/main/java/com/alibaba/dubbo/registry/RegistryService.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.registry;
+
+import java.util.List;
+
+import com.alibaba.dubbo.common.URL;
+
+/**
+ * RegistryService
+ *
+ * @author william.liangf
+ */
+public interface RegistryService {
+
+ /**
+ * 注册服务
+ *
+ * @param url 服务提供者地址
+ */
+ void register(URL url);
+
+ /**
+ * 取消注册服务
+ *
+ * @param url 服务提供者地址
+ */
+ void unregister(URL url);
+
+ /**
+ * 订阅服务
+ *
+ * @param url 服务查询键值对,如:version=1.0.0&application=kylin
+ * @param listener 服务变更事件监听器
+ */
+ void subscribe(URL url, NotifyListener listener);
+
+ /**
+ * 取消订阅服务
+ *
+ * @param url 服务查询键值对,如:version=1.0.0&application=kylin
+ * @param listener 服务变更事件监听器
+ */
+ void unsubscribe(URL url, NotifyListener listener);
+
+ /**
+ * 查询服务
+ *
+ * @param url 服务查询键值对
+ * @return 服务提供者者列表
+ */
+ List<URL> lookup(URL url);
+
+}
\ No newline at end of file
diff --git a/dubbo-registry/src/main/java/com/alibaba/dubbo/registry/support/AbstractRegistry.java b/dubbo-registry/src/main/java/com/alibaba/dubbo/registry/support/AbstractRegistry.java
new file mode 100644
index 0000000..d2e084f
--- /dev/null
+++ b/dubbo-registry/src/main/java/com/alibaba/dubbo/registry/support/AbstractRegistry.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.registry.support;
+
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.logger.Logger;
+import com.alibaba.dubbo.common.logger.LoggerFactory;
+import com.alibaba.dubbo.common.utils.ConcurrentHashSet;
+import com.alibaba.dubbo.registry.NotifyListener;
+import com.alibaba.dubbo.registry.Registry;
+
+/**
+ * 嵌入式注册中心实现,不开端口,只是map进行存储查询.不需要显示声明
+ *
+ * @author chao.liuc
+ * @author william.liangf
+ */
+public abstract class AbstractRegistry implements Registry {
+
+ // 日志输出
+ protected final Logger logger = LoggerFactory.getLogger(getClass());
+
+ private final URL registryUrl;
+
+ private final Set<String> registered = new ConcurrentHashSet<String>();
+
+ private final ConcurrentMap<String, Set<NotifyListener>> subscribed = new ConcurrentHashMap<String, Set<NotifyListener>>();
+
+ public AbstractRegistry(URL url) {
+ if (url == null) {
+ throw new IllegalArgumentException("registry url == null");
+ }
+ this.registryUrl = url;
+ }
+
+ public Set<String> getRegistered() {
+ return registered;
+ }
+
+ public Map<String, Set<NotifyListener>> getSubscribed() {
+ return subscribed;
+ }
+
+ public URL getUrl() {
+ return registryUrl;
+ }
+
+ public void register(URL url) {
+ if (url == null) {
+ throw new IllegalArgumentException("register url == null");
+ }
+ if (logger.isInfoEnabled()){
+ logger.info("Register: " + url);
+ }
+ registered.add(url.toFullString());
+ }
+
+ public void unregister(URL url) {
+ if (url == null) {
+ throw new IllegalArgumentException("unregister url == null");
+ }
+ if (logger.isInfoEnabled()){
+ logger.info("Unregister: " + url);
+ }
+ registered.remove(url.toFullString());
+ }
+
+ public void subscribe(URL url, NotifyListener listener) {
+ if (url == null) {
+ throw new IllegalArgumentException("subscribe url == null");
+ }
+ if (logger.isInfoEnabled()){
+ logger.info("Subscribe: " + url);
+ }
+ if (listener == null) {
+ throw new IllegalArgumentException("subscribe listener == null");
+ }
+ String key = url.toFullString();
+ Set<NotifyListener> listeners = subscribed.get(key);
+ if (listeners == null) {
+ subscribed.putIfAbsent(key, new ConcurrentHashSet<NotifyListener>());
+ listeners = subscribed.get(key);
+ }
+ listeners.add(listener);
+ }
+
+ public void unsubscribe(URL url, NotifyListener listener) {
+ if (url == null) {
+ throw new IllegalArgumentException("unsubscribe url == null");
+ }
+ if (listener == null) {
+ throw new IllegalArgumentException("unsubscribe listener == null");
+ }
+ if (logger.isInfoEnabled()){
+ logger.info("Unsubscribe: " + url);
+ }
+ String key = url.toFullString();
+ Set<NotifyListener> listeners = subscribed.get(key);
+ if (listeners != null) {
+ listeners.remove(listener);
+ }
+ }
+
+ public void destroy() {
+ if (logger.isInfoEnabled()){
+ logger.info("Destroy registry: " + getUrl());
+ }
+ for (String url : new HashSet<String>(registered)) {
+ try {
+ unregister(URL.valueOf(url));
+ } catch (Throwable t) {
+ logger.warn(t.getMessage(), t);
+ }
+ }
+ }
+
+ public String toString() {
+ return getUrl().toString();
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-registry/src/main/java/com/alibaba/dubbo/registry/support/AbstractRegistryFactory.java b/dubbo-registry/src/main/java/com/alibaba/dubbo/registry/support/AbstractRegistryFactory.java
new file mode 100644
index 0000000..37a063b
--- /dev/null
+++ b/dubbo-registry/src/main/java/com/alibaba/dubbo/registry/support/AbstractRegistryFactory.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.registry.support;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.locks.ReentrantLock;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.logger.Logger;
+import com.alibaba.dubbo.common.logger.LoggerFactory;
+import com.alibaba.dubbo.registry.Registry;
+import com.alibaba.dubbo.registry.RegistryFactory;
+
+/**
+ * RegistryLocators. (API, Static, ThreadSafe)
+ *
+ * @see com.alibaba.dubbo.registry.RegistryFactory
+ * @author william.liangf
+ */
+public abstract class AbstractRegistryFactory implements RegistryFactory {
+
+ // 日志输出
+ private static final Logger LOGGER = LoggerFactory.getLogger(AbstractRegistryFactory.class);
+
+ // 注册中心获取过程锁
+ protected static final ReentrantLock LOCK = new ReentrantLock();
+
+ // 注册中心集合 Map<RegistryAddress, Registry>
+ protected static final Map<String, Registry> REGISTRIES = new ConcurrentHashMap<String, Registry>();
+
+ public Registry getRegistry(URL url) {
+ // 锁定注册中心获取过程,保证注册中心单一实例
+ LOCK.lock();
+ try {
+ Registry registry = REGISTRIES.get(getCacheKey(url));
+ if (registry != null) {
+ return registry;
+ }
+ registry = createRegistry(url);
+ if (registry == null) {
+ throw new IllegalStateException("Can not create registry " + url);
+ }
+ REGISTRIES.put(getCacheKey(url), registry);
+ return registry;
+ } finally {
+ // 释放锁
+ LOCK.unlock();
+ }
+ }
+
+ protected abstract Registry createRegistry(URL url);
+
+ /**
+ * 获取所有注册中心
+ *
+ * @return 所有注册中心
+ */
+ public static Collection<Registry> getRegistries() {
+ return Collections.unmodifiableCollection(REGISTRIES.values());
+ }
+
+ /**
+ * 关闭所有已创建注册中心
+ */
+ public static void destroyAll() {
+ if (LOGGER.isInfoEnabled()) {
+ LOGGER.info("Close all registries " + getRegistries());
+ }
+ // 锁定注册中心关闭过程
+ LOCK.lock();
+ try {
+ for (Registry registry : getRegistries()) {
+ try {
+ registry.destroy();
+ } catch (Throwable e) {
+ LOGGER.error(e.getMessage(), e);
+ }
+ }
+ REGISTRIES.clear();
+ } finally {
+ // 释放锁
+ LOCK.unlock();
+ }
+ }
+
+ protected static String getCacheKey(URL url){
+ return url.getProtocol() + "://" + url.getUsername() + ":" + url.getPassword() + "@" + url.getAddress();
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-registry/src/main/java/com/alibaba/dubbo/registry/support/AbstractRegistryService.java b/dubbo-registry/src/main/java/com/alibaba/dubbo/registry/support/AbstractRegistryService.java
new file mode 100644
index 0000000..cf78ba9
--- /dev/null
+++ b/dubbo-registry/src/main/java/com/alibaba/dubbo/registry/support/AbstractRegistryService.java
@@ -0,0 +1,214 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.registry.support;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.logger.Logger;
+import com.alibaba.dubbo.common.logger.LoggerFactory;
+import com.alibaba.dubbo.registry.NotifyListener;
+import com.alibaba.dubbo.registry.RegistryService;
+
+/**
+ * AbstractRegistryService
+ *
+ * @author william.liangf
+ */
+public abstract class AbstractRegistryService implements RegistryService {
+
+ // 日志输出
+ protected final Logger logger = LoggerFactory.getLogger(getClass());
+
+ // 已注册的服务
+ // Map<serviceName, Map<url, queryString>>
+ private final ConcurrentMap<String, List<URL>> registered = new ConcurrentHashMap<String, List<URL>>();
+
+ // 已订阅的服务
+ // Map<serviceName, queryString>
+ private final ConcurrentMap<String, Map<String, String>> subscribed = new ConcurrentHashMap<String, Map<String, String>>();
+
+ // 已通知的服务
+ // Map<serviceName, Map<url, queryString>>
+ private final ConcurrentMap<String, List<URL>> notified = new ConcurrentHashMap<String, List<URL>>();
+
+ // 已订阅服务的监听器列表
+ // Map<serviceName, List<notificationListener>>
+ private final ConcurrentMap<String, List<NotifyListener>> notifyListeners = new ConcurrentHashMap<String, List<NotifyListener>>();
+
+ public void register(URL url) {
+ if (logger.isInfoEnabled()) {
+ logger.info("Register service: " + url.getServiceKey() + ",url:" + url);
+ }
+ register(url.getServiceKey(), url);
+ }
+
+ public void unregister(URL url) {
+ if (logger.isInfoEnabled()) {
+ logger.info("Unregister service: " + url.getServiceKey() + ",url:" + url);
+ }
+ unregister(url.getServiceKey(), url);
+ }
+
+ public void subscribe(URL url, NotifyListener listener) {
+ if (logger.isInfoEnabled()) {
+ logger.info("Subscribe service: " + url.getServiceKey() + ",url:" + url);
+ }
+ subscribe(url.getServiceKey(), url, listener);
+ }
+
+ public void unsubscribe(URL url, NotifyListener listener) {
+ if (logger.isInfoEnabled()) {
+ logger.info("Unsubscribe service: " + url.getServiceKey() + ",url:" + url);
+ }
+ unsubscribe(url.getServiceKey(), url, listener);
+ }
+
+ public List<URL> lookup(URL url) {
+ return getRegistered(url.getServiceKey());
+ }
+
+ public void register(String service, URL url) {
+ if (service == null) {
+ throw new IllegalArgumentException("service == null");
+ }
+ if (url == null) {
+ throw new IllegalArgumentException("url == null");
+ }
+ List<URL> urls = registered.get(service);
+ if (urls == null) {
+ registered.putIfAbsent(service, new CopyOnWriteArrayList<URL>());
+ urls = registered.get(service);
+ }
+ if (! urls.contains(url)) {
+ urls.add(url);
+ }
+ }
+
+ public void unregister(String service, URL url) {
+ if (service == null) {
+ throw new IllegalArgumentException("service == null");
+ }
+ if (url == null) {
+ throw new IllegalArgumentException("url == null");
+ }
+ List<URL> urls = registered.get(service);
+ if (urls != null) {
+ urls.remove(url);
+ }
+ }
+
+ public void subscribe(String service, URL url, NotifyListener listener) {
+ if (service == null) {
+ throw new IllegalArgumentException("service == null");
+ }
+ if (url == null) {
+ throw new IllegalArgumentException("parameters == null");
+ }
+ if (listener == null) {
+ throw new IllegalArgumentException("listener == null");
+ }
+ subscribed.put(service, url.getParameters());
+ List<NotifyListener> listeners = notifyListeners.get(service);
+ if (listeners == null) {
+ notifyListeners.putIfAbsent(service, new CopyOnWriteArrayList<NotifyListener>());
+ listeners = notifyListeners.get(service);
+ }
+ listeners.add(listener);
+ }
+
+ public void unsubscribe(String service, URL url, NotifyListener listener) {
+ if (service == null) {
+ throw new IllegalArgumentException("service == null");
+ }
+ if (url == null) {
+ throw new IllegalArgumentException("parameters == null");
+ }
+ if (listener == null) {
+ throw new IllegalArgumentException("listener == null");
+ }
+ subscribed.remove(service);
+ List<NotifyListener> listeners = notifyListeners.get(service);
+ if (listeners != null) {
+ listeners.remove(listener);
+ }
+ }
+
+ private void doNotify(String service, List<URL> urls) {
+ notified.put(service, urls);
+ List<NotifyListener> listeners = notifyListeners.get(service);
+ if (listeners != null) {
+ for (NotifyListener listener : listeners) {
+ try {
+ notify(service, urls, listener);
+ } catch (Throwable t) {
+ logger.error("Failed to notify registry event, service: " + service + ", urls: " + urls + ", cause: " + t.getMessage(), t);
+ }
+ }
+ }
+ }
+
+ protected void notify(String service, List<URL> urls, NotifyListener listener) {
+ listener.notify(urls);
+ }
+
+ protected final void forbid(String service) {
+ doNotify(service, new ArrayList<URL>(0));
+ }
+
+ protected final void notify(String service, List<URL> urls) {
+ if (service == null || service.length() == 0
+ || urls == null || urls.size() == 0) {
+ return;
+ }
+ doNotify(service, urls);
+ }
+
+ public Map<String, List<URL>> getRegistered() {
+ return Collections.unmodifiableMap(registered);
+ }
+
+ public List<URL> getRegistered(String service) {
+ return Collections.unmodifiableList(registered.get(service));
+ }
+
+ public Map<String, Map<String, String>> getSubscribed() {
+ return Collections.unmodifiableMap(subscribed);
+ }
+
+ public Map<String, String> getSubscribed(String service) {
+ return subscribed.get(service);
+ }
+
+ public Map<String, List<URL>> getNotified() {
+ return Collections.unmodifiableMap(notified);
+ }
+
+ public List<URL> getNotified(String service) {
+ return Collections.unmodifiableList(notified.get(service));
+ }
+
+ public Map<String, List<NotifyListener>> getListeners() {
+ return Collections.unmodifiableMap(notifyListeners);
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-registry/src/main/java/com/alibaba/dubbo/registry/support/CacheRegistry.java b/dubbo-registry/src/main/java/com/alibaba/dubbo/registry/support/CacheRegistry.java
new file mode 100644
index 0000000..726495e
--- /dev/null
+++ b/dubbo-registry/src/main/java/com/alibaba/dubbo/registry/support/CacheRegistry.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.registry.support;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.utils.ConcurrentHashSet;
+import com.alibaba.dubbo.common.utils.UrlUtils;
+import com.alibaba.dubbo.registry.NotifyListener;
+
+/**
+ * CacheRegistry
+ *
+ * @author william.liangf
+ */
+public abstract class CacheRegistry extends FailbackRegistry {
+
+ private final ConcurrentMap<String, Set<String>> notified = new ConcurrentHashMap<String, Set<String>>();
+
+ public CacheRegistry(URL url) {
+ super(url);
+ }
+
+ protected void registered(URL url) {
+ for (Map.Entry<String, Set<NotifyListener>> entry : getSubscribed().entrySet()) {
+ String key = entry.getKey();
+ URL subscribe = URL.valueOf(key);
+ if (UrlUtils.isMatch(subscribe, url)) {
+ Set<String> urls = notified.get(key);
+ if (urls == null) {
+ notified.putIfAbsent(key, new ConcurrentHashSet<String>());
+ urls = notified.get(key);
+ }
+ urls.add(url.toFullString());
+ List<URL> list = toList(urls);
+ for (NotifyListener listener : entry.getValue()) {
+ notify(subscribe, listener, list);
+ synchronized (listener) {
+ listener.notify();
+ }
+ }
+ }
+ }
+ }
+
+ protected void unregistered(URL url) {
+ for (Map.Entry<String, Set<NotifyListener>> entry : getSubscribed().entrySet()) {
+ String key = entry.getKey();
+ URL subscribe = URL.valueOf(key);
+ if (UrlUtils.isMatch(subscribe, url)) {
+ Set<String> urls = notified.get(key);
+ if (urls != null) {
+ urls.remove(url.toFullString());
+ }
+ List<URL> list = toList(urls);
+ for (NotifyListener listener : entry.getValue()) {
+ notify(subscribe, listener, list);
+ }
+ }
+ }
+ }
+
+ protected void subscribed(URL url, NotifyListener listener) {
+ Set<String> urls = notified.get(url.toFullString());
+ if (urls != null && urls.size() > 0) {
+ notify(url, listener, toList(urls));
+ }
+ }
+
+ private List<URL> toList(Set<String> urls) {
+ List<URL> list = new ArrayList<URL>();
+ if (urls != null && urls.size() > 0) {
+ for (String url : urls) {
+ list.add(URL.valueOf(url));
+ }
+ }
+ return list;
+ }
+
+ public List<URL> lookup(URL url) {
+ List<URL> urls= new ArrayList<URL>();
+ for (String r: getRegistered()) {
+ URL u = URL.valueOf(r);
+ if (UrlUtils.isMatch(url, u)) {
+ urls.add(u);
+ }
+ }
+ return urls;
+ }
+
+ public void register(URL url) {
+ 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);
+ }
+
+ public Map<String, Set<String>> getNotified() {
+ return notified;
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-registry/src/main/java/com/alibaba/dubbo/registry/support/FailbackRegistry.java b/dubbo-registry/src/main/java/com/alibaba/dubbo/registry/support/FailbackRegistry.java
new file mode 100644
index 0000000..57177bf
--- /dev/null
+++ b/dubbo-registry/src/main/java/com/alibaba/dubbo/registry/support/FailbackRegistry.java
@@ -0,0 +1,327 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.registry.support;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.utils.ConcurrentHashSet;
+import com.alibaba.dubbo.common.utils.NamedThreadFactory;
+import com.alibaba.dubbo.registry.NotifyListener;
+import com.alibaba.dubbo.rpc.RpcConstants;
+
+/**
+ * FailbackRegistry
+ *
+ * @author william.liangf
+ */
+public abstract class FailbackRegistry extends AbstractRegistry {
+
+ // 重试周期
+ private static final int DEFAULT_RETRY_PERIOD = 5 * 1000;
+
+ // 定时任务执行器
+ private final ScheduledExecutorService retryExecutor = Executors.newScheduledThreadPool(1, new NamedThreadFactory("DubboRegistryFailedRetryTimer", true));
+
+ // 失败重试定时器,定时检查是否有请求失败,如有,无限次重试
+ private final ScheduledFuture<?> retryFuture;
+
+ private final Set<String> failedRegistered = new ConcurrentHashSet<String>();
+
+ private final Set<String> failedUnregistered = new ConcurrentHashSet<String>();
+
+ private final ConcurrentMap<String, Set<NotifyListener>> failedSubscribed = new ConcurrentHashMap<String, Set<NotifyListener>>();
+
+ private final ConcurrentMap<String, Set<NotifyListener>> failedUnsubscribed = new ConcurrentHashMap<String, Set<NotifyListener>>();
+
+ private final ConcurrentMap<String, Map<NotifyListener, List<URL>>> failedNotified = new ConcurrentHashMap<String, Map<NotifyListener, List<URL>>>();
+
+ public FailbackRegistry(URL url) {
+ super(url);
+ int retryPeriod = url.getParameter(RpcConstants.REGISTRY_RETRY_PERIOD_KEY, DEFAULT_RETRY_PERIOD);
+ this.retryFuture = retryExecutor.scheduleWithFixedDelay(new Runnable() {
+ public void run() {
+ // 检测并连接注册中心
+ try {
+ retry();
+ } catch (Throwable t) { // 防御性容错
+ logger.error("Unexpected error occur at failed retry, cause: " + t.getMessage(), t);
+ }
+ }
+ }, retryPeriod, retryPeriod, TimeUnit.MILLISECONDS);
+ }
+
+ // 重试失败的动作
+ private void retry() throws Exception {
+ if (! failedRegistered.isEmpty()) {
+ Set<String> failed = new HashSet<String>(failedRegistered);
+ if (failed.size() > 0) {
+ if (logger.isInfoEnabled()) {
+ logger.info("Retry register " + failed);
+ }
+ try {
+ for (String url : failed) {
+ doRegister(URL.valueOf(url));
+ failedRegistered.remove(url);
+ }
+ } catch (Throwable t) { // 忽略所有异常,等待下次重试
+ logger.warn("Failed to retry register " + failed + ", waiting for again, cause: " + t.getMessage(), t);
+ }
+ }
+ }
+ if(! failedUnregistered.isEmpty()) {
+ Set<String> failed = new HashSet<String>(failedUnregistered);
+ if (failed.size() > 0) {
+ if (logger.isInfoEnabled()) {
+ logger.info("Retry unregister " + failed);
+ }
+ try {
+ for (String url : failed) {
+ doUnregister(URL.valueOf(url));
+ failedUnregistered.remove(url);
+ }
+ } catch (Throwable t) { // 忽略所有异常,等待下次重试
+ logger.warn("Failed to retry unregister " + failed + ", waiting for again, cause: " + t.getMessage(), t);
+ }
+ }
+ }
+ if (! failedSubscribed.isEmpty()) {
+ Map<String, Set<NotifyListener>> failed = new HashMap<String, Set<NotifyListener>>(failedSubscribed);
+ for (Map.Entry<String, Set<NotifyListener>> entry : failed.entrySet()) {
+ if (entry.getValue() == null || entry.getValue().size() == 0) {
+ failed.remove(entry.getKey());
+ }
+ }
+ if (failed.size() > 0) {
+ if (logger.isInfoEnabled()) {
+ logger.info("Retry subscribe " + failed);
+ }
+ try {
+ for (Map.Entry<String, Set<NotifyListener>> entry : failed.entrySet()) {
+ URL url = URL.valueOf(entry.getKey());
+ Set<NotifyListener> listeners = entry.getValue();
+ for (NotifyListener listener : listeners) {
+ doSubscribe(url, listener);
+ listeners.remove(listener);
+ }
+ }
+ } catch (Throwable t) { // 忽略所有异常,等待下次重试
+ logger.warn("Failed to retry subscribe " + failed + ", waiting for again, cause: " + t.getMessage(), t);
+ }
+ }
+ }
+ if (! failedUnsubscribed.isEmpty()) {
+ Map<String, Set<NotifyListener>> failed = new HashMap<String, Set<NotifyListener>>(failedUnsubscribed);
+ for (Map.Entry<String, Set<NotifyListener>> entry : failed.entrySet()) {
+ if (entry.getValue() == null || entry.getValue().size() == 0) {
+ failed.remove(entry.getKey());
+ }
+ }
+ if (failed.size() > 0) {
+ if (logger.isInfoEnabled()) {
+ logger.info("Retry unsubscribe " + failed);
+ }
+ try {
+ for (Map.Entry<String, Set<NotifyListener>> entry : failed.entrySet()) {
+ URL url = URL.valueOf(entry.getKey());
+ Set<NotifyListener> listeners = entry.getValue();
+ for (NotifyListener listener : listeners) {
+ doUnsubscribe(url, listener);
+ listeners.remove(listener);
+ }
+ }
+ } catch (Throwable t) { // 忽略所有异常,等待下次重试
+ logger.warn("Failed to retry unsubscribe " + failed + ", waiting for again, cause: " + t.getMessage(), t);
+ }
+ }
+ }
+ if (! failedNotified.isEmpty()) {
+ Map<String, Map<NotifyListener, List<URL>>> failed = new HashMap<String, Map<NotifyListener, List<URL>>>(failedNotified);
+ for (Map.Entry<String, Map<NotifyListener, List<URL>>> entry : failed.entrySet()) {
+ if (entry.getValue() == null || entry.getValue().size() == 0) {
+ failed.remove(entry.getKey());
+ }
+ }
+ if (failed.size() > 0) {
+ if (logger.isInfoEnabled()) {
+ logger.info("Retry notify " + failed);
+ }
+ try {
+ for (Map<NotifyListener, List<URL>> values : failed.values()) {
+ for (Map.Entry<NotifyListener, List<URL>> entry : values.entrySet()) {
+ 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);
+ }
+ }
+ }
+ doRetry();
+ }
+
+ public void destroy() {
+ super.destroy();
+ try {
+ retryFuture.cancel(true);
+ } catch (Throwable t) {
+ logger.warn(t.getMessage(), t);
+ }
+ }
+
+ public void register(URL url) {
+ super.register(url);
+ try {
+ // 向服务器端发送注册请求
+ doRegister(url);
+ } catch (Exception t) {
+ if (getUrl().getParameter(Constants.CHECK_KEY, true)) { // 如果开启了启动时检测,则直接抛出异常
+ throw new IllegalStateException("Failed to register " + url + ", cause: " + t.getMessage(), t);
+ }
+ // 否则,将失败的注册请求记录到失败列表,定时重试
+ failedRegistered.add(url.toFullString());
+ logger.error("Failed to register " + url + ", waiting for retry, cause: " + t.getMessage(), t);
+ }
+ }
+
+ public void unregister(URL url) {
+ super.unregister(url);
+ try {
+ // 向服务器端发送取消注册请求
+ doUnregister(url);
+ } catch (Exception t) {
+ if (getUrl().getParameter(Constants.CHECK_KEY, true)) { // 如果开启了启动时检测,则直接抛出异常
+ throw new IllegalStateException("Failed to uregister " + url + ", cause: " + t.getMessage(), t);
+ }
+ // 否则,将失败的取消注册请求记录到失败列表,定时重试
+ failedUnregistered.add(url.toFullString());
+ logger.error("Failed to uregister " + url + ", waiting for retry, cause: " + t.getMessage(), t);
+ }
+ }
+
+ public void subscribe(URL url, NotifyListener listener) {
+ super.subscribe(url, listener);
+ try {
+ // 向服务器端发送订阅请求
+ doSubscribe(url, listener);
+ } catch (Exception t) {
+ if (getUrl().getParameter(Constants.CHECK_KEY, true)) { // 如果开启了启动时检测,则直接抛出异常
+ throw new IllegalStateException("Failed to subscribe " + url + ", cause: " + t.getMessage(), t);
+ }
+ // 否则,将失败的订阅请求记录到失败列表,定时重试
+ String key = url.toFullString();
+ Set<NotifyListener> listeners = failedSubscribed.get(key);
+ if (listeners == null) {
+ failedSubscribed.putIfAbsent(key, new ConcurrentHashSet<NotifyListener>());
+ listeners = failedSubscribed.get(key);
+ }
+ listeners.add(listener);
+ logger.error("Failed to subscribe " + url + ", waiting for retry, cause: " + t.getMessage(), t);
+ }
+ }
+
+ public void unsubscribe(URL url, NotifyListener listener) {
+ super.unsubscribe(url, listener);
+ try {
+ // 向服务器端发送取消订阅请求
+ doUnsubscribe(url, listener);
+ } catch (Exception t) {
+ if (getUrl().getParameter(Constants.CHECK_KEY, true)) { // 如果开启了启动时检测,则直接抛出异常
+ throw new IllegalStateException("Failed to unsubscribe " + url + ", cause: " + t.getMessage(), t);
+ }
+ // 否则,将失败的取消订阅请求记录到失败列表,定时重试
+ String key = url.toFullString();
+ Set<NotifyListener> listeners = failedUnsubscribed.get(key);
+ if (listeners == null) {
+ failedUnsubscribed.putIfAbsent(key, new ConcurrentHashSet<NotifyListener>());
+ listeners = failedUnsubscribed.get(key);
+ }
+ listeners.add(listener);
+ logger.error("Failed to unsubscribe " + url + ", waiting for retry, cause: " + t.getMessage(), t);
+ }
+ }
+
+ protected void notify(URL url, NotifyListener listener, List<URL> urls) {
+ if (url == null) {
+ throw new IllegalArgumentException("notify url == null");
+ }
+ if (listener == null) {
+ throw new IllegalArgumentException("notify listener == null");
+ }
+ if (urls == null) {
+ urls = new ArrayList<URL>(0);
+ }
+ try {
+ listener.notify(urls);
+ } catch (Exception t) {
+ // 将失败的通知请求记录到失败列表,定时重试
+ String key = url.toFullString();
+ Map<NotifyListener, List<URL>> values = failedNotified.get(key);
+ if (values == null) {
+ failedNotified.putIfAbsent(key, new ConcurrentHashMap<NotifyListener, List<URL>>());
+ values = failedNotified.get(key);
+ }
+ values.put(listener, urls);
+ logger.error("Failed to unsubscribe " + url + ", waiting for retry, cause: " + t.getMessage(), t);
+ }
+ }
+
+ protected abstract void doRegister(URL url);
+
+ protected abstract void doUnregister(URL url);
+
+ protected abstract void doSubscribe(URL url, NotifyListener listener);
+
+ protected abstract void doUnsubscribe(URL url, NotifyListener listener);
+
+ protected void doRetry() {}
+
+ public Set<String> getFailedRegistered() {
+ return failedRegistered;
+ }
+
+ public Set<String> getFailedUnregistered() {
+ return failedUnregistered;
+ }
+
+ public Map<String, Set<NotifyListener>> getFailedSubscribed() {
+ return failedSubscribed;
+ }
+
+ public Map<String, Set<NotifyListener>> getFailedUnsubscribed() {
+ return failedUnsubscribed;
+ }
+
+ public Map<String, Map<NotifyListener, List<URL>>> getFailedNotified() {
+ return failedNotified;
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-registry/src/main/java/com/alibaba/dubbo/registry/support/RegistryDirectory.java b/dubbo-registry/src/main/java/com/alibaba/dubbo/registry/support/RegistryDirectory.java
new file mode 100644
index 0000000..7c894a0
--- /dev/null
+++ b/dubbo-registry/src/main/java/com/alibaba/dubbo/registry/support/RegistryDirectory.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.registry.support;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.ExtensionLoader;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.Version;
+import com.alibaba.dubbo.common.logger.Logger;
+import com.alibaba.dubbo.common.logger.LoggerFactory;
+import com.alibaba.dubbo.common.utils.NetUtils;
+import com.alibaba.dubbo.common.utils.StringUtils;
+import com.alibaba.dubbo.registry.NotifyListener;
+import com.alibaba.dubbo.registry.Registry;
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.Protocol;
+import com.alibaba.dubbo.rpc.RpcConstants;
+import com.alibaba.dubbo.rpc.RpcException;
+import com.alibaba.dubbo.rpc.cluster.Router;
+import com.alibaba.dubbo.rpc.cluster.RouterFactory;
+import com.alibaba.dubbo.rpc.cluster.router.ScriptRouterFactory;
+import com.alibaba.dubbo.rpc.cluster.support.AbstractDirectory;
+import com.alibaba.dubbo.rpc.cluster.support.ClusterUtils;
+
+/**
+ * RegistryDirectory
+ *
+ * @author william.liangf
+ * @author chao.liuc
+ */
+public class RegistryDirectory<T> extends AbstractDirectory<T> implements NotifyListener {
+
+ private static final Logger logger = LoggerFactory.getLogger(RegistryDirectory.class);
+
+ private volatile boolean forbidden = false;
+
+ private final String serviceKey;
+
+ private final Class<T> serviceType;
+
+ private volatile URL directoryUrl;
+
+ private volatile Map<String, String> queryMap;
+
+ // Map<url, Invoker> cache service url to invoker mapping.
+ private Map<String, Invoker<T>> urlInvokerMap = new ConcurrentHashMap<String, Invoker<T>>();
+
+ // Map<methodName, Invoker> cache service method to invokers mapping.
+ private volatile Map<String, List<Invoker<T>>> methodInvokerMap;
+
+ private volatile Protocol protocol;
+
+ private volatile Registry registry;
+
+ public RegistryDirectory(Class<T> serviceType, URL url) {
+ super(url);
+ if(serviceType == null )
+ throw new IllegalArgumentException("service type is null.");
+ if(url.getServiceKey() == null || url.getServiceKey().length() == 0)
+ throw new IllegalArgumentException("registry serviceKey is null.");
+ this.serviceType = serviceType;
+ this.serviceKey = url.getServiceKey();
+ this.queryMap = StringUtils.parseQueryString(url.getParameterAndDecoded(RpcConstants.REFER_KEY));
+ this.directoryUrl = url.removeParameter(RpcConstants.REFER_KEY).addParameters(queryMap);
+ }
+
+ public void setProtocol(Protocol protocol) {
+ this.protocol = protocol;
+ }
+
+ public void setRegistry(Registry registry) {
+ this.registry = registry;
+ }
+
+ public void destroy() {
+ if(destroyed) {
+ return;
+ }
+ super.destroy();
+ // unsubscribe.
+ try {
+ if(registry != null && registry.isAvailable()) {
+ registry.unsubscribe(directoryUrl, this);
+ }
+ } catch (Throwable t) {
+ logger.warn("unexpeced error when unsubscribe service " + serviceKey + "from registry" + registry.getUrl(), t);
+ }
+ try {
+ destroyAllInvokers();
+ } catch (Throwable t) {
+ logger.warn("Failed to destroy service " + serviceKey, t);
+ }
+ }
+
+ public synchronized void notify(List<URL> urls) {
+ if (urls == null || urls.size() == 0) { // 黑白名单限制
+ this.forbidden = true; // 禁止访问
+ this.methodInvokerMap = null; // 置空列表
+ destroyAllInvokers(); // 关闭所有Invoker
+ } else {
+ this.forbidden = false; // 允许访问
+ List<URL> invokerUrls = new ArrayList<URL>();
+ List<URL> routerUrls = new ArrayList<URL>();
+ for (URL url : urls) {
+ if (RpcConstants.ROUTE_PROTOCOL.equals(url.getProtocol())) {
+ if (! routerUrls.contains(url)) {
+ routerUrls.add(url);
+ }
+ } else if (ExtensionLoader.getExtensionLoader(Protocol.class).hasExtension(url.getProtocol())) {
+ if (! invokerUrls.contains(url)) {
+ invokerUrls.add(url);
+ }
+ } else {
+ logger.error(new IllegalStateException("Unsupported protocol " + url.getProtocol() + " in notified url: " + url + " from registry " + getUrl().getAddress() + " to consumer " + NetUtils.getLocalHost()
+ + ", supported protocol: "+ExtensionLoader.getExtensionLoader(Protocol.class).getSupportedExtensions()));
+ }
+ }
+
+ Map<String, Invoker<T>> newUrlInvokerMap = null ;
+ Map<String, List<Invoker<T>>> newMethodInvokerMap = null ;
+ List<Router> routers = null ;
+ Map<String, Invoker<T>> oldUrlInvokerMap = urlInvokerMap;
+ //transaction convert
+ {
+ routers = toRouters(routerUrls);
+ newUrlInvokerMap = toInvokers(invokerUrls); // 将URL列表转成Invoker列表
+ newMethodInvokerMap = toMethodInvokers(newUrlInvokerMap); // 换方法名映射Invoker列表
+ }
+ // state change
+ {
+ this.methodInvokerMap = newMethodInvokerMap;
+ this.urlInvokerMap = newUrlInvokerMap;
+ if(routers != null){ // null - do nothing
+ setRouters(routers);
+ }
+ try{
+ destroyUnusedInvokers(oldUrlInvokerMap,newUrlInvokerMap); // 关闭未使用的Invoker
+ }catch (Exception e) {
+ logger.warn("destroyUnusedInvokers error. ", e);
+ }
+ }
+ }
+ }
+
+ /**
+ *
+ * @param urls
+ * @return null : no routers ,do nothing
+ * else :routers list
+ */
+ private List<Router> toRouters(List<URL> urls) {
+ //no router urls , do nothing
+ if(urls == null || urls.size() < 1){
+ return null ;
+ }
+ List<Router> routers = new ArrayList<Router>();
+
+ // on these conditions: clear all current routers
+ // 1. there is only one route url
+ // 2. with type = clear
+ if(urls.size() == 1){
+ URL u = urls.get(0);
+ // clean current routers
+ if(RpcConstants.ROUTER_TYPE_CLEAR.equals(u.getParameter(RpcConstants.ROUTER_KEY))){
+ return routers;
+ }
+ }
+
+ if (urls != null && urls.size() > 0) {
+ for (URL url : urls) {
+ String router_type = url.getParameter(RpcConstants.ROUTER_KEY, ScriptRouterFactory.NAME);
+ if (router_type == null || router_type.length() == 0){
+ logger.warn("Router url:\"" + url.toString() + "\" does not contain " + RpcConstants.ROUTER_KEY + ", router creation ignored!");
+ continue;
+ }
+ try{
+ Router router = ExtensionLoader.getExtensionLoader(RouterFactory.class).getExtension(router_type).getRouter(url);
+ if (!routers.contains(router))
+ routers.add(router);
+ }catch (Throwable t) {
+ logger.error("convert router url to router error, url: "+ url, t);
+ }
+ }
+ }
+ return routers;
+ }
+
+ /**
+ * 将urls转成invokers
+ *
+ * @param urls
+ * @param query
+ * @return invokers
+ */
+ private Map<String, Invoker<T>> toInvokers(List<URL> urls) {
+ if(urls == null || urls.size() == 0){
+ return null;
+ }
+ Map<String, Invoker<T>> newUrlInvokerMap = new ConcurrentHashMap<String, Invoker<T>>();
+ Set<String> keys = new HashSet<String>();
+ for (URL url : urls) {
+ String key = url.toFullString(); // URL参数是排序的
+ if (keys.contains(key)) { // 重复URL
+ continue;
+ }
+ keys.add(key);
+ // 缓存key为没有合并消费端参数的URL,不管消费端如何合并参数,如果服务端URL发生变化,则重新refer
+ Invoker<T> invoker = urlInvokerMap.get(key);
+ if (invoker == null) { // 缓存中没有,重新refer
+ try {
+ if ((url.getPath() == null || url.getPath().length() == 0)
+ && "dubbo".equals(url.getProtocol())) { // 兼容1.0
+ //fix by tony.chenl DUBBO-44
+ String path = directoryUrl.getParameter(Constants.INTERFACE_KEY);
+ int i = path.indexOf('/');
+ if (i >= 0) {
+ path = path.substring(i + 1);
+ }
+ i = path.lastIndexOf(':');
+ if (i >= 0) {
+ path = path.substring(0, i);
+ }
+ url = url.setPath(path);
+ }
+ url = ClusterUtils.mergeUrl(url, queryMap); // 合并消费端参数
+ this.directoryUrl = this.directoryUrl.addParametersIfAbsent(url.getParameters()); // 合并提供者参数
+ url = url.addParameter(Constants.CHECK_KEY, String.valueOf(false)); // 不检查连接是否成功,总是创建Invoker!
+ invoker = protocol.refer(serviceType, url);
+ } catch (Throwable t) {
+ logger.error("Failed to refer invoker for interface:"+serviceType+",url:("+url+")" + t.getMessage(), t);
+ }
+ if (invoker != null) { // 将新的引用放入缓存
+ newUrlInvokerMap.put(key, invoker);
+ }
+ }else {
+ newUrlInvokerMap.put(key, invoker);
+ }
+ }
+ keys.clear();
+ return newUrlInvokerMap;
+ }
+
+ /**
+ * 将invokers列表转成与方法的映射关系
+ *
+ * @param invokersMap Invoker列表
+ * @return Invoker与方法的映射关系
+ */
+ private Map<String, List<Invoker<T>>> toMethodInvokers(Map<String, Invoker<T>> invokersMap) {
+ Map<String, List<Invoker<T>>> methodInvokerMap = new HashMap<String, List<Invoker<T>>>();
+ if (invokersMap != null && invokersMap.size() > 0) {
+ List<Invoker<T>> invokersList = new ArrayList<Invoker<T>>();
+ for (Invoker<T> invoker : invokersMap.values()) {
+ String parameter = invoker.getUrl().getParameter(Constants.METHODS_KEY);
+ if (parameter != null && parameter.length() > 0) {
+ String[] methods = Constants.COMMA_SPLIT_PATTERN.split(parameter);
+ if (methods != null && methods.length > 0) {
+ for (String method : methods) {
+ if (method != null && method.length() > 0
+ && ! Constants.ANY_VALUE.equals(method)) {
+ List<Invoker<T>> methodInvokers = methodInvokerMap.get(method);
+ if (methodInvokers == null) {
+ methodInvokers = new ArrayList<Invoker<T>>();
+ methodInvokerMap.put(method, methodInvokers);
+ }
+ methodInvokers.add(invoker);
+ }
+ }
+ }
+ }
+ invokersList.add(invoker);
+ }
+ methodInvokerMap.put(Constants.ANY_VALUE, invokersList);
+ }
+ // sort and unmodifiable
+ for (String method : new HashSet<String>(methodInvokerMap.keySet())) {
+ List<Invoker<T>> methodInvokers = methodInvokerMap.get(method);
+ Collections.sort(methodInvokers, InvokerComparator.getComparator());
+ methodInvokerMap.put(method, Collections.unmodifiableList(methodInvokers));
+ }
+ return Collections.unmodifiableMap(methodInvokerMap);
+ }
+
+ /**
+ * 关闭所有Invoker
+ */
+ private void destroyAllInvokers() {
+ if(urlInvokerMap != null) {
+ for (Invoker<T> invoker : new ArrayList<Invoker<T>>(urlInvokerMap.values())) {
+ try {
+ invoker.destroy();
+ } catch (Throwable t) {
+ logger.warn("Failed to destroy service " + serviceKey + " to provider " + invoker.getUrl(), t);
+ }
+ }
+ urlInvokerMap.clear();
+ }
+ methodInvokerMap = null;
+ }
+
+ /**
+ * 检查缓存中的invoker是否需要被destroy
+ * 如果url中指定refer.autodestroy=false,则只增加不减少,可能会有refer泄漏,
+ *
+ * @param invokers
+ */
+ private void destroyUnusedInvokers(Map<String, Invoker<T>> oldUrlInvokerMap, Map<String, Invoker<T>> newUrlInvokerMap) {
+ if (newUrlInvokerMap == null || newUrlInvokerMap.size() == 0) {
+ destroyAllInvokers();
+ return;
+ }
+ // check deleted invoker
+ List<String> deleted = null;
+ if (oldUrlInvokerMap != null) {
+ Collection<Invoker<T>> newInvokers = newUrlInvokerMap.values();
+ for (Map.Entry<String, Invoker<T>> entry : oldUrlInvokerMap.entrySet()){
+ if (! newInvokers.contains(entry.getValue())) {
+ if (deleted == null) {
+ deleted = new ArrayList<String>();
+ }
+ deleted.add(entry.getKey());
+ }
+ }
+ }
+
+ if (deleted != null) {
+ for (String url : deleted){
+ if (url != null ) {
+ Invoker<T> invoker = oldUrlInvokerMap.remove(url);
+ if (invoker != null) {
+ try {
+ invoker.destroy();
+ if(logger.isDebugEnabled()){
+ logger.debug("destory invoker["+invoker.getUrl()+"] success. ");
+ }
+ } catch (Exception e) {
+ logger.warn("destory invoker["+invoker.getUrl()+"] faild. " + e.getMessage(), e);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ public List<Invoker<T>> doList(Invocation invocation) {
+ if (forbidden) {
+ throw new RpcException(RpcException.FORBIDDEN_EXCEPTION, "Forbid consumer " + NetUtils.getLocalHost() + " access service " + getInterface().getName() + " from registry " + getUrl().getAddress() + " use dubbo version " + Version.getVersion() + ", Please check registry access list (whitelist/blacklist).");
+ }
+ List<Invoker<T>> invokers = null;
+ if (methodInvokerMap != null && methodInvokerMap.size() > 0) {
+ String methodName = invocation.getMethodName();
+ Object[] args = invocation.getArguments();
+
+ // Generic invoke: Object $invoke(String method, String[] parameterTypes, Object[] args) throws GenericException;
+ if (Constants.$INVOKE.equals(methodName)
+ && args != null && args.length == 3
+ && args[0] instanceof String
+ && args[2] instanceof Object[]) {
+ methodName = (String) args[0];
+ args = (Object[]) args[2];
+ }
+ if(args != null && args.length > 0 && args[0] != null
+ && (args[0] instanceof String || args[0].getClass().isEnum())) {
+ invokers = methodInvokerMap.get(methodName + "." + args[0]); // 可根据第一个参数枚举路由
+ }
+ if(invokers == null) {
+ invokers = methodInvokerMap.get(methodName);
+ }
+ if(invokers == null) {
+ invokers = methodInvokerMap.get(Constants.ANY_VALUE);
+ }
+ if(invokers == null) {
+ Iterator<List<Invoker<T>>> iterator = methodInvokerMap.values().iterator();
+ if (iterator.hasNext()) {
+ invokers = iterator.next();
+ }
+ }
+ }
+ return invokers == null ? new ArrayList<Invoker<T>>(0) : invokers;
+ }
+
+ public Class<T> getInterface() {
+ return serviceType;
+ }
+
+ public URL getUrl() {
+ return directoryUrl;
+ }
+
+ public boolean isAvailable() {
+ if (destroyed) return false;
+ Map<String, Invoker<T>> map = urlInvokerMap;
+ if (map != null && map.size() > 0) {
+ for (Invoker<T> invoker : new ArrayList<Invoker<T>>(map.values())) {
+ if (invoker.isAvailable()) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Haomin: added for test purpose
+ */
+ public Map<String, Invoker<T>> getUrlInvokerMap(){
+ return urlInvokerMap;
+ }
+
+ /**
+ * Haomin: added for test purpose
+ */
+ public Map<String, List<Invoker<T>>> getMethodInvokerMap(){
+ return methodInvokerMap;
+ }
+
+ private static class InvokerComparator implements Comparator<Invoker<?>> {
+
+ private static final InvokerComparator comparator = new InvokerComparator();
+
+ public static InvokerComparator getComparator() {
+ return comparator;
+ }
+
+ private InvokerComparator() {}
+
+ public int compare(Invoker<?> o1, Invoker<?> o2) {
+ return o1.getUrl().toString().compareTo(o2.getUrl().toString());
+ }
+
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-registry/src/main/java/com/alibaba/dubbo/registry/support/RegistryProtocol.java b/dubbo-registry/src/main/java/com/alibaba/dubbo/registry/support/RegistryProtocol.java
new file mode 100644
index 0000000..177902a
--- /dev/null
+++ b/dubbo-registry/src/main/java/com/alibaba/dubbo/registry/support/RegistryProtocol.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.registry.support;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.utils.NetUtils;
+import com.alibaba.dubbo.registry.Registry;
+import com.alibaba.dubbo.registry.RegistryFactory;
+import com.alibaba.dubbo.rpc.Exporter;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.Protocol;
+import com.alibaba.dubbo.rpc.RpcConstants;
+import com.alibaba.dubbo.rpc.RpcException;
+import com.alibaba.dubbo.rpc.cluster.Cluster;
+import com.alibaba.dubbo.rpc.protocol.InvokerWrapper;
+
+/**
+ * RegistryProtocol
+ *
+ * @author william.liangf
+ */
+@Extension(Constants.REGISTRY_PROTOCOL)
+public class RegistryProtocol implements Protocol {
+
+ private Cluster cluster;
+
+ public void setCluster(Cluster cluster) {
+ this.cluster = cluster;
+ }
+
+ private Protocol protocol;
+
+ public void setProtocol(Protocol protocol) {
+ this.protocol = protocol;
+ }
+
+ private RegistryFactory registryFactory;
+
+ public void setRegistryFactory(RegistryFactory registryFactory) {
+ this.registryFactory = registryFactory;
+ }
+
+ public int getDefaultPort() {
+ return 9090;
+ }
+
+ private final Map<String, Exporter<?>> bounds = new ConcurrentHashMap<String, Exporter<?>>();
+
+ @SuppressWarnings({ "unchecked", "rawtypes" })
+ public <T> Exporter<T> export(final Invoker<T> invoker) throws RpcException {
+ String export = invoker.getUrl().getParameterAndDecoded(RpcConstants.EXPORT_KEY);
+ if (export == null || export.length() == 0) {
+ throw new IllegalArgumentException("The registry export url is null! registry: " + invoker.getUrl());
+ }
+
+ URL url = URL.valueOf(export);
+ final String key = url.removeParameters("dynamic", "enabled").toFullString();
+ Exporter<T> exporter = (Exporter) bounds.get(key);
+ if (exporter == null) {
+ synchronized (bounds) {
+ exporter = (Exporter) bounds.get(key);
+ if (exporter == null) {
+ exporter = protocol.export(new InvokerWrapper<T>(invoker, url));
+ bounds.put(key, exporter);
+ }
+ }
+ }
+
+ URL registryUrl = invoker.getUrl();
+ if (Constants.REGISTRY_PROTOCOL.equals(registryUrl.getProtocol())) {
+ String protocol = registryUrl.getParameter(Constants.REGISTRY_KEY, Constants.DEFAULT_DIRECTORY);
+ registryUrl = registryUrl.setProtocol(protocol).removeParameter(Constants.REGISTRY_KEY);
+ }
+ final Exporter<T> serviceExporter = exporter;
+ final URL serviceUrl = url.removeParameters(getFilteredKeys(url));
+ final Registry registry = registryFactory.getRegistry(registryUrl);
+ registry.register(serviceUrl);
+
+ return new Exporter<T>() {
+ public Invoker<T> getInvoker() {
+ return invoker;
+ }
+ public void unexport() {
+ bounds.remove(key);
+ try {
+ registry.unregister(serviceUrl);
+ } finally {
+ serviceExporter.unexport();
+ }
+ }
+ };
+ }
+
+ public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {
+ url = url.setProtocol(url.getParameter(Constants.REGISTRY_KEY, Constants.DEFAULT_REGISTRY)).removeParameter(Constants.REGISTRY_KEY);
+ Registry registry = registryFactory.getRegistry(url);
+ RegistryDirectory<T> directory = new RegistryDirectory<T>(type, url);
+ directory.setRegistry(registry);
+ directory.setProtocol(protocol);
+ registry.subscribe(directory.getUrl().setHost(NetUtils.getLocalHost()), directory);
+ return cluster.merge(directory);
+ }
+
+ //过滤URL中不需要输出的参数(以点号开头的)
+ private static String[] getFilteredKeys(URL url) {
+ Map<String, String> params = url.getParameters();
+ if (params != null && !params.isEmpty()) {
+ List<String> filteredKeys = new ArrayList<String>();
+ for (Map.Entry<String, String> entry : params.entrySet()) {
+ if (entry != null && entry.getKey() != null && entry.getKey().startsWith(Constants.HIDE_KEY_PREFIX)) {
+ filteredKeys.add(entry.getKey());
+ }
+ }
+ return filteredKeys.toArray(new String[filteredKeys.size()]);
+ } else {
+ return new String[] {};
+ }
+ }
+
+ public void destroy() {
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-registry/src/main/java/com/alibaba/dubbo/registry/support/RegistryStatusChecker.java b/dubbo-registry/src/main/java/com/alibaba/dubbo/registry/support/RegistryStatusChecker.java
new file mode 100644
index 0000000..b3577b8
--- /dev/null
+++ b/dubbo-registry/src/main/java/com/alibaba/dubbo/registry/support/RegistryStatusChecker.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.registry.support;
+
+import java.util.Collection;
+
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.common.status.Status;
+import com.alibaba.dubbo.common.status.StatusChecker;
+import com.alibaba.dubbo.registry.Registry;
+
+/**
+ * RegistryStatusChecker
+ *
+ * @author william.liangf
+ */
+@Extension("registry")
+public class RegistryStatusChecker implements StatusChecker {
+
+ public Status check() {
+ Collection<Registry> regsitries = AbstractRegistryFactory.getRegistries();
+ if (regsitries == null || regsitries.size() == 0) {
+ return new Status(Status.Level.UNKNOWN);
+ }
+ Status.Level level = Status.Level.OK;
+ StringBuilder buf = new StringBuilder();
+ for (Registry registry : regsitries) {
+ if (buf.length() > 0) {
+ buf.append(",");
+ }
+ buf.append(registry.getUrl().getAddress());
+ if (! registry.isAvailable()) {
+ level = Status.Level.ERROR;
+ buf.append("(disconnected)");
+ } else {
+ buf.append("(connected)");
+ }
+ }
+ return new Status(level, buf.toString());
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-registry/src/main/java/com/alibaba/dubbo/registry/support/SimpleRegistryExporter.java b/dubbo-registry/src/main/java/com/alibaba/dubbo/registry/support/SimpleRegistryExporter.java
new file mode 100644
index 0000000..b806b81
--- /dev/null
+++ b/dubbo-registry/src/main/java/com/alibaba/dubbo/registry/support/SimpleRegistryExporter.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.registry.support;
+
+import java.io.IOException;
+import java.net.ServerSocket;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.ExtensionLoader;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.utils.NetUtils;
+import com.alibaba.dubbo.registry.RegistryService;
+import com.alibaba.dubbo.rpc.Exporter;
+import com.alibaba.dubbo.rpc.Protocol;
+import com.alibaba.dubbo.rpc.ProxyFactory;
+import com.alibaba.dubbo.rpc.RpcConstants;
+
+/**
+ * SimpleRegistryExporter
+ *
+ * @author william.liangf
+ */
+public class SimpleRegistryExporter {
+
+ private static final Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
+
+ private static final ProxyFactory proxyFactory = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();
+
+ public synchronized static Exporter<RegistryService> exportIfAbsent(int port) {
+ try {
+ new ServerSocket(port).close();
+ return export(port);
+ } catch (IOException e) {
+ return null;
+ }
+ }
+
+ public static Exporter<RegistryService> export(int port) {
+ return export(port, new SimpleRegistryService());
+ }
+
+ public static Exporter<RegistryService> export(int port, RegistryService registryService) {
+ return protocol.export(proxyFactory.getInvoker(registryService, RegistryService.class,
+ new URL("dubbo", NetUtils.getLocalHost(), port, RegistryService.class.getName())
+ .setPath(RegistryService.class.getName())
+ .addParameter(Constants.INTERFACE_KEY, RegistryService.class.getName())
+ .addParameter(RpcConstants.CLUSTER_STICKY_KEY, "true")
+ .addParameter(RpcConstants.CALLBACK_INSTANCES_LIMIT_KEY, "1000")
+ .addParameter("ondisconnect", "disconnect")
+ .addParameter("subscribe.1.callback", "true")
+ .addParameter("unsubscribe.1.callback", "false")));
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-registry/src/main/java/com/alibaba/dubbo/registry/support/SimpleRegistryService.java b/dubbo-registry/src/main/java/com/alibaba/dubbo/registry/support/SimpleRegistryService.java
new file mode 100644
index 0000000..5dc3faf
--- /dev/null
+++ b/dubbo-registry/src/main/java/com/alibaba/dubbo/registry/support/SimpleRegistryService.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.registry.support;
+
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.logger.Logger;
+import com.alibaba.dubbo.common.logger.LoggerFactory;
+import com.alibaba.dubbo.common.utils.NetUtils;
+import com.alibaba.dubbo.common.utils.UrlUtils;
+import com.alibaba.dubbo.registry.NotifyListener;
+import com.alibaba.dubbo.registry.RegistryService;
+import com.alibaba.dubbo.rpc.RpcContext;
+
+/**
+ * SimpleRegistryService
+ *
+ * @author william.liangf
+ */
+public class SimpleRegistryService extends AbstractRegistryService {
+
+ private final ConcurrentMap<String, ConcurrentMap<String, URL>> remoteRegistered = new ConcurrentHashMap<String, ConcurrentMap<String, URL>>();
+
+ private final ConcurrentMap<String, ConcurrentMap<String, NotifyListener>> remoteListeners = new ConcurrentHashMap<String, ConcurrentMap<String, NotifyListener>>();
+
+ private final static Logger logger = LoggerFactory.getLogger(SimpleRegistryService.class);
+
+ private List<String> registries;
+
+ @Override
+ public void register(String service, URL url) {
+ super.register(service, url);
+ String client = RpcContext.getContext().getRemoteAddressString();
+ Map<String, URL> urls = remoteRegistered.get(client);
+ if (urls == null) {
+ remoteRegistered.putIfAbsent(client, new ConcurrentHashMap<String, URL>());
+ urls = remoteRegistered.get(client);
+ }
+ urls.put(service, url);
+ notify(service, getRegistered().get(service));
+ }
+
+ @Override
+ public void unregister(String service, URL url) {
+ super.unregister(service, url);
+ String client = RpcContext.getContext().getRemoteAddressString();
+ Map<String, URL> urls = remoteRegistered.get(client);
+ if (urls != null && urls.size() > 0) {
+ urls.remove(service);
+ }
+ notify(service, getRegistered().get(service));
+ }
+
+ @Override
+ public void subscribe(String service, URL url, NotifyListener listener) {
+ String client = RpcContext.getContext().getRemoteAddressString();
+ if (logger.isInfoEnabled()){
+ logger.info("[subscribe] service: "+service + ",client:"+ client);
+ }
+ List<URL> urls = getRegistered().get(service);
+ if ((RegistryService.class.getName() + ":0.0.0").equals(service)
+ && (urls == null || urls.size() == 0)) {
+ register(service, new URL("dubbo",
+ NetUtils.getLocalHost(),
+ RpcContext.getContext().getLocalPort(),
+ com.alibaba.dubbo.registry.RegistryService.class.getName(),
+ url.getParameters()));
+ List<String> rs = registries;
+ if (rs != null && rs.size() > 0) {
+ for (String registry : rs) {
+ register(service, UrlUtils.parseURL(registry, url.getParameters()));
+ }
+ }
+ }
+ super.subscribe(service, url, listener);
+
+ Map<String, NotifyListener> listeners = remoteListeners.get(client);
+ if (listeners == null) {
+ remoteListeners.putIfAbsent(client, new ConcurrentHashMap<String, NotifyListener>());
+ listeners = remoteListeners.get(client);
+ }
+ listeners.put(service, listener);
+ urls = getRegistered().get(service);
+ if (urls != null && urls.size() > 0) {
+ listener.notify(urls);
+ }
+
+
+ }
+
+ @Override
+ public void unsubscribe(String service, URL url, NotifyListener listener) {
+ super.unsubscribe(service, url, listener);
+ String client = RpcContext.getContext().getRemoteAddressString();
+ Map<String, NotifyListener> listeners = remoteListeners.get(client);
+ if (listeners != null && listeners.size() > 0) {
+ listeners.remove(service);
+ }
+ List<URL> urls = getRegistered().get(service);
+ if (urls != null && urls.size() > 0) {
+ listener.notify(urls);
+ }
+ }
+
+ public void disconnect() {
+ String client = RpcContext.getContext().getRemoteAddressString();
+ if (logger.isInfoEnabled()) {
+ logger.info("Disconnected " + client);
+ }
+ ConcurrentMap<String, URL> urls = remoteRegistered.get(client);
+ if (urls != null && urls.size() > 0) {
+ for (Map.Entry<String, URL> entry : urls.entrySet()) {
+ super.unregister(entry.getKey(), entry.getValue());
+ }
+ }
+ Map<String, NotifyListener> listeners = remoteListeners.get(client);
+ if (listeners != null && listeners.size() > 0) {
+ for (Map.Entry<String, NotifyListener> entry : listeners.entrySet()) {
+ String service = entry.getKey();
+ super.unsubscribe(service, new URL("subscribe",
+ RpcContext.getContext().getRemoteHost(),
+ RpcContext.getContext().getRemotePort(),
+ com.alibaba.dubbo.registry.RegistryService.class.getName(), getSubscribed(service)), entry.getValue());
+ }
+ }
+ }
+
+ public List<String> getRegistries() {
+ return registries;
+ }
+
+ public void setRegistries(List<String> registries) {
+ this.registries = registries;
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-registry/src/main/resources/META-INF/services/com.alibaba.dubbo.common.status.StatusChecker b/dubbo-registry/src/main/resources/META-INF/services/com.alibaba.dubbo.common.status.StatusChecker
new file mode 100644
index 0000000..58920c6
--- /dev/null
+++ b/dubbo-registry/src/main/resources/META-INF/services/com.alibaba.dubbo.common.status.StatusChecker
@@ -0,0 +1 @@
+com.alibaba.dubbo.registry.support.RegistryStatusChecker
\ No newline at end of file
diff --git a/dubbo-registry/src/main/resources/META-INF/services/com.alibaba.dubbo.rpc.Protocol b/dubbo-registry/src/main/resources/META-INF/services/com.alibaba.dubbo.rpc.Protocol
new file mode 100644
index 0000000..c210ed7
--- /dev/null
+++ b/dubbo-registry/src/main/resources/META-INF/services/com.alibaba.dubbo.rpc.Protocol
@@ -0,0 +1 @@
+com.alibaba.dubbo.registry.support.RegistryProtocol
\ No newline at end of file
diff --git a/dubbo-registry/src/test/java/com/alibaba/dubbo/registry/PerformanceRegistryTest.java b/dubbo-registry/src/test/java/com/alibaba/dubbo/registry/PerformanceRegistryTest.java
new file mode 100644
index 0000000..832aaa5
--- /dev/null
+++ b/dubbo-registry/src/test/java/com/alibaba/dubbo/registry/PerformanceRegistryTest.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.registry;
+
+import junit.framework.TestCase;
+
+import org.junit.Test;
+
+import com.alibaba.dubbo.common.ExtensionLoader;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.logger.Logger;
+import com.alibaba.dubbo.common.logger.LoggerFactory;
+import com.alibaba.dubbo.common.utils.NetUtils;
+
+/**
+ * RegistryPerformanceTest
+ *
+ * @author william.liangf
+ */
+public class PerformanceRegistryTest extends TestCase {
+
+ private static final Logger logger = LoggerFactory.getLogger(PerformanceRegistryTest.class);
+
+ @Test
+ public void testRegistry() {
+ // 读取参数
+ if (PerformanceUtils.getProperty("server", null) == null) {
+ logger.warn("Please set -Dserver=127.0.0.1:9090");
+ return;
+ }
+ final int base = PerformanceUtils.getIntProperty("base", 0);
+ final int concurrent = PerformanceUtils.getIntProperty("concurrent", 100);
+ int r = PerformanceUtils.getIntProperty("runs", 1000);
+ final int runs = r > 0 ? r : Integer.MAX_VALUE;
+ final Registry registry = ExtensionLoader.getExtensionLoader(RegistryFactory.class).getAdaptiveExtension().getRegistry(URL.valueOf("remote://admin:hello1234@" + PerformanceUtils.getProperty("server", "10.20.153.28:9090")));
+ for (int i = 0; i < concurrent; i ++) {
+ final int t = i;
+ new Thread(new Runnable() {
+ public void run() {
+ for (int j = 0; j < runs; j ++) {
+ registry.register(URL.valueOf("remote://" + NetUtils.getLocalHost() + ":8080/demoService" + t + "_" + j + "?version=1.0.0&application=demo&dubbo=2.0&interface=" + "com.alibaba.dubbo.demo.DemoService" + (base + t) + "_" + (base + j)));
+ }
+ }
+ }).start();
+ }
+ synchronized (PerformanceRegistryTest.class) {
+ while (true) {
+ try {
+ PerformanceRegistryTest.class.wait();
+ } catch (InterruptedException e) {
+ }
+ }
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-registry/src/test/java/com/alibaba/dubbo/registry/PerformanceUtils.java b/dubbo-registry/src/test/java/com/alibaba/dubbo/registry/PerformanceUtils.java
new file mode 100644
index 0000000..1e6683c
--- /dev/null
+++ b/dubbo-registry/src/test/java/com/alibaba/dubbo/registry/PerformanceUtils.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.registry;
+
+import java.net.NetworkInterface;
+import java.net.SocketException;
+import java.text.DecimalFormat;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.List;
+
+/**
+ * PerformanceUtils
+ *
+ * @author william.liangf
+ */
+public class PerformanceUtils {
+
+ public static String getProperty(String key, String defaultValue) {
+ String value = System.getProperty(key);
+ if (value == null || value.trim().length() == 0 || value.startsWith("$")) {
+ return defaultValue;
+ }
+ return value.trim();
+ }
+
+ public static int getIntProperty(String key, int defaultValue) {
+ String value = System.getProperty(key);
+ if (value == null || value.trim().length() == 0 || value.startsWith("$")) {
+ return defaultValue;
+ }
+ return Integer.parseInt(value.trim());
+ }
+ public static boolean getBooleanProperty(String key, boolean defaultValue) {
+ String value = System.getProperty(key);
+ if (value == null || value.trim().length() == 0 || value.startsWith("$")) {
+ return defaultValue;
+ }
+ return Boolean.parseBoolean(value.trim());
+ }
+
+ public static List<String> getEnvironment() {
+ List<String> environment = new ArrayList<String>();
+ environment.add("OS: " + System.getProperty("os.name") + " " + System.getProperty("os.version") + " " + System.getProperty("os.arch", ""));
+ environment.add("CPU: " + Runtime.getRuntime().availableProcessors() + " cores");
+ environment.add("JVM: " + System.getProperty("java.vm.name") + " " + System.getProperty("java.runtime.version"));
+ environment.add("Memory: " + DecimalFormat.getIntegerInstance().format(Runtime.getRuntime().totalMemory())
+ + " bytes (Max: " + DecimalFormat.getIntegerInstance().format(Runtime.getRuntime().maxMemory()) + " bytes)");
+ NetworkInterface ni = PerformanceUtils.getNetworkInterface();
+ if (ni != null) {
+ environment.add("Network: " + ni.getDisplayName());
+ }
+ return environment;
+ }
+
+ private static final int WIDTH = 64;
+
+ public static void printSeparator() {
+ StringBuilder pad = new StringBuilder();
+ for (int i = 0; i < WIDTH; i ++) {
+ pad.append("-");
+ }
+ System.out.println("+" + pad + "+");
+ }
+
+ public static void printBorder() {
+ StringBuilder pad = new StringBuilder();
+ for (int i = 0; i < WIDTH; i ++) {
+ pad.append("=");
+ }
+ System.out.println("+" + pad + "+");
+ }
+
+ public static void printBody(String msg) {
+ StringBuilder pad = new StringBuilder();
+ int len = WIDTH - msg.length() - 1;
+ if (len > 0) {
+ for (int i = 0; i < len; i ++) {
+ pad.append(" ");
+ }
+ }
+ System.out.println("| " + msg + pad + "|");
+ }
+
+ public static void printHeader(String msg) {
+ StringBuilder pad = new StringBuilder();
+ int len = WIDTH - msg.length();
+ if (len > 0) {
+ int half = len / 2;
+ for (int i = 0; i < half; i ++) {
+ pad.append(" ");
+ }
+ }
+ System.out.println("|" + pad + msg + pad + ((len % 2 == 0) ? "" : " ") + "|");
+ }
+
+ public static NetworkInterface getNetworkInterface() {
+ try {
+ Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces();
+ if (interfaces != null) {
+ while (interfaces.hasMoreElements()) {
+ try {
+ return interfaces.nextElement();
+ } catch (Throwable e) {
+ }
+ }
+ }
+ } catch (SocketException e) {
+ }
+ return null;
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-registry/src/test/java/com/alibaba/dubbo/registry/RegistryTestSupport.java b/dubbo-registry/src/test/java/com/alibaba/dubbo/registry/RegistryTestSupport.java
new file mode 100644
index 0000000..d5ae6c0
--- /dev/null
+++ b/dubbo-registry/src/test/java/com/alibaba/dubbo/registry/RegistryTestSupport.java
@@ -0,0 +1,215 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.registry;
+
+
+/**
+ * @author ding.lid
+ */
+public class RegistryTestSupport {
+ /*public static <T> void assertEqualsIgnoreOrde(Collection<T> expected, Collection<T> actual) {
+ Set<T> expectedSet;
+ if (expected instanceof Set) {
+ expectedSet = (Set<T>) expected;
+ } else {
+ expectedSet = new HashSet<T>(expected);
+ }
+
+ Set<T> actualSet;
+ if (actual instanceof Set) {
+ actualSet = (Set<T>) actual;
+ } else {
+ actualSet = new HashSet<T>(actual);
+ }
+
+ Assert.assertEquals(expectedSet, actualSet);
+ }
+
+ public static final String member_service_name = "com.alibaba.morgan.MemberService";
+
+ public static final Map<String, String> member_urls1;
+ static {
+ Map<String, String> m = new HashMap<String, String>();
+ m.put("remote://10.20.130.230:9090/memberService", "version=1.0.0");
+
+ member_urls1 = Collections.unmodifiableMap(m);
+ }
+
+ public static final Map<String, String> member_urls2;
+ static {
+ Map<String, String> m = new HashMap<String, String>();
+ m.put("remote://11.11.11.11:9090/memberService", "version=1.0.0");
+ m.put("remote://22.22.22.22:9090/memberService", "version=1.0.0");
+ m.put("remote://33.33.33.33:9090/memberService", "version=1.0.0");
+
+ member_urls2 = Collections.unmodifiableMap(m);
+ }
+
+ public static final Map<String, String> member_urls3;
+ static {
+ Map<String, String> m = new HashMap<String, String>();
+ m.put("remote://44.44.44.44:9090/memberService", "version=1.0.0");
+ member_urls3 = Collections.unmodifiableMap(m);
+ }
+
+ public static final String xxx_service_name = "com.alibaba.xxx.XxxService";
+
+ public static final Map<String, String> xxx_urls1;
+ static {
+ Map<String, String> m = new HashMap<String, String>();
+ m.put("remote://11.11.11.11:9090/xxxService", "version=1.0.0");
+ m.put("remote://22.22.22.22:9090/xxxService", "version=1.0.0");
+
+ xxx_urls1 = Collections.unmodifiableMap(m);
+ }
+
+ public static void registerCheck(AbstractRegistry registry) {
+ {
+ List<URL> registeredList = registry
+ .getRegistered("com.alibaba.morgan.MemberService");
+ assertEquals(0, registeredList.size());
+
+ Map<String, Map<String, String>> registeredMap = registry.getRegistered();
+ assertEquals(0, registeredMap.size());
+ }
+
+ {
+ for (Map.Entry<String, String> entry : member_urls1.entrySet()) {
+ registry.register(member_service_name, entry.getKey(), entry.getValue());
+ }
+
+ Map<String, String> registered = registry.getRegistered(member_service_name);
+ assertEquals(1, registered.size());
+ assertNotSame(member_urls1, registered);
+ assertEquals(member_urls1, registered);
+
+ Map<String, Map<String, String>> registeredMap = registry.getRegistered();
+ assertEquals(1, registeredMap.size());
+ assertTrue(registeredMap.containsKey(member_service_name));
+
+ registered = registeredMap.get(member_service_name);
+ assertEquals(1, registered.size());
+ assertNotSame(member_urls1, registered);
+ assertEquals(member_urls1, registered);
+ }
+
+ {
+ registry.register(member_service_name, member_urls2);
+
+ Map<String, String> registered = registry.getRegistered(member_service_name);
+
+ final Map<String, String> urls = new HashMap<String, String>();
+ urls.putAll(member_urls1);
+ urls.putAll(member_urls2);
+
+ assertEquals(urls, registered);
+
+ Map<String, Map<String, String>> registeredMap = registry.getRegistered();
+ assertEquals(1, registeredMap.size());
+ assertTrue(registeredMap.containsKey(member_service_name));
+
+ registered = registeredMap.get(member_service_name);
+ assertEquals(urls, registered);
+ }
+
+ {
+ Map<String, Map<String, String>> services = new HashMap<String, Map<String, String>>();
+ services.put(member_service_name, member_urls3);
+ services.put(xxx_service_name, xxx_urls1);
+
+ registry.register(services);
+
+ final Map<String, String> urls = new HashMap<String, String>();
+ urls.putAll(member_urls1);
+ urls.putAll(member_urls2);
+ urls.putAll(member_urls3);
+ {
+ Map<String, String> registered = registry.getRegistered(member_service_name);
+ assertEquals(urls, registered);
+
+ registered = registry.getRegistered(xxx_service_name);
+ assertEquals(xxx_urls1, registered);
+ }
+
+ {
+ Map<String, Map<String, String>> registeredMap = registry.getRegistered();
+ assertEquals(2, registeredMap.size());
+ assertTrue(registeredMap.containsKey(member_service_name));
+ assertTrue(registeredMap.containsKey(xxx_service_name));
+
+ Map<String, String> registered = registeredMap.get(member_service_name);
+ assertEquals(urls, registered);
+
+ registered = registeredMap.get(xxx_service_name);
+ assertEquals(xxx_urls1, registered);
+ }
+ }
+ }
+
+ public static void subscribeCheck(AbstractRegistry registry, NotificationListener mockNotifyListener) {
+ String subscribed = registry.getSubscribed("com.alibaba.morgan.MemberService");
+ assertNull(subscribed);
+
+ Map<String, String> subscribedMap = registry.getSubscribed();
+ assertEquals(0, subscribedMap.size());
+
+ // query允许为null
+ registry.subscribe("com.alibaba.xxx.XxxService", (String) null, mockNotifyListener);
+
+ subscribed = registry.getSubscribed("com.alibaba.xxx.XxxService");
+ assertEquals("", subscribed);
+
+ subscribedMap = registry.getSubscribed();
+ assertEquals(1, subscribedMap.size());
+ assertTrue(subscribedMap.containsKey("com.alibaba.xxx.XxxService"));
+ assertEquals("", subscribedMap.get("com.alibaba.xxx.XxxService"));
+
+ registry.subscribe("com.alibaba.empty.EmptyService", "", mockNotifyListener);
+
+ // query允许为空
+ subscribed = registry.getSubscribed("com.alibaba.empty.EmptyService");
+ assertEquals("", subscribed);
+
+ subscribedMap = registry.getSubscribed();
+ assertEquals(2, subscribedMap.size());
+ assertTrue(subscribedMap.containsKey("com.alibaba.empty.EmptyService"));
+ assertEquals("", subscribedMap.get("com.alibaba.empty.EmptyService"));
+
+ // 多项值的Query
+ registry.subscribe("com.alibaba.morgan.MemberService", "dog=bad,cat=god",
+ mockNotifyListener);
+
+ subscribed = registry.getSubscribed("com.alibaba.morgan.MemberService");
+ assertEquals("dog=bad,cat=god", subscribed);
+
+ subscribedMap = registry.getSubscribed();
+ assertEquals(3, subscribedMap.size());
+ String s = subscribedMap.get("com.alibaba.morgan.MemberService");
+ assertEquals("dog=bad,cat=god", s);
+
+ registry.subscribe("com.alibaba.complex.ComplexService",
+ "version=1.0.0&application=kylin&methods=findPerson,findVAccount",
+ mockNotifyListener);
+
+ subscribedMap = registry.getSubscribed();
+ assertEquals(4, subscribedMap.size());
+ String q = subscribedMap.get("com.alibaba.complex.ComplexService");
+ assertEquals("version=1.0.0&application=kylin&methods=findPerson,findVAccount", q);
+ }*/
+
+ public void testDummy() {
+ }
+}
\ No newline at end of file
diff --git a/dubbo-registry/src/test/resources/log4j.xml b/dubbo-registry/src/test/resources/log4j.xml
new file mode 100644
index 0000000..1eb50f0
--- /dev/null
+++ b/dubbo-registry/src/test/resources/log4j.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ - Copyright 1999-2011 Alibaba Group.
+ -
+ - Licensed under the Apache License, Version 2.0 (the "License");
+ - you may not use this file except in compliance with the License.
+ - You may obtain a copy of the License at
+ -
+ - http://www.apache.org/licenses/LICENSE-2.0
+ -
+ - Unless required by applicable law or agreed to in writing, software
+ - distributed under the License is distributed on an "AS IS" BASIS,
+ - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ - See the License for the specific language governing permissions and
+ - limitations under the License.
+-->
+<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
+<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/" debug="false">
+ <appender name="CONSOLE" class="org.apache.log4j.ConsoleAppender">
+ <layout class="org.apache.log4j.PatternLayout">
+ <param name="ConversionPattern" value="[%d{dd/MM/yy hh:mm:ss:sss z}] %5p %c{2}: %m%n" />
+ </layout>
+ </appender>
+ <root>
+ <level value="WARN" />
+ <appender-ref ref="CONSOLE" />
+ </root>
+</log4j:configuration>
\ No newline at end of file
diff --git a/dubbo-remoting-grizzly/pom.xml b/dubbo-remoting-grizzly/pom.xml
new file mode 100644
index 0000000..9dbd6f4
--- /dev/null
+++ b/dubbo-remoting-grizzly/pom.xml
@@ -0,0 +1,42 @@
+<!--
+ - Copyright 1999-2011 Alibaba Group.
+ -
+ - Licensed under the Apache License, Version 2.0 (the "License");
+ - you may not use this file except in compliance with the License.
+ - You may obtain a copy of the License at
+ -
+ - http://www.apache.org/licenses/LICENSE-2.0
+ -
+ - Unless required by applicable law or agreed to in writing, software
+ - distributed under the License is distributed on an "AS IS" BASIS,
+ - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ - See the License for the specific language governing permissions and
+ - limitations under the License.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>com.alibaba</groupId>
+ <artifactId>dubbo-parent</artifactId>
+ <version>2.0.8</version>
+ </parent>
+ <artifactId>dubbo-remoting-grizzly</artifactId>
+ <packaging>jar</packaging>
+ <name>Dubbo Grizzly Remoting Module</name>
+ <description>The grizzly remoting module of dubbo project</description>
+ <properties>
+ <skip_maven_deploy>false</skip_maven_deploy>
+ </properties>
+ <dependencies>
+ <dependency>
+ <groupId>com.alibaba</groupId>
+ <artifactId>dubbo-remoting</artifactId>
+ <version>${project.parent.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.glassfish.grizzly</groupId>
+ <artifactId>grizzly-core</artifactId>
+ </dependency>
+ </dependencies>
+</project>
\ No newline at end of file
diff --git a/dubbo-remoting-grizzly/src/main/java/com/alibaba/dubbo/remoting/transport/grizzly/GrizzlyChannel.java b/dubbo-remoting-grizzly/src/main/java/com/alibaba/dubbo/remoting/transport/grizzly/GrizzlyChannel.java
new file mode 100644
index 0000000..65df0cf
--- /dev/null
+++ b/dubbo-remoting-grizzly/src/main/java/com/alibaba/dubbo/remoting/transport/grizzly/GrizzlyChannel.java
@@ -0,0 +1,178 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting.transport.grizzly;
+
+import java.net.InetSocketAddress;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+import org.glassfish.grizzly.Connection;
+import org.glassfish.grizzly.Grizzly;
+import org.glassfish.grizzly.GrizzlyFuture;
+import org.glassfish.grizzly.attributes.Attribute;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.logger.Logger;
+import com.alibaba.dubbo.common.logger.LoggerFactory;
+import com.alibaba.dubbo.remoting.ChannelHandler;
+import com.alibaba.dubbo.remoting.RemotingException;
+import com.alibaba.dubbo.remoting.transport.AbstractChannel;
+
+/**
+ * GrizzlyChannel
+ *
+ * @author william.liangf
+ */
+final class GrizzlyChannel extends AbstractChannel {
+
+ private static final Logger logger = LoggerFactory.getLogger(GrizzlyChannel.class);
+
+ private static final String CHANNEL_KEY = GrizzlyChannel.class.getName() + ".CHANNEL";
+
+ private static final Attribute<GrizzlyChannel> ATTRIBUTE = Grizzly.DEFAULT_ATTRIBUTE_BUILDER.createAttribute(CHANNEL_KEY);
+
+ private final Connection<?> connection;
+
+ /**
+ * @param connection
+ * @param url
+ * @param handler
+ */
+ private GrizzlyChannel(Connection<?> connection, URL url, ChannelHandler handler){
+ super(url, handler);
+ if (connection == null) {
+ throw new IllegalArgumentException("grizzly connection == null");
+ }
+ this.connection = connection;
+ }
+
+ static GrizzlyChannel getOrAddChannel(Connection<?> connection, URL url, ChannelHandler handler) {
+ if (connection == null) {
+ return null;
+ }
+ GrizzlyChannel ret = ATTRIBUTE.get(connection);
+ if (ret == null) {
+ ret = new GrizzlyChannel(connection, url, handler);
+ if (connection.isOpen()) {
+ ATTRIBUTE.set(connection, ret);
+ }
+ }
+ return ret;
+ }
+
+ static void removeChannelIfDisconnectd(Connection<?> connection) {
+ if (connection != null && ! connection.isOpen()) {
+ ATTRIBUTE.remove(connection);
+ }
+ }
+
+ public InetSocketAddress getRemoteAddress() {
+ return (InetSocketAddress) connection.getPeerAddress();
+ }
+
+ public boolean isConnected() {
+ return connection.isOpen();
+ }
+
+ public InetSocketAddress getLocalAddress() {
+ return (InetSocketAddress) connection.getLocalAddress();
+ }
+
+ @SuppressWarnings("rawtypes")
+ public void send(Object message, boolean sent) throws RemotingException {
+ super.send(message, sent);
+
+ int timeout = 0;
+ try {
+ GrizzlyFuture future = connection.write(message);
+ if (sent) {
+ timeout = getUrl().getPositiveParameter(Constants.TIMEOUT_KEY, Constants.DEFAULT_TIMEOUT);
+ future.get(timeout, TimeUnit.MILLISECONDS);
+ }
+ }
+ catch (TimeoutException e) {
+ throw new RemotingException(this, "Failed to send message " + message + " to " + getRemoteAddress()
+ + "in timeout(" + timeout + "ms) limit", e);
+ }
+ catch (Throwable e) {
+ throw new RemotingException(this, "Failed to send message " + message + " to " + getRemoteAddress() + ", cause: " + e.getMessage(), e);
+ }
+ }
+
+ public void close() {
+ try {
+ super.close();
+ } catch (Exception e) {
+ logger.warn(e.getMessage(), e);
+ }
+ try {
+ removeChannelIfDisconnectd(connection);
+ } catch (Exception e) {
+ logger.warn(e.getMessage(), e);
+ }
+ try {
+ if (logger.isInfoEnabled()) {
+ logger.info("Close grizzly channel " + connection);
+ }
+ connection.close();
+ } catch (Exception e) {
+ logger.warn(e.getMessage(), e);
+ }
+ }
+
+ public boolean hasAttribute(String key) {
+ return getAttribute(key) == null;
+ }
+
+ public Object getAttribute(String key) {
+ return Grizzly.DEFAULT_ATTRIBUTE_BUILDER.createAttribute(key).get(connection);
+ }
+
+ public void setAttribute(String key, Object value) {
+ Grizzly.DEFAULT_ATTRIBUTE_BUILDER.createAttribute(key).set(connection, value);
+ }
+
+ public void removeAttribute(String key) {
+ Grizzly.DEFAULT_ATTRIBUTE_BUILDER.createAttribute(key).remove(connection);
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((connection == null) ? 0 : connection.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) return true;
+ if (obj == null) return false;
+ if (getClass() != obj.getClass()) return false;
+ GrizzlyChannel other = (GrizzlyChannel) obj;
+ if (connection == null) {
+ if (other.connection != null) return false;
+ } else if (!connection.equals(other.connection)) return false;
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ return "GrizzlyChannel [connection=" + connection + "]";
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting-grizzly/src/main/java/com/alibaba/dubbo/remoting/transport/grizzly/GrizzlyClient.java b/dubbo-remoting-grizzly/src/main/java/com/alibaba/dubbo/remoting/transport/grizzly/GrizzlyClient.java
new file mode 100644
index 0000000..34a6050
--- /dev/null
+++ b/dubbo-remoting-grizzly/src/main/java/com/alibaba/dubbo/remoting/transport/grizzly/GrizzlyClient.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting.transport.grizzly;
+
+import java.util.concurrent.TimeUnit;
+
+import org.glassfish.grizzly.Connection;
+import org.glassfish.grizzly.filterchain.FilterChainBuilder;
+import org.glassfish.grizzly.filterchain.TransportFilter;
+import org.glassfish.grizzly.nio.transport.TCPNIOTransport;
+import org.glassfish.grizzly.nio.transport.TCPNIOTransportBuilder;
+import org.glassfish.grizzly.strategies.SameThreadIOStrategy;
+import org.glassfish.grizzly.threadpool.ThreadPoolConfig;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.logger.Logger;
+import com.alibaba.dubbo.common.logger.LoggerFactory;
+import com.alibaba.dubbo.remoting.Channel;
+import com.alibaba.dubbo.remoting.ChannelHandler;
+import com.alibaba.dubbo.remoting.RemotingException;
+import com.alibaba.dubbo.remoting.transport.AbstractClient;
+
+/**
+ * GrizzlyClient
+ *
+ * @author william.liangf
+ */
+public class GrizzlyClient extends AbstractClient {
+
+ private static final Logger logger = LoggerFactory.getLogger(GrizzlyClient.class);
+
+ private TCPNIOTransport transport;
+
+ private volatile Connection<?> connection; // volatile, please copy reference to use
+
+ public GrizzlyClient(URL url, ChannelHandler handler) throws RemotingException {
+ super(url, handler);
+ }
+
+ @Override
+ protected void doOpen() throws Throwable {
+ FilterChainBuilder filterChainBuilder = FilterChainBuilder.stateless();
+ filterChainBuilder.add(new TransportFilter());
+ filterChainBuilder.add(new GrizzlyCodecAdapter(getCodec(), getUrl(), this));
+ filterChainBuilder.add(new GrizzlyHandler(getUrl(), this));
+ TCPNIOTransportBuilder builder = TCPNIOTransportBuilder.newInstance();
+ ThreadPoolConfig config = builder.getWorkerThreadPoolConfig();
+ config.setPoolName(CLIENT_THREAD_POOL_NAME)
+ .setQueueLimit(-1)
+ .setCorePoolSize(0)
+ .setMaxPoolSize(Integer.MAX_VALUE)
+ .setKeepAliveTime(60L, TimeUnit.SECONDS);
+ builder.setTcpNoDelay(true).setKeepAlive(true)
+ .setConnectionTimeout(getTimeout())
+ .setIOStrategy(SameThreadIOStrategy.getInstance());
+ transport = builder.build();
+ transport.setProcessor(filterChainBuilder.build());
+ transport.start();
+ }
+
+
+
+ @Override
+ protected void doConnect() throws Throwable {
+ connection = transport.connect(getConnectAddress())
+ .get(getUrl().getPositiveParameter(Constants.TIMEOUT_KEY, Constants.DEFAULT_TIMEOUT), TimeUnit.MILLISECONDS);
+ }
+
+ @Override
+ protected void doDisConnect() throws Throwable {
+ try {
+ GrizzlyChannel.removeChannelIfDisconnectd(connection);
+ } catch (Throwable t) {
+ logger.warn(t.getMessage());
+ }
+ }
+
+ @Override
+ protected void doClose() throws Throwable {
+ try {
+ transport.stop();
+ } catch (Throwable e) {
+ logger.warn(e.getMessage(), e);
+ }
+ }
+
+ @Override
+ protected Channel getChannel() {
+ Connection<?> c = connection;
+ if (c == null || ! c.isOpen())
+ return null;
+ return GrizzlyChannel.getOrAddChannel(c, getUrl(), this);
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting-grizzly/src/main/java/com/alibaba/dubbo/remoting/transport/grizzly/GrizzlyCodecAdapter.java b/dubbo-remoting-grizzly/src/main/java/com/alibaba/dubbo/remoting/transport/grizzly/GrizzlyCodecAdapter.java
new file mode 100644
index 0000000..2d235ab
--- /dev/null
+++ b/dubbo-remoting-grizzly/src/main/java/com/alibaba/dubbo/remoting/transport/grizzly/GrizzlyCodecAdapter.java
@@ -0,0 +1,189 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting.transport.grizzly;
+
+import java.io.IOException;
+
+import org.glassfish.grizzly.Buffer;
+import org.glassfish.grizzly.Connection;
+import org.glassfish.grizzly.filterchain.BaseFilter;
+import org.glassfish.grizzly.filterchain.FilterChainContext;
+import org.glassfish.grizzly.filterchain.NextAction;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.io.Bytes;
+import com.alibaba.dubbo.common.io.UnsafeByteArrayInputStream;
+import com.alibaba.dubbo.common.io.UnsafeByteArrayOutputStream;
+import com.alibaba.dubbo.remoting.Channel;
+import com.alibaba.dubbo.remoting.ChannelHandler;
+import com.alibaba.dubbo.remoting.Codec;
+import com.alibaba.dubbo.remoting.exchange.Response;
+
+/**
+ * GrizzlyCodecAdapter
+ *
+ * @author william.liangf
+ */
+public class GrizzlyCodecAdapter extends BaseFilter {
+
+ private static final String BUFFER_KEY = GrizzlyCodecAdapter.class.getName() + ".BUFFER";
+
+ private final Codec upstreamCodec;
+ private final Codec downstreamCodec;
+
+ private final URL url;
+
+ private final ChannelHandler handler;
+
+ private final int bufferSize;
+
+ public GrizzlyCodecAdapter(Codec codec, URL url, ChannelHandler handler){
+ this(codec, codec, url, handler);
+ }
+ /**
+ * server 端如果有消息发送需要分开codec,默认的上行code是dubbo1兼容的
+ */
+ public GrizzlyCodecAdapter(Codec upstreamCodec, Codec downstreamCodec, URL url, ChannelHandler handler){
+ this.upstreamCodec = upstreamCodec;
+ this.downstreamCodec = downstreamCodec;
+ this.url = url;
+ this.handler = handler;
+ int b = url.getPositiveParameter(Constants.BUFFER_KEY, Constants.DEFAULT_BUFFER_SIZE);
+ this.bufferSize = b >= Constants.MIN_BUFFER_SIZE && b <= Constants.MAX_BUFFER_SIZE ? b : Constants.DEFAULT_BUFFER_SIZE;
+ }
+
+ @Override
+ public NextAction handleWrite(FilterChainContext context) throws IOException {
+ Connection<?> connection = context.getConnection();
+ GrizzlyChannel channel = GrizzlyChannel.getOrAddChannel(connection, url, handler);
+ try {
+ UnsafeByteArrayOutputStream output = new UnsafeByteArrayOutputStream(1024); // 不需要关闭
+
+ if(!(context.getMessage() instanceof Response)){
+ downstreamCodec.encode(channel, output, context.getMessage());
+ }else{
+ upstreamCodec.encode(channel, output, context.getMessage());
+ }
+
+ GrizzlyChannel.removeChannelIfDisconnectd(connection);
+ byte[] bytes = output.toByteArray();
+ Buffer buffer = connection.getTransport().getMemoryManager().allocate(bytes.length);
+ buffer.put(bytes);
+ buffer.flip();
+ buffer.allowBufferDispose(true);
+ context.setMessage(buffer);
+ } finally {
+ GrizzlyChannel.removeChannelIfDisconnectd(connection);
+ }
+ return context.getInvokeAction();
+ }
+
+ @Override
+ public NextAction handleRead(FilterChainContext context) throws IOException {
+ Object message = context.getMessage();
+ Connection<?> connection = context.getConnection();
+ Channel channel = GrizzlyChannel.getOrAddChannel(connection, url, handler);
+ try {
+ if (message instanceof Buffer) { // 收到新的数据包
+ Buffer buffer = (Buffer) message; // 缓存
+ int readable = buffer.capacity(); // 本次可读取新数据的大小
+ if (readable == 0) {
+ return context.getStopAction();
+ }
+ byte[] bytes; // byte[]缓存区,将Buffer转成byte[],再转成UnsafeByteArrayInputStream
+ int offset; // 指向已用数据的偏移量,off之前的数据都是已用过的
+ int limit; // 有效长度,limit之后的长度是空白或无效数据,off到limit之间的数据是准备使用的有效数据
+ Object[] remainder = (Object[]) channel.getAttribute(BUFFER_KEY); // 上次序列化剩下的数据
+ channel.removeAttribute(BUFFER_KEY);
+ if (remainder == null) { // 如果没有,创建新的bytes缓存
+ bytes = new byte[bufferSize];
+ offset = 0;
+ limit = 0;
+ } else { // 如果有,使用剩下的bytes缓存
+ bytes = (byte[]) remainder[0];
+ offset = (Integer) remainder[1];
+ limit = (Integer) remainder[2];
+ }
+ return receive(context, channel, buffer, readable, bytes, offset, limit);
+ } else if (message instanceof Object[]) { // 同一Buffer多轮Filter,即:一个Buffer里有多个请求
+ Object[] remainder = (Object[]) message;
+ Buffer buffer = (Buffer) remainder[0];
+ int readable = (Integer) remainder[1];
+ byte[] bytes = (byte[]) remainder[2];
+ int offset = (Integer) remainder[3];
+ int limit = (Integer) remainder[4];
+ return receive(context, channel, buffer, readable, bytes, offset, limit);
+ } else { // 其它事件直接往下传
+ return context.getInvokeAction();
+ }
+ } finally {
+ GrizzlyChannel.removeChannelIfDisconnectd(connection);
+ }
+ }
+
+ /*
+ * 接收
+ *
+ * @param context 上下文
+ * @param channel 通道
+ * @param buffer 缓存
+ * @param readable 缓存可读
+ * @param bytes 输入缓存
+ * @param offset 指向已读数据的偏移量,off之前的数据都是已用过的
+ * @param limit 有效长度,limit之后的长度是空白或无效数据,off到limit之间的数据是准备使用的数据
+ * @return 后续动作
+ * @throws IOException
+ */
+ private NextAction receive(FilterChainContext context, Channel channel, Buffer buffer, int readable, byte[] bytes, int offset, int limit) throws IOException {
+ for(;;) {
+ int read = Math.min(readable, bytes.length - limit); // 取bytes缓存空闲区,和可读取新数据,的最小值,即:此次最多读写数据的大小
+ buffer.get(bytes, limit, read); // 从可读取新数据中,读取数据,尽量填满bytes缓存空闲区
+ limit += read; // 有效数据变长
+ readable -= read; // 可读数据变少
+ UnsafeByteArrayInputStream input = new UnsafeByteArrayInputStream(bytes, offset, limit - offset); // 将bytes缓存转成InputStream,不需要关闭
+ Object msg = upstreamCodec.decode(channel, input); // 调用Codec接口,解码数据
+ if (msg == Codec.NEED_MORE_INPUT) { // 如果Codec觉得数据不够,不足以解码成一个对象
+ if (readable == 0) { // 如果没有更多可读数据
+ channel.setAttribute(BUFFER_KEY, new Object[] { bytes, offset, limit }); // 放入通道属性中,等待下一个Buffer的到来
+ return context.getStopAction();
+ } else { // 扩充或挪出空闲区,并循环,直到可读数据都加载到bytes缓存
+ if (offset == 0) { // 如果bytes缓存全部没有被使用,如果这时数据还不够
+ bytes = Bytes.copyOf(bytes, bytes.length << 1); // 将bytes缓存扩大一倍
+ } else { // 如果bytes缓存有一段数据已被使用
+ int len = limit - offset; // 计算有效数据长度
+ System.arraycopy(bytes, offset, bytes, 0, len); // 将数据向前移到,压缩到已使用的部分,这样limit后面就会多出一些空闲,可以放数据
+ offset = 0; // 移到后,bytes缓存没有数据被使用
+ limit = len; // 移到后,有效数据都在bytes缓存最前面
+ }
+ }
+ } else { // 如果解析出一个结果
+ int position = input.position(); // 记录InputStream用了多少
+ if (position == offset) { // 如果InputStream没有被读过,就返回了数据,直接报错,否则InputStream永远读不完,会死递归
+ throw new IOException("Decode without read data.");
+ }
+ offset = position; // 记录已读数据
+ context.setMessage(msg); // 将消息改为解码后的对象,以便被后面的Filter使用。
+ if (limit - offset > 0 || readable > 0) { // 如果有效数据没有被读完,或者Buffer区还有未读数据
+ return context.getInvokeAction(new Object[] { buffer, readable, bytes, offset, limit }); // 正常执行完Filter,并重新发起一轮Filter,继续读
+ } else { // 否则所有数据读完
+ return context.getInvokeAction(); // 正常执行完Filter
+ }
+ }
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting-grizzly/src/main/java/com/alibaba/dubbo/remoting/transport/grizzly/GrizzlyHandler.java b/dubbo-remoting-grizzly/src/main/java/com/alibaba/dubbo/remoting/transport/grizzly/GrizzlyHandler.java
new file mode 100644
index 0000000..f7196cd
--- /dev/null
+++ b/dubbo-remoting-grizzly/src/main/java/com/alibaba/dubbo/remoting/transport/grizzly/GrizzlyHandler.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting.transport.grizzly;
+
+import java.io.IOException;
+
+import org.glassfish.grizzly.Connection;
+import org.glassfish.grizzly.filterchain.BaseFilter;
+import org.glassfish.grizzly.filterchain.FilterChainContext;
+import org.glassfish.grizzly.filterchain.NextAction;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.logger.Logger;
+import com.alibaba.dubbo.common.logger.LoggerFactory;
+import com.alibaba.dubbo.common.utils.StringUtils;
+import com.alibaba.dubbo.remoting.ChannelHandler;
+import com.alibaba.dubbo.remoting.RemotingException;
+
+/**
+ * GrizzlyHandler
+ *
+ * @author william.liangf
+ */
+public class GrizzlyHandler extends BaseFilter {
+
+ private static final Logger logger = LoggerFactory.getLogger(GrizzlyHandler.class);
+
+ private final URL url;
+
+ private final ChannelHandler handler;
+
+ public GrizzlyHandler(URL url, ChannelHandler handler){
+ this.url = url;
+ this.handler = handler;
+ }
+
+ @Override
+ public NextAction handleConnect(FilterChainContext ctx) throws IOException {
+ Connection<?> connection = ctx.getConnection();
+ GrizzlyChannel channel = GrizzlyChannel.getOrAddChannel(connection, url, handler);
+ try {
+ handler.connected(channel);
+ } catch (RemotingException e) {
+ throw new IOException(StringUtils.toString(e));
+ } finally {
+ GrizzlyChannel.removeChannelIfDisconnectd(connection);
+ }
+ return ctx.getInvokeAction();
+ }
+
+ @Override
+ public NextAction handleClose(FilterChainContext ctx) throws IOException {
+ Connection<?> connection = ctx.getConnection();
+ GrizzlyChannel channel = GrizzlyChannel.getOrAddChannel(connection, url, handler);
+ try {
+ handler.disconnected(channel);
+ } catch (RemotingException e) {
+ throw new IOException(StringUtils.toString(e));
+ } finally {
+ GrizzlyChannel.removeChannelIfDisconnectd(connection);
+ }
+ return ctx.getInvokeAction();
+ }
+
+ @Override
+ public NextAction handleRead(FilterChainContext ctx) throws IOException {
+ Connection<?> connection = ctx.getConnection();
+ GrizzlyChannel channel = GrizzlyChannel.getOrAddChannel(connection, url, handler);
+ try {
+ handler.received(channel, ctx.getMessage());
+ } catch (RemotingException e) {
+ throw new IOException(StringUtils.toString(e));
+ } finally {
+ GrizzlyChannel.removeChannelIfDisconnectd(connection);
+ }
+ return ctx.getInvokeAction();
+ }
+
+ @Override
+ public NextAction handleWrite(FilterChainContext ctx) throws IOException {
+ Connection<?> connection = ctx.getConnection();
+ GrizzlyChannel channel = GrizzlyChannel.getOrAddChannel(connection, url, handler);
+ try {
+ handler.sent(channel, ctx.getMessage());
+ } catch (RemotingException e) {
+ throw new IOException(StringUtils.toString(e));
+ } finally {
+ GrizzlyChannel.removeChannelIfDisconnectd(connection);
+ }
+ return ctx.getInvokeAction();
+ }
+
+ @Override
+ public void exceptionOccurred(FilterChainContext ctx, Throwable error) {
+ Connection<?> connection = ctx.getConnection();
+ GrizzlyChannel channel = GrizzlyChannel.getOrAddChannel(connection, url, handler);
+ try {
+ handler.caught(channel, error);
+ } catch (RemotingException e) {
+ logger.error("RemotingException on channel " + channel, e);
+ } finally {
+ GrizzlyChannel.removeChannelIfDisconnectd(connection);
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting-grizzly/src/main/java/com/alibaba/dubbo/remoting/transport/grizzly/GrizzlyServer.java b/dubbo-remoting-grizzly/src/main/java/com/alibaba/dubbo/remoting/transport/grizzly/GrizzlyServer.java
new file mode 100644
index 0000000..7fe7b06
--- /dev/null
+++ b/dubbo-remoting-grizzly/src/main/java/com/alibaba/dubbo/remoting/transport/grizzly/GrizzlyServer.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.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;
+import com.alibaba.dubbo.remoting.transport.handler.ChannelHandlers;
+
+/**
+ * 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, ChannelHandlers.wrap(handler, url));
+ }
+
+ @Override
+ protected void doOpen() throws Throwable {
+ FilterChainBuilder filterChainBuilder = FilterChainBuilder.stateless();
+ filterChainBuilder.add(new TransportFilter());
+
+ filterChainBuilder.add(new GrizzlyCodecAdapter(getCodec(), getDownstreamCodec(), getUrl(), this));
+ filterChainBuilder.add(new GrizzlyHandler(getUrl(), this));
+ TCPNIOTransportBuilder builder = TCPNIOTransportBuilder.newInstance();
+ ThreadPoolConfig config = builder.getWorkerThreadPoolConfig();
+ config.setPoolName(SERVER_THREAD_POOL_NAME).setQueueLimit(-1);
+ String threadpool = getUrl().getParameter(Constants.THREADPOOL_KEY, Constants.DEFAULT_THREADPOOL);
+ if (Constants.DEFAULT_THREADPOOL.equals(threadpool)) {
+ int threads = getUrl().getPositiveParameter(Constants.THREADS_KEY, Constants.DEFAULT_THREADS);
+ config.setCorePoolSize(threads).setMaxPoolSize(threads)
+ .setKeepAliveTime(0L, TimeUnit.SECONDS);
+ } else if ("cached".equals(threadpool)) {
+ int threads = getUrl().getPositiveParameter(Constants.THREADS_KEY, Integer.MAX_VALUE);
+ config.setCorePoolSize(0).setMaxPoolSize(threads)
+ .setKeepAliveTime(60L, TimeUnit.SECONDS);
+ } else {
+ throw new IllegalArgumentException("Unsupported threadpool type " + threadpool);
+ }
+ builder.setKeepAlive(true).setReuseAddress(false)
+ .setIOStrategy(SameThreadIOStrategy.getInstance());
+ transport = builder.build();
+ transport.setProcessor(filterChainBuilder.build());
+ transport.bind(getBindAddress());
+ transport.start();
+ }
+
+ @Override
+ protected void doClose() throws Throwable {
+ try {
+ transport.stop();
+ } catch (Throwable e) {
+ logger.warn(e.getMessage(), e);
+ }
+ }
+
+ public boolean isBound() {
+ return ! transport.isStopped();
+ }
+
+ public Collection<Channel> getChannels() {
+ return channels.values();
+ }
+
+ public Channel getChannel(InetSocketAddress remoteAddress) {
+ return channels.get(NetUtils.toAddressString(remoteAddress));
+ }
+
+ @Override
+ public void connected(Channel ch) throws RemotingException {
+ channels.put(NetUtils.toAddressString(ch.getRemoteAddress()), ch);
+ super.connected(ch);
+ }
+
+ @Override
+ public void disconnected(Channel ch) throws RemotingException {
+ channels.remove(NetUtils.toAddressString(ch.getRemoteAddress()));
+ super.disconnected(ch);
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting-grizzly/src/main/java/com/alibaba/dubbo/remoting/transport/grizzly/GrizzlyTransporter.java b/dubbo-remoting-grizzly/src/main/java/com/alibaba/dubbo/remoting/transport/grizzly/GrizzlyTransporter.java
new file mode 100644
index 0000000..1a3e37f
--- /dev/null
+++ b/dubbo-remoting-grizzly/src/main/java/com/alibaba/dubbo/remoting/transport/grizzly/GrizzlyTransporter.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting.transport.grizzly;
+
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.remoting.ChannelHandler;
+import com.alibaba.dubbo.remoting.Client;
+import com.alibaba.dubbo.remoting.RemotingException;
+import com.alibaba.dubbo.remoting.Server;
+import com.alibaba.dubbo.remoting.Transporter;
+
+/**
+ * GrizzlyTransporter
+ *
+ * @author william.liangf
+ */
+@Extension(GrizzlyTransporter.NAME)
+public class GrizzlyTransporter implements Transporter {
+
+ public static final String NAME = "grizzly";
+
+ public Server bind(URL url, ChannelHandler listener) throws RemotingException {
+ return new GrizzlyServer(url, listener);
+ }
+
+ public Client connect(URL url, ChannelHandler listener) throws RemotingException {
+ return new GrizzlyClient(url, listener);
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting-grizzly/src/main/resources/META-INF/services/com.alibaba.dubbo.remoting.Transporter b/dubbo-remoting-grizzly/src/main/resources/META-INF/services/com.alibaba.dubbo.remoting.Transporter
new file mode 100644
index 0000000..5ce7f67
--- /dev/null
+++ b/dubbo-remoting-grizzly/src/main/resources/META-INF/services/com.alibaba.dubbo.remoting.Transporter
@@ -0,0 +1 @@
+com.alibaba.dubbo.remoting.transport.grizzly.GrizzlyTransporter
\ No newline at end of file
diff --git a/dubbo-remoting-http/pom.xml b/dubbo-remoting-http/pom.xml
new file mode 100644
index 0000000..e70fa92
--- /dev/null
+++ b/dubbo-remoting-http/pom.xml
@@ -0,0 +1,42 @@
+<!--
+ - Copyright 1999-2011 Alibaba Group.
+ -
+ - Licensed under the Apache License, Version 2.0 (the "License");
+ - you may not use this file except in compliance with the License.
+ - You may obtain a copy of the License at
+ -
+ - http://www.apache.org/licenses/LICENSE-2.0
+ -
+ - Unless required by applicable law or agreed to in writing, software
+ - distributed under the License is distributed on an "AS IS" BASIS,
+ - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ - See the License for the specific language governing permissions and
+ - limitations under the License.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>com.alibaba</groupId>
+ <artifactId>dubbo-parent</artifactId>
+ <version>2.0.8</version>
+ </parent>
+ <artifactId>dubbo-remoting-http</artifactId>
+ <packaging>jar</packaging>
+ <name>Dubbo Http Remoting Module</name>
+ <description>The http remoting module of dubbo project</description>
+ <properties>
+ <skip_maven_deploy>false</skip_maven_deploy>
+ </properties>
+ <dependencies>
+ <dependency>
+ <groupId>com.alibaba</groupId>
+ <artifactId>dubbo-common</artifactId>
+ <version>${project.parent.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.mortbay.jetty</groupId>
+ <artifactId>jetty</artifactId>
+ </dependency>
+ </dependencies>
+</project>
\ No newline at end of file
diff --git a/dubbo-remoting-http/src/main/java/com/alibaba/dubbo/remoting/http/HttpBinder.java b/dubbo-remoting-http/src/main/java/com/alibaba/dubbo/remoting/http/HttpBinder.java
new file mode 100644
index 0000000..a7356bb
--- /dev/null
+++ b/dubbo-remoting-http/src/main/java/com/alibaba/dubbo/remoting/http/HttpBinder.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting.http;
+
+import com.alibaba.dubbo.common.Adaptive;
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.common.URL;
+
+/**
+ * HttpBinder
+ *
+ * @author william.liangf
+ */
+@Extension("jetty")
+public interface HttpBinder {
+
+ /**
+ * bind the server.
+ *
+ * @param url server url.
+ * @return server.
+ */
+ @Adaptive({Constants.SERVER_KEY})
+ HttpServer bind(URL url, HttpHandler handler);
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting-http/src/main/java/com/alibaba/dubbo/remoting/http/HttpHandler.java b/dubbo-remoting-http/src/main/java/com/alibaba/dubbo/remoting/http/HttpHandler.java
new file mode 100644
index 0000000..6550a3b
--- /dev/null
+++ b/dubbo-remoting-http/src/main/java/com/alibaba/dubbo/remoting/http/HttpHandler.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting.http;
+
+import java.io.IOException;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * http invocation handler.
+ *
+ * @author william.liangf
+ */
+public interface HttpHandler {
+
+ /**
+ * invoke.
+ *
+ * @param request request.
+ * @param response response.
+ * @throws IOException
+ * @throws ServletException
+ */
+ void handle(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException;
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting-http/src/main/java/com/alibaba/dubbo/remoting/http/HttpServer.java b/dubbo-remoting-http/src/main/java/com/alibaba/dubbo/remoting/http/HttpServer.java
new file mode 100644
index 0000000..1aa44ed
--- /dev/null
+++ b/dubbo-remoting-http/src/main/java/com/alibaba/dubbo/remoting/http/HttpServer.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting.http;
+
+import java.net.InetSocketAddress;
+
+import com.alibaba.dubbo.common.Resetable;
+import com.alibaba.dubbo.common.URL;
+
+public interface HttpServer extends Resetable {
+
+ /**
+ * get http handler.
+ *
+ * @return http handler.
+ */
+ HttpHandler getHttpHandler();
+
+ /**
+ * get url.
+ *
+ * @return url
+ */
+ URL getUrl();
+
+ /**
+ * get local address.
+ *
+ * @return local address.
+ */
+ InetSocketAddress getLocalAddress();
+
+ /**
+ * close the channel.
+ */
+ void close();
+
+ /**
+ * Graceful close the channel.
+ */
+ void close(int timeout);
+
+ /**
+ * is bound.
+ *
+ * @return bound
+ */
+ boolean isBound();
+
+ /**
+ * is closed.
+ *
+ * @return closed
+ */
+ boolean isClosed();
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting-http/src/main/java/com/alibaba/dubbo/remoting/http/jetty/JettyHttpBinder.java b/dubbo-remoting-http/src/main/java/com/alibaba/dubbo/remoting/http/jetty/JettyHttpBinder.java
new file mode 100644
index 0000000..dc67f11
--- /dev/null
+++ b/dubbo-remoting-http/src/main/java/com/alibaba/dubbo/remoting/http/jetty/JettyHttpBinder.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting.http.jetty;
+
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.remoting.http.HttpHandler;
+import com.alibaba.dubbo.remoting.http.HttpServer;
+import com.alibaba.dubbo.remoting.http.HttpBinder;
+
+/**
+ * JettyHttpTransporter
+ *
+ * @author william.liangf
+ */
+@Extension("jetty")
+public class JettyHttpBinder implements HttpBinder {
+
+ public HttpServer bind(URL url, HttpHandler handler) {
+ return new JettyHttpServer(url, handler);
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting-http/src/main/java/com/alibaba/dubbo/remoting/http/jetty/JettyHttpServer.java b/dubbo-remoting-http/src/main/java/com/alibaba/dubbo/remoting/http/jetty/JettyHttpServer.java
new file mode 100644
index 0000000..cb30752
--- /dev/null
+++ b/dubbo-remoting-http/src/main/java/com/alibaba/dubbo/remoting/http/jetty/JettyHttpServer.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting.http.jetty;
+
+import java.io.IOException;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.mortbay.jetty.Server;
+import org.mortbay.jetty.handler.AbstractHandler;
+import org.mortbay.jetty.nio.SelectChannelConnector;
+import org.mortbay.thread.QueuedThreadPool;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.logger.Logger;
+import com.alibaba.dubbo.common.logger.LoggerFactory;
+import com.alibaba.dubbo.common.utils.NetUtils;
+import com.alibaba.dubbo.remoting.http.HttpHandler;
+import com.alibaba.dubbo.remoting.http.support.AbstractHttpServer;
+
+public class JettyHttpServer extends AbstractHttpServer {
+
+ private static final Logger logger = LoggerFactory.getLogger(JettyHttpServer.class);
+
+ private Server server;
+
+ public JettyHttpServer(URL url, final HttpHandler handler){
+ super(url, handler);
+
+ int threads = url.getParameter(Constants.THREADS_KEY, Constants.DEFAULT_THREADS);
+ QueuedThreadPool threadPool = new QueuedThreadPool();
+ threadPool.setDaemon(true);
+ threadPool.setMaxThreads(threads);
+ threadPool.setMinThreads(threads);
+
+ SelectChannelConnector connector = new SelectChannelConnector();
+ if (NetUtils.isValidLocalHost(url.getHost())) {
+ connector.setHost(url.getHost());
+ }
+ connector.setPort(url.getPort());
+
+ server = new Server();
+ server.setThreadPool(threadPool);
+ server.addConnector(connector);
+ server.addHandler(new AbstractHandler() {
+ public void handle(String target, HttpServletRequest request,
+ HttpServletResponse response, int dispatch) throws IOException,
+ ServletException {
+ handler.handle(request, response);
+ }
+ });
+
+ try {
+ server.start();
+ } catch (Exception e) {
+ throw new IllegalStateException("Failed to start jetty server on " + url.getAddress() + ", cause: "
+ + e.getMessage(), e);
+ }
+ }
+
+ public void close() {
+ super.close();
+ if (server != null) {
+ try {
+ server.stop();
+ } catch (Exception e) {
+ logger.warn(e.getMessage(), e);
+ }
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting-http/src/main/java/com/alibaba/dubbo/remoting/http/servlet/DispatcherServlet.java b/dubbo-remoting-http/src/main/java/com/alibaba/dubbo/remoting/http/servlet/DispatcherServlet.java
new file mode 100644
index 0000000..29e7a34
--- /dev/null
+++ b/dubbo-remoting-http/src/main/java/com/alibaba/dubbo/remoting/http/servlet/DispatcherServlet.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting.http.servlet;
+
+import java.io.IOException;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import com.alibaba.dubbo.remoting.http.HttpHandler;
+
+/**
+ * Service dispatcher Servlet.
+ *
+ * @author qian.lei
+ */
+public class DispatcherServlet extends HttpServlet {
+
+ private static final long serialVersionUID = 5766349180380479888L;
+
+ private static final Map<Integer, HttpHandler> handlers = new ConcurrentHashMap<Integer, HttpHandler>();
+
+ static void addHttpInvoker(int port, HttpHandler processor) {
+ handlers.put(port, processor);
+ }
+
+ static void removeHttpInvoker(int port) {
+ handlers.remove(port);
+ }
+
+ protected void service(HttpServletRequest request, HttpServletResponse response)
+ throws ServletException, IOException {
+ HttpHandler handler = handlers.get(request.getLocalPort());
+ if( handler == null ) {// service not found.
+ response.sendError(HttpServletResponse.SC_NOT_FOUND, "Service not found.");
+ } else {
+ handler.handle(request, response);
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting-http/src/main/java/com/alibaba/dubbo/remoting/http/servlet/ServletHttpBinder.java b/dubbo-remoting-http/src/main/java/com/alibaba/dubbo/remoting/http/servlet/ServletHttpBinder.java
new file mode 100644
index 0000000..ef459ee
--- /dev/null
+++ b/dubbo-remoting-http/src/main/java/com/alibaba/dubbo/remoting/http/servlet/ServletHttpBinder.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting.http.servlet;
+
+import com.alibaba.dubbo.common.Adaptive;
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.remoting.http.HttpBinder;
+import com.alibaba.dubbo.remoting.http.HttpHandler;
+import com.alibaba.dubbo.remoting.http.HttpServer;
+
+/**
+ * ServletHttpTransporter
+ *
+ * @author william.liangf
+ */
+@Extension("servlet")
+public class ServletHttpBinder implements HttpBinder {
+
+ @Adaptive()
+ public HttpServer bind(URL url, HttpHandler handler) {
+ return new ServletHttpServer(url, handler);
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting-http/src/main/java/com/alibaba/dubbo/remoting/http/servlet/ServletHttpServer.java b/dubbo-remoting-http/src/main/java/com/alibaba/dubbo/remoting/http/servlet/ServletHttpServer.java
new file mode 100644
index 0000000..0b9433a
--- /dev/null
+++ b/dubbo-remoting-http/src/main/java/com/alibaba/dubbo/remoting/http/servlet/ServletHttpServer.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting.http.servlet;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.remoting.http.HttpHandler;
+import com.alibaba.dubbo.remoting.http.support.AbstractHttpServer;
+
+public class ServletHttpServer extends AbstractHttpServer {
+
+ public ServletHttpServer(URL url, HttpHandler handler){
+ super(url, handler);
+ DispatcherServlet.addHttpInvoker(url.getPort(), handler);
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting-http/src/main/java/com/alibaba/dubbo/remoting/http/support/AbstractHttpServer.java b/dubbo-remoting-http/src/main/java/com/alibaba/dubbo/remoting/http/support/AbstractHttpServer.java
new file mode 100644
index 0000000..295cb18
--- /dev/null
+++ b/dubbo-remoting-http/src/main/java/com/alibaba/dubbo/remoting/http/support/AbstractHttpServer.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting.http.support;
+
+import java.net.InetSocketAddress;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.remoting.http.HttpHandler;
+import com.alibaba.dubbo.remoting.http.HttpServer;
+
+/**
+ * AbstractHttpServer
+ *
+ * @author william.liangf
+ */
+public abstract class AbstractHttpServer implements HttpServer {
+
+ private final URL url;
+
+ private final HttpHandler handler;
+
+ private volatile boolean closed;
+
+ public AbstractHttpServer(URL url, HttpHandler handler){
+ if (url == null) {
+ throw new IllegalArgumentException("url == null");
+ }
+ if (handler == null) {
+ throw new IllegalArgumentException("handler == null");
+ }
+ this.url = url;
+ this.handler = handler;
+ }
+
+ public HttpHandler getHttpHandler() {
+ return handler;
+ }
+
+ public URL getUrl() {
+ return url;
+ }
+
+ public void reset(URL url) {
+ }
+
+ public boolean isBound() {
+ return true;
+ }
+
+ public InetSocketAddress getLocalAddress() {
+ return url.toInetSocketAddress();
+ }
+
+ public void close() {
+ closed = true;
+ }
+
+ public void close(int timeout) {
+ close();
+ }
+
+ public boolean isClosed() {
+ return closed;
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting-http/src/main/resources/META-INF/services/com.alibaba.dubbo.remoting.http.HttpBinder b/dubbo-remoting-http/src/main/resources/META-INF/services/com.alibaba.dubbo.remoting.http.HttpBinder
new file mode 100644
index 0000000..46f65c4
--- /dev/null
+++ b/dubbo-remoting-http/src/main/resources/META-INF/services/com.alibaba.dubbo.remoting.http.HttpBinder
@@ -0,0 +1,2 @@
+com.alibaba.dubbo.remoting.http.servlet.ServletHttpBinder
+com.alibaba.dubbo.remoting.http.jetty.JettyHttpBinder
\ No newline at end of file
diff --git a/dubbo-remoting-mina/pom.xml b/dubbo-remoting-mina/pom.xml
new file mode 100644
index 0000000..b11d121
--- /dev/null
+++ b/dubbo-remoting-mina/pom.xml
@@ -0,0 +1,46 @@
+<!--
+ - Copyright 1999-2011 Alibaba Group.
+ -
+ - Licensed under the Apache License, Version 2.0 (the "License");
+ - you may not use this file except in compliance with the License.
+ - You may obtain a copy of the License at
+ -
+ - http://www.apache.org/licenses/LICENSE-2.0
+ -
+ - Unless required by applicable law or agreed to in writing, software
+ - distributed under the License is distributed on an "AS IS" BASIS,
+ - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ - See the License for the specific language governing permissions and
+ - limitations under the License.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>com.alibaba</groupId>
+ <artifactId>dubbo-parent</artifactId>
+ <version>2.0.8</version>
+ </parent>
+ <artifactId>dubbo-remoting-mina</artifactId>
+ <packaging>jar</packaging>
+ <name>Dubbo Mina Remoting Module</name>
+ <description>The mina remoting module of dubbo project</description>
+ <properties>
+ <skip_maven_deploy>false</skip_maven_deploy>
+ </properties>
+ <dependencies>
+ <dependency>
+ <groupId>com.alibaba</groupId>
+ <artifactId>dubbo-remoting</artifactId>
+ <version>${project.parent.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.mina</groupId>
+ <artifactId>mina-core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ </dependency>
+ </dependencies>
+</project>
\ No newline at end of file
diff --git a/dubbo-remoting-mina/src/main/java/com/alibaba/dubbo/remoting/transport/mina/MinaChannel.java b/dubbo-remoting-mina/src/main/java/com/alibaba/dubbo/remoting/transport/mina/MinaChannel.java
new file mode 100644
index 0000000..12fd662
--- /dev/null
+++ b/dubbo-remoting-mina/src/main/java/com/alibaba/dubbo/remoting/transport/mina/MinaChannel.java
@@ -0,0 +1,172 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting.transport.mina;
+
+import java.net.InetSocketAddress;
+
+import org.apache.mina.common.IoSession;
+import org.apache.mina.common.WriteFuture;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.logger.Logger;
+import com.alibaba.dubbo.common.logger.LoggerFactory;
+import com.alibaba.dubbo.remoting.ChannelHandler;
+import com.alibaba.dubbo.remoting.RemotingException;
+import com.alibaba.dubbo.remoting.transport.AbstractChannel;
+
+/**
+ * MinaChannel
+ *
+ * @author qian.lei
+ * @author william.liangf
+ */
+final class MinaChannel extends AbstractChannel {
+
+ private static final Logger logger = LoggerFactory.getLogger(MinaChannel.class);
+
+ private static final String CHANNEL_KEY = MinaChannel.class.getName() + ".CHANNEL";
+
+ private final IoSession session;
+
+ private MinaChannel(IoSession session, URL url, ChannelHandler handler){
+ super(url, handler);
+ if (session == null) {
+ throw new IllegalArgumentException("mina session == null");
+ }
+ this.session = session;
+ }
+
+ static MinaChannel getOrAddChannel(IoSession session, URL url, ChannelHandler handler) {
+ if (session == null) {
+ return null;
+ }
+ MinaChannel ret = (MinaChannel) session.getAttribute(CHANNEL_KEY);
+ if (ret == null) {
+ ret = new MinaChannel(session, url, handler);
+ if (session.isConnected()) {
+ MinaChannel old = (MinaChannel) session.setAttribute(CHANNEL_KEY, ret);
+ if (old != null) {
+ session.setAttribute(CHANNEL_KEY, old);
+ ret = old;
+ }
+ }
+ }
+ return ret;
+ }
+
+ static void removeChannelIfDisconnectd(IoSession session) {
+ if (session != null && ! session.isConnected()) {
+ session.removeAttribute(CHANNEL_KEY);
+ }
+ }
+
+ public InetSocketAddress getLocalAddress() {
+ return (InetSocketAddress) session.getLocalAddress();
+ }
+
+ public InetSocketAddress getRemoteAddress() {
+ return (InetSocketAddress) session.getRemoteAddress();
+ }
+
+ public boolean isConnected() {
+ return session.isConnected();
+ }
+
+ public void send(Object message, boolean sent) throws RemotingException {
+ super.send(message, sent);
+
+ boolean success = true;
+ int timeout = 0;
+ try {
+ WriteFuture future = session.write(message);
+ if (sent) {
+ timeout = getUrl().getPositiveParameter(Constants.TIMEOUT_KEY, Constants.DEFAULT_TIMEOUT);
+ success = future.join(timeout);
+ }
+ } catch (Throwable e) {
+ throw new RemotingException(this, "Failed to send message " + message + " to " + getRemoteAddress() + ", cause: " + e.getMessage(), e);
+ }
+
+ if(!success) {
+ throw new RemotingException(this, "Failed to send message " + message + " to " + getRemoteAddress()
+ + "in timeout(" + timeout + "ms) limit");
+ }
+ }
+
+ public void close() {
+ try {
+ super.close();
+ } catch (Exception e) {
+ logger.warn(e.getMessage(), e);
+ }
+ try {
+ removeChannelIfDisconnectd(session);
+ } catch (Exception e) {
+ logger.warn(e.getMessage(), e);
+ }
+ try {
+ if (logger.isInfoEnabled()) {
+ logger.info("CLose mina channel " + session);
+ }
+ session.close();
+ } catch (Exception e) {
+ logger.warn(e.getMessage(), e);
+ }
+ }
+
+ public boolean hasAttribute(String key) {
+ return session.containsAttribute(key);
+ }
+
+ public Object getAttribute(String key) {
+ return session.getAttribute(key);
+ }
+
+ public void setAttribute(String key, Object value) {
+ session.setAttribute(key, value);
+ }
+
+ public void removeAttribute(String key) {
+ session.removeAttribute(key);
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((session == null) ? 0 : session.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) return true;
+ if (obj == null) return false;
+ if (getClass() != obj.getClass()) return false;
+ MinaChannel other = (MinaChannel) obj;
+ if (session == null) {
+ if (other.session != null) return false;
+ } else if (!session.equals(other.session)) return false;
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ return "MinaChannel [session=" + session + "]";
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting-mina/src/main/java/com/alibaba/dubbo/remoting/transport/mina/MinaClient.java b/dubbo-remoting-mina/src/main/java/com/alibaba/dubbo/remoting/transport/mina/MinaClient.java
new file mode 100644
index 0000000..a872f34
--- /dev/null
+++ b/dubbo-remoting-mina/src/main/java/com/alibaba/dubbo/remoting/transport/mina/MinaClient.java
@@ -0,0 +1,181 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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;
+import com.alibaba.dubbo.remoting.transport.handler.ChannelHandlers;
+
+/**
+ * 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));
+ }
+
+ protected static ChannelHandler wrapChannelHandler(URL url, ChannelHandler handler){
+ url = url.addParameter(Constants.THREAD_NAME_KEY, CLIENT_THREAD_POOL_NAME)
+ .addParameter(Constants.THREADPOOL_KEY, Constants.DEFAULT_CLIENT_THREADPOOL);
+ return ChannelHandlers.wrap(handler, url);
+ }
+
+ @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, "Failed to connect to server " + getRemoteAddress() + " client-side timeout "
+ + getTimeout() + "ms (elapsed: " + (System.currentTimeMillis() - start)
+ + "ms) from netty client " + NetUtils.getLocalHost() + " using dubbo version "
+ + Version.getVersion() + ", cause: " + e.getMessage(), e);
+ }
+ Throwable e = exception.get();
+ if (e != null) {
+ throw e;
+ }
+ }
+
+ @Override
+ protected void doDisConnect() throws Throwable {
+ try {
+ MinaChannel.removeChannelIfDisconnectd(session);
+ } catch (Throwable t) {
+ logger.warn(t.getMessage());
+ }
+ }
+
+ @Override
+ protected void doClose() throws Throwable {
+ //release mina resouces.
+ }
+
+ @Override
+ protected Channel getChannel() {
+ IoSession s = session;
+ if (s == null || ! s.isConnected())
+ return null;
+ return MinaChannel.getOrAddChannel(s, getUrl(), this);
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting-mina/src/main/java/com/alibaba/dubbo/remoting/transport/mina/MinaCodecAdapter.java b/dubbo-remoting-mina/src/main/java/com/alibaba/dubbo/remoting/transport/mina/MinaCodecAdapter.java
new file mode 100644
index 0000000..48c52f7
--- /dev/null
+++ b/dubbo-remoting-mina/src/main/java/com/alibaba/dubbo/remoting/transport/mina/MinaCodecAdapter.java
@@ -0,0 +1,193 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting.transport.mina;
+
+import java.io.IOException;
+
+import org.apache.mina.common.ByteBuffer;
+import org.apache.mina.common.IoSession;
+import org.apache.mina.filter.codec.ProtocolCodecFactory;
+import org.apache.mina.filter.codec.ProtocolDecoder;
+import org.apache.mina.filter.codec.ProtocolDecoderOutput;
+import org.apache.mina.filter.codec.ProtocolEncoder;
+import org.apache.mina.filter.codec.ProtocolEncoderOutput;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.io.Bytes;
+import com.alibaba.dubbo.common.io.UnsafeByteArrayInputStream;
+import com.alibaba.dubbo.common.io.UnsafeByteArrayOutputStream;
+import com.alibaba.dubbo.remoting.Channel;
+import com.alibaba.dubbo.remoting.ChannelHandler;
+import com.alibaba.dubbo.remoting.Codec;
+import com.alibaba.dubbo.remoting.exchange.Response;
+
+/**
+ * MinaCodecAdapter.
+ *
+ * @author qian.lei
+ */
+final class MinaCodecAdapter implements ProtocolCodecFactory {
+
+ private static final String BUFFER_KEY = MinaCodecAdapter.class.getName() + ".BUFFER";
+
+ private final ProtocolEncoder encoder = new InternalEncoder();
+
+ private final ProtocolDecoder decoder = new InternalDecoder();
+
+ private final Codec upstreamCodec;
+ private final Codec downstreamCodec;
+
+ private final URL url;
+
+ private final ChannelHandler handler;
+
+ private final int bufferSize;
+
+ public MinaCodecAdapter(Codec codec, URL url, ChannelHandler handler){
+ this(codec,codec,url,handler);
+ }
+
+ /**
+ * server 端如果有消息发送需要分开codec,默认的上行code是dubbo1兼容的
+ */
+ public MinaCodecAdapter(Codec upstreamCodec, Codec downstreamCodec, URL url, ChannelHandler handler){
+ this.upstreamCodec = upstreamCodec;
+ this.downstreamCodec = downstreamCodec;
+ this.url = url;
+ this.handler = handler;
+ int b = url.getPositiveParameter(Constants.BUFFER_KEY, Constants.DEFAULT_BUFFER_SIZE);
+ this.bufferSize = b >= Constants.MIN_BUFFER_SIZE && b <= Constants.MAX_BUFFER_SIZE ? b : Constants.DEFAULT_BUFFER_SIZE;
+ }
+
+ public ProtocolEncoder getEncoder() {
+ return encoder;
+ }
+
+ public ProtocolDecoder getDecoder() {
+ return decoder;
+ }
+
+ private class InternalEncoder implements ProtocolEncoder {
+
+ public void dispose(IoSession session) throws Exception {
+ }
+
+ public void encode(IoSession session, Object msg, ProtocolEncoderOutput out) throws Exception {
+ UnsafeByteArrayOutputStream os = new UnsafeByteArrayOutputStream(1024); // 不需要关闭
+ MinaChannel channel = MinaChannel.getOrAddChannel(session, url, handler);
+ try {
+ if(!(msg instanceof Response)){
+ downstreamCodec.encode(channel, os, msg);
+ }else{
+ upstreamCodec.encode(channel, os, msg);
+ }
+
+ } finally {
+ MinaChannel.removeChannelIfDisconnectd(session);
+ }
+ out.write(ByteBuffer.wrap(os.toByteArray()));
+ out.flush();
+ }
+ }
+
+ private class InternalDecoder implements ProtocolDecoder {
+
+ public void decode(IoSession session, ByteBuffer in, ProtocolDecoderOutput out) throws Exception {
+ int readable = in.limit();
+ if (readable <= 0) return;
+
+ int off, limit;
+ byte[] buf;
+ // load buffer from context.
+ Object[] tmp = (Object[]) session.getAttribute(BUFFER_KEY);
+ if (tmp == null) {
+ buf = new byte[bufferSize];
+ off = limit = 0;
+ } else {
+ buf = (byte[]) tmp[0];
+ off = (Integer) tmp[1];
+ limit = (Integer) tmp[2];
+ }
+
+ Channel channel = MinaChannel.getOrAddChannel(session, url, handler);
+ boolean remaining = true;
+ Object msg;
+ UnsafeByteArrayInputStream bis;
+ try {
+ do {
+ // read data into buffer.
+ int read = Math.min(readable, buf.length - limit);
+ in.get(buf, limit, read);
+ limit += read;
+ readable -= read;
+ bis = new UnsafeByteArrayInputStream(buf, off, limit - off); // 不需要关闭
+ // decode object.
+ do {
+ try {
+ msg = upstreamCodec.decode(channel, bis);
+ } catch (IOException e) {
+ remaining = false;
+ throw e;
+ }
+ if (msg == Codec.NEED_MORE_INPUT) {
+ if (off == 0) {
+ if (readable > 0) {
+ buf = Bytes.copyOf(buf, buf.length << 1);
+ }
+ } else {
+ int len = limit - off;
+ System.arraycopy(buf, off, buf, 0, len);
+ off = 0;
+ limit = len;
+ }
+ break;
+ } else {
+ int pos = bis.position();
+ if (pos == off) {
+ remaining = false;
+ throw new IOException("Decode without read data.");
+ }
+ if (msg != null) {
+ out.write(msg);
+ }
+ off = pos;
+ }
+ } while (bis.available() > 0);
+ } while (readable > 0);
+ } finally {
+ if (remaining) {
+ int len = limit - off;
+ if (len < buf.length / 2) {
+ System.arraycopy(buf, off, buf, 0, len);
+ off = 0;
+ limit = len;
+ }
+ session.setAttribute(BUFFER_KEY, new Object[] { buf, off, limit });
+ } else {
+ session.removeAttribute(BUFFER_KEY);
+ }
+ MinaChannel.removeChannelIfDisconnectd(session);
+ }
+ }
+
+ public void dispose(IoSession session) throws Exception {
+ }
+
+ public void finishDecode(IoSession session, ProtocolDecoderOutput out) throws Exception {
+ }
+ }
+}
\ No newline at end of file
diff --git a/dubbo-remoting-mina/src/main/java/com/alibaba/dubbo/remoting/transport/mina/MinaHandler.java b/dubbo-remoting-mina/src/main/java/com/alibaba/dubbo/remoting/transport/mina/MinaHandler.java
new file mode 100644
index 0000000..75108f5
--- /dev/null
+++ b/dubbo-remoting-mina/src/main/java/com/alibaba/dubbo/remoting/transport/mina/MinaHandler.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting.transport.mina;
+
+import org.apache.mina.common.IoHandlerAdapter;
+import org.apache.mina.common.IoSession;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.remoting.ChannelHandler;
+
+/**
+ * MinaHandler
+ *
+ * @author william.liangf
+ */
+public class MinaHandler extends IoHandlerAdapter {
+
+ private final URL url;
+
+ private final ChannelHandler handler;
+
+ public MinaHandler(URL url, ChannelHandler handler) {
+ if (url == null) {
+ throw new IllegalArgumentException("url == null");
+ }
+ if (handler == null) {
+ throw new IllegalArgumentException("handler == null");
+ }
+ this.url = url;
+ this.handler = handler;
+ }
+
+ @Override
+ public void sessionOpened(IoSession session) throws Exception {
+ MinaChannel channel = MinaChannel.getOrAddChannel(session, url, handler);
+ try {
+ handler.connected(channel);
+ } finally {
+ MinaChannel.removeChannelIfDisconnectd(session);
+ }
+ }
+
+ @Override
+ public void sessionClosed(IoSession session) throws Exception {
+ MinaChannel channel = MinaChannel.getOrAddChannel(session, url, handler);
+ try {
+ handler.disconnected(channel);
+ } finally {
+ MinaChannel.removeChannelIfDisconnectd(session);
+ }
+ }
+
+ @Override
+ public void messageReceived(IoSession session, Object message) throws Exception {
+ MinaChannel channel = MinaChannel.getOrAddChannel(session, url, handler);
+ try {
+ handler.received(channel, message);
+ } finally {
+ MinaChannel.removeChannelIfDisconnectd(session);
+ }
+ }
+
+ @Override
+ public void messageSent(IoSession session, Object message) throws Exception {
+ MinaChannel channel = MinaChannel.getOrAddChannel(session, url, handler);
+ try {
+ handler.sent(channel, message);
+ } finally {
+ MinaChannel.removeChannelIfDisconnectd(session);
+ }
+ }
+
+ @Override
+ public void exceptionCaught(IoSession session, Throwable cause) throws Exception {
+ MinaChannel channel = MinaChannel.getOrAddChannel(session, url, handler);
+ try {
+ handler.caught(channel, cause);
+ } finally {
+ MinaChannel.removeChannelIfDisconnectd(session);
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting-mina/src/main/java/com/alibaba/dubbo/remoting/transport/mina/MinaServer.java b/dubbo-remoting-mina/src/main/java/com/alibaba/dubbo/remoting/transport/mina/MinaServer.java
new file mode 100644
index 0000000..af6942c
--- /dev/null
+++ b/dubbo-remoting-mina/src/main/java/com/alibaba/dubbo/remoting/transport/mina/MinaServer.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting.transport.mina;
+
+import java.net.InetSocketAddress;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.concurrent.Executors;
+
+import org.apache.mina.common.IoSession;
+import org.apache.mina.common.ThreadModel;
+import org.apache.mina.filter.codec.ProtocolCodecFilter;
+import org.apache.mina.transport.socket.nio.SocketAcceptor;
+import org.apache.mina.transport.socket.nio.SocketAcceptorConfig;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.logger.Logger;
+import com.alibaba.dubbo.common.logger.LoggerFactory;
+import com.alibaba.dubbo.common.utils.NamedThreadFactory;
+import com.alibaba.dubbo.remoting.Channel;
+import com.alibaba.dubbo.remoting.ChannelHandler;
+import com.alibaba.dubbo.remoting.RemotingException;
+import com.alibaba.dubbo.remoting.transport.AbstractServer;
+import com.alibaba.dubbo.remoting.transport.handler.ChannelHandlers;
+
+/**
+ * MinaServer
+ *
+ * @author qian.lei
+ * @author william.liangf
+ * @author ding.lid
+ */
+public class MinaServer extends AbstractServer {
+
+ private static final Logger logger = LoggerFactory.getLogger(MinaServer.class);
+
+ private SocketAcceptor acceptor;
+
+ public MinaServer(URL url, ChannelHandler handler) throws RemotingException{
+ super(url, ChannelHandlers.wrap(handler, url.addParameter(Constants.THREAD_NAME_KEY, SERVER_THREAD_POOL_NAME)));
+ }
+
+ @Override
+ protected void doOpen() throws Throwable {
+ // set thread pool.
+ acceptor = new SocketAcceptor(getUrl().getPositiveParameter(Constants.IO_THREADS_KEY, Constants.DEFAULT_IO_THREADS),
+ Executors.newCachedThreadPool(new NamedThreadFactory("MinaServerWorker",
+ true)));
+ // config
+ SocketAcceptorConfig cfg = (SocketAcceptorConfig) acceptor.getDefaultConfig();
+ cfg.setThreadModel(ThreadModel.MANUAL);
+ // set codec.
+ acceptor.getFilterChain().addLast("codec", new ProtocolCodecFilter(new MinaCodecAdapter(getCodec(), getDownstreamCodec(), getUrl(), this)));
+
+ acceptor.bind(getBindAddress(), new MinaHandler(getUrl(), this));
+ }
+
+ @Override
+ protected void doClose() throws Throwable {
+ try {
+ if (acceptor != null) {
+ acceptor.unbind(getBindAddress());
+ }
+ } catch (Throwable e) {
+ logger.warn(e.getMessage(), e);
+ }
+ }
+
+ public Collection<Channel> getChannels() {
+ Set<IoSession> sessions = acceptor.getManagedSessions(getBindAddress());
+ Collection<Channel> channels = new HashSet<Channel>();
+ for (IoSession session : sessions) {
+ if (session.isConnected()) {
+ channels.add(MinaChannel.getOrAddChannel(session, getUrl(), this));
+ }
+ }
+ return channels;
+ }
+
+ public Channel getChannel(InetSocketAddress remoteAddress) {
+ Set<IoSession> sessions = acceptor.getManagedSessions(getBindAddress());
+ for (IoSession session : sessions) {
+ if (session.getRemoteAddress().equals(remoteAddress)) {
+ return MinaChannel.getOrAddChannel(session, getUrl(), this);
+ }
+ }
+ return null;
+ }
+
+ public boolean isBound() {
+ return acceptor.isManaged(getBindAddress());
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting-mina/src/main/java/com/alibaba/dubbo/remoting/transport/mina/MinaTransporter.java b/dubbo-remoting-mina/src/main/java/com/alibaba/dubbo/remoting/transport/mina/MinaTransporter.java
new file mode 100644
index 0000000..6b7c18e
--- /dev/null
+++ b/dubbo-remoting-mina/src/main/java/com/alibaba/dubbo/remoting/transport/mina/MinaTransporter.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting.transport.mina;
+
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.remoting.ChannelHandler;
+import com.alibaba.dubbo.remoting.Client;
+import com.alibaba.dubbo.remoting.RemotingException;
+import com.alibaba.dubbo.remoting.Server;
+import com.alibaba.dubbo.remoting.Transporter;
+
+/**
+ * @author ding.lid
+ */
+@Extension(MinaTransporter.NAME)
+public class MinaTransporter implements Transporter {
+
+ public static final String NAME = "mina";
+
+ public Server bind(URL url, ChannelHandler handler) throws RemotingException {
+ return new MinaServer(url, handler);
+ }
+
+ public Client connect(URL url, ChannelHandler handler) throws RemotingException {
+ return new MinaClient(url, handler);
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting-mina/src/main/resources/META-INF/services/com.alibaba.dubbo.remoting.Transporter b/dubbo-remoting-mina/src/main/resources/META-INF/services/com.alibaba.dubbo.remoting.Transporter
new file mode 100644
index 0000000..aaaf989
--- /dev/null
+++ b/dubbo-remoting-mina/src/main/resources/META-INF/services/com.alibaba.dubbo.remoting.Transporter
@@ -0,0 +1 @@
+com.alibaba.dubbo.remoting.transport.mina.MinaTransporter
\ No newline at end of file
diff --git a/dubbo-remoting-mina/src/test/java/com/alibaba/remoting/transport/mina/ClientToServerTest.java b/dubbo-remoting-mina/src/test/java/com/alibaba/remoting/transport/mina/ClientToServerTest.java
new file mode 100644
index 0000000..d2b496f
--- /dev/null
+++ b/dubbo-remoting-mina/src/test/java/com/alibaba/remoting/transport/mina/ClientToServerTest.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.remoting.transport.mina;
+
+import junit.framework.Assert;
+import junit.framework.TestCase;
+
+import org.junit.Test;
+
+import com.alibaba.dubbo.remoting.RemotingException;
+import com.alibaba.dubbo.remoting.exchange.ExchangeChannel;
+import com.alibaba.dubbo.remoting.exchange.ExchangeServer;
+import com.alibaba.dubbo.remoting.exchange.ResponseFuture;
+import com.alibaba.dubbo.remoting.exchange.support.Replier;
+
+/**
+ * ClientToServer
+ *
+ * @author william.liangf
+ */
+public abstract class ClientToServerTest extends TestCase {
+
+ protected static final String LOCALHOST = "127.0.0.1";
+
+ protected ExchangeServer server;
+
+ protected ExchangeChannel client;
+
+ protected WorldHandler handler = new WorldHandler();
+
+ protected abstract ExchangeServer newServer(int port, Replier<?> receiver) throws RemotingException;
+
+ protected abstract ExchangeChannel newClient(int port) throws RemotingException;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ int port = (int) (1000 * Math.random() + 10000);
+ server = newServer(port, handler);
+ client = newClient(port);
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ super.tearDown();
+ try {
+ if (server != null)
+ server.close();
+ } finally {
+ if (client != null)
+ client.close();
+ }
+ }
+
+ @Test
+ public void testFuture() throws Exception {
+ ResponseFuture future = client.request(new World("world"));
+ Hello result = (Hello)future.get();
+ Assert.assertEquals("hello,world", result.getName());
+ }
+
+// @Test
+// public void testCallback() throws Exception {
+// final Object waitter = new Object();
+// client.invoke(new World("world"), new InvokeCallback<Hello>() {
+// public void callback(Hello result) {
+// Assert.assertEquals("hello,world", result.getName());
+// synchronized (waitter) {
+// waitter.notifyAll();
+// }
+// }
+// public void onException(Throwable exception) {
+// }
+// });
+// synchronized (waitter) {
+// waitter.wait();
+// }
+// }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting-mina/src/test/java/com/alibaba/remoting/transport/mina/ClientsTest.java b/dubbo-remoting-mina/src/test/java/com/alibaba/remoting/transport/mina/ClientsTest.java
new file mode 100644
index 0000000..ed7e059
--- /dev/null
+++ b/dubbo-remoting-mina/src/test/java/com/alibaba/remoting/transport/mina/ClientsTest.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.remoting.transport.mina;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.fail;
+import static org.junit.matchers.JUnitMatchers.containsString;
+
+import org.junit.Test;
+
+import com.alibaba.dubbo.common.ExtensionLoader;
+import com.alibaba.dubbo.remoting.Transporter;
+import com.alibaba.dubbo.remoting.transport.mina.MinaTransporter;
+
+/**
+ * @author ding.lid
+ */
+public class ClientsTest {
+
+ @Test
+ public void testGetTransportEmpty() {
+ try {
+ ExtensionLoader.getExtensionLoader(Transporter.class).getExtension("");
+ fail();
+ } catch (IllegalArgumentException expected) {
+ assertThat(expected.getMessage(), containsString("Extension name == null"));
+ }
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testGetTransportNull() {
+ String name = null;
+ ExtensionLoader.getExtensionLoader(Transporter.class).getExtension(name);
+ }
+
+ @Test
+ public void testGetTransport1() {
+ String name = "mina";
+ assertEquals(MinaTransporter.class, ExtensionLoader.getExtensionLoader(Transporter.class).getExtension(name).getClass());
+ }
+
+ @Test(expected = IllegalStateException.class)
+ public void testGetTransportWrong() {
+ String name = "nety";
+ assertNull(ExtensionLoader.getExtensionLoader(Transporter.class).getExtension(name).getClass());
+ }
+}
\ No newline at end of file
diff --git a/dubbo-remoting-mina/src/test/java/com/alibaba/remoting/transport/mina/Hello.java b/dubbo-remoting-mina/src/test/java/com/alibaba/remoting/transport/mina/Hello.java
new file mode 100644
index 0000000..eca6042
--- /dev/null
+++ b/dubbo-remoting-mina/src/test/java/com/alibaba/remoting/transport/mina/Hello.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.remoting.transport.mina;
+
+import java.io.Serializable;
+
+/**
+ * Result
+ *
+ * @author william.liangf
+ */
+public class Hello implements Serializable {
+
+ private static final long serialVersionUID = 8563900571013747774L;
+
+ private String name;
+
+ public Hello() {
+ }
+
+ public Hello(String name) {
+ this.name = name;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting-mina/src/test/java/com/alibaba/remoting/transport/mina/MinaClientToServerTest.java b/dubbo-remoting-mina/src/test/java/com/alibaba/remoting/transport/mina/MinaClientToServerTest.java
new file mode 100644
index 0000000..b0313ce
--- /dev/null
+++ b/dubbo-remoting-mina/src/test/java/com/alibaba/remoting/transport/mina/MinaClientToServerTest.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.remoting.transport.mina;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.remoting.RemotingException;
+import com.alibaba.dubbo.remoting.exchange.ExchangeChannel;
+import com.alibaba.dubbo.remoting.exchange.ExchangeServer;
+import com.alibaba.dubbo.remoting.exchange.Exchangers;
+import com.alibaba.dubbo.remoting.exchange.support.Replier;
+
+/**
+ * MinaServerClientTest
+ *
+ * @author william.liangf
+ */
+public class MinaClientToServerTest extends ClientToServerTest {
+
+ @Override
+ protected ExchangeServer newServer(int port, Replier<?> receiver) throws RemotingException {
+ return Exchangers.bind(URL.valueOf("exchange://localhost:" + port + "?server=mina"), receiver);
+ }
+
+ @Override
+ protected ExchangeChannel newClient(int port) throws RemotingException {
+ return Exchangers.connect(URL.valueOf("exchange://localhost:" + port + "?client=mina"));
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting-mina/src/test/java/com/alibaba/remoting/transport/mina/World.java b/dubbo-remoting-mina/src/test/java/com/alibaba/remoting/transport/mina/World.java
new file mode 100644
index 0000000..bfec203
--- /dev/null
+++ b/dubbo-remoting-mina/src/test/java/com/alibaba/remoting/transport/mina/World.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.remoting.transport.mina;
+
+import java.io.Serializable;
+
+/**
+ * Data
+ *
+ * @author william.liangf
+ */
+public class World implements Serializable {
+
+ private static final long serialVersionUID = 8563900571013747774L;
+
+ private String name;
+
+ public World() {
+ }
+
+ public World(String name) {
+ this.name = name;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting-mina/src/test/java/com/alibaba/remoting/transport/mina/WorldHandler.java b/dubbo-remoting-mina/src/test/java/com/alibaba/remoting/transport/mina/WorldHandler.java
new file mode 100644
index 0000000..1175ab3
--- /dev/null
+++ b/dubbo-remoting-mina/src/test/java/com/alibaba/remoting/transport/mina/WorldHandler.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.remoting.transport.mina;
+
+import com.alibaba.dubbo.remoting.RemotingException;
+import com.alibaba.dubbo.remoting.exchange.ExchangeChannel;
+import com.alibaba.dubbo.remoting.exchange.support.Replier;
+
+/**
+ * DataHandler
+ *
+ * @author william.liangf
+ */
+public class WorldHandler implements Replier<World> {
+
+ public Class<World> interest() {
+ return World.class;
+ }
+
+ public Object reply(ExchangeChannel channel, World msg) throws RemotingException {
+ return new Hello("hello," + msg.getName());
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting-netty/pom.xml b/dubbo-remoting-netty/pom.xml
new file mode 100644
index 0000000..b4190d0
--- /dev/null
+++ b/dubbo-remoting-netty/pom.xml
@@ -0,0 +1,42 @@
+<!--
+ - Copyright 1999-2011 Alibaba Group.
+ -
+ - Licensed under the Apache License, Version 2.0 (the "License");
+ - you may not use this file except in compliance with the License.
+ - You may obtain a copy of the License at
+ -
+ - http://www.apache.org/licenses/LICENSE-2.0
+ -
+ - Unless required by applicable law or agreed to in writing, software
+ - distributed under the License is distributed on an "AS IS" BASIS,
+ - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ - See the License for the specific language governing permissions and
+ - limitations under the License.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>com.alibaba</groupId>
+ <artifactId>dubbo-parent</artifactId>
+ <version>2.0.8</version>
+ </parent>
+ <artifactId>dubbo-remoting-netty</artifactId>
+ <packaging>jar</packaging>
+ <name>Dubbo Netty Remoting Module</name>
+ <description>The netty remoting module of dubbo project</description>
+ <properties>
+ <skip_maven_deploy>false</skip_maven_deploy>
+ </properties>
+ <dependencies>
+ <dependency>
+ <groupId>com.alibaba</groupId>
+ <artifactId>dubbo-remoting</artifactId>
+ <version>${project.parent.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.jboss.netty</groupId>
+ <artifactId>netty</artifactId>
+ </dependency>
+ </dependencies>
+</project>
\ No newline at end of file
diff --git a/dubbo-remoting-netty/src/main/java/com/alibaba/dubbo/remoting/transport/netty/NettyChannel.java b/dubbo-remoting-netty/src/main/java/com/alibaba/dubbo/remoting/transport/netty/NettyChannel.java
new file mode 100644
index 0000000..816224c
--- /dev/null
+++ b/dubbo-remoting-netty/src/main/java/com/alibaba/dubbo/remoting/transport/netty/NettyChannel.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.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);
+ }
+ } catch (Throwable e) {
+ throw new RemotingException(this, "Failed to send message " + message + " to " + getRemoteAddress() + ", cause: " + e.getMessage(), e);
+ }
+
+ if(!success) {
+ throw new RemotingException(this, "Failed to send message " + message + " to " + getRemoteAddress()
+ + "in timeout(" + timeout + "ms) limit");
+ }
+ }
+
+ public void close() {
+ try {
+ super.close();
+ } catch (Exception e) {
+ logger.warn(e.getMessage(), e);
+ }
+ try {
+ removeChannelIfDisconnected(channel);
+ } catch (Exception e) {
+ logger.warn(e.getMessage(), e);
+ }
+ try {
+ attributes.clear();
+ } catch (Exception e) {
+ logger.warn(e.getMessage(), e);
+ }
+ try {
+ if (logger.isInfoEnabled()) {
+ logger.info("Close netty channel " + channel);
+ }
+ channel.close();
+ } catch (Exception e) {
+ logger.warn(e.getMessage(), e);
+ }
+ }
+
+ public boolean hasAttribute(String key) {
+ return attributes.containsKey(key);
+ }
+
+ public Object getAttribute(String key) {
+ return attributes.get(key);
+ }
+
+ public void setAttribute(String key, Object value) {
+ if (value == null) { // The null value unallowed in the ConcurrentHashMap.
+ attributes.remove(key);
+ } else {
+ attributes.put(key, value);
+ }
+ }
+
+ public void removeAttribute(String key) {
+ attributes.remove(key);
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((channel == null) ? 0 : channel.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) return true;
+ if (obj == null) return false;
+ if (getClass() != obj.getClass()) return false;
+ NettyChannel other = (NettyChannel) obj;
+ if (channel == null) {
+ if (other.channel != null) return false;
+ } else if (!channel.equals(other.channel)) return false;
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ return "NettyChannel [channel=" + channel + "]";
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting-netty/src/main/java/com/alibaba/dubbo/remoting/transport/netty/NettyClient.java b/dubbo-remoting-netty/src/main/java/com/alibaba/dubbo/remoting/transport/netty/NettyClient.java
new file mode 100644
index 0000000..517bfca
--- /dev/null
+++ b/dubbo-remoting-netty/src/main/java/com/alibaba/dubbo/remoting/transport/netty/NettyClient.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.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;
+import com.alibaba.dubbo.remoting.transport.handler.ChannelHandlers;
+
+/**
+ * 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));
+ }
+ protected static ChannelHandler wrapChannelHandler(URL url, ChannelHandler handler){
+ url = url.addParameter(Constants.THREAD_NAME_KEY, CLIENT_THREAD_POOL_NAME)
+ .addParameter(Constants.THREADPOOL_KEY, Constants.DEFAULT_CLIENT_THREADPOOL);
+ return ChannelHandlers.wrap(handler, url);
+ }
+ @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, "Failed to connect to server " + getRemoteAddress() + ", error message is:" + future.getCause().getMessage(), future.getCause());
+ } else {
+ throw new RemotingException(this, "Failed to connect to server " + getRemoteAddress() + " client-side timeout "
+ + getConnectTimeout() + "ms (elapsed: " + (System.currentTimeMillis() - start)
+ + "ms) from netty client " + NetUtils.getLocalHost() + " using dubbo version "
+ + Version.getVersion());
+ }
+ }finally{
+ if (! isConnected()) {
+ future.cancel();
+ }
+ }
+ }
+
+ @Override
+ protected void doDisConnect() throws Throwable {
+ try {
+ NettyChannel.removeChannelIfDisconnected(channel);
+ } catch (Throwable t) {
+ logger.warn(t.getMessage());
+ }
+ }
+
+ @Override
+ protected void doClose() throws Throwable {
+ /*try {
+ bootstrap.releaseExternalResources();
+ } catch (Throwable t) {
+ logger.warn(t.getMessage());
+ }*/
+ }
+
+ @Override
+ protected com.alibaba.dubbo.remoting.Channel getChannel() {
+ Channel c = channel;
+ if (c == null || ! c.isConnected())
+ return null;
+ return NettyChannel.getOrAddChannel(c, getUrl(), this);
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting-netty/src/main/java/com/alibaba/dubbo/remoting/transport/netty/NettyCodecAdapter.java b/dubbo-remoting-netty/src/main/java/com/alibaba/dubbo/remoting/transport/netty/NettyCodecAdapter.java
new file mode 100644
index 0000000..c10b9dd
--- /dev/null
+++ b/dubbo-remoting-netty/src/main/java/com/alibaba/dubbo/remoting/transport/netty/NettyCodecAdapter.java
@@ -0,0 +1,203 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting.transport.netty;
+
+import java.io.IOException;
+
+import org.jboss.netty.buffer.ChannelBuffer;
+import org.jboss.netty.buffer.ChannelBuffers;
+import org.jboss.netty.channel.Channel;
+import org.jboss.netty.channel.ChannelHandler;
+import org.jboss.netty.channel.ChannelHandler.Sharable;
+import org.jboss.netty.channel.ChannelHandlerContext;
+import org.jboss.netty.channel.Channels;
+import org.jboss.netty.channel.ExceptionEvent;
+import org.jboss.netty.channel.MessageEvent;
+import org.jboss.netty.channel.SimpleChannelUpstreamHandler;
+import org.jboss.netty.handler.codec.oneone.OneToOneEncoder;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.io.Bytes;
+import com.alibaba.dubbo.common.io.UnsafeByteArrayInputStream;
+import com.alibaba.dubbo.common.io.UnsafeByteArrayOutputStream;
+import com.alibaba.dubbo.remoting.Codec;
+import com.alibaba.dubbo.remoting.exchange.Response;
+
+/**
+ * NettyCodecAdapter.
+ *
+ * @author qian.lei
+ */
+final class NettyCodecAdapter {
+
+ private final ChannelHandler encoder = new InternalEncoder();
+
+ private final ChannelHandler decoder = new InternalDecoder();
+
+ private final Codec upstreamCodec;
+ private final Codec downstreamCodec;
+
+ private final URL url;
+
+ private final int bufferSize;
+
+ private final com.alibaba.dubbo.remoting.ChannelHandler handler;
+
+ public NettyCodecAdapter(Codec codec, URL url, com.alibaba.dubbo.remoting.ChannelHandler handler){
+ this(codec, codec, url, handler);
+ }
+ /**
+ * server 端如果有消息发送需要分开codec,默认的上行code是dubbo1兼容的
+ */
+ public NettyCodecAdapter(Codec upstreamCodec, Codec downstreamCodec, URL url, com.alibaba.dubbo.remoting.ChannelHandler handler){
+ this.downstreamCodec = downstreamCodec;
+ this.upstreamCodec = upstreamCodec;
+ this.url = url;
+ this.handler = handler;
+ int b = url.getPositiveParameter(Constants.BUFFER_KEY, Constants.DEFAULT_BUFFER_SIZE);
+ this.bufferSize = b >= Constants.MIN_BUFFER_SIZE && b <= Constants.MAX_BUFFER_SIZE ? b : Constants.DEFAULT_BUFFER_SIZE;
+ }
+
+ public ChannelHandler getEncoder() {
+ return encoder;
+ }
+
+ public ChannelHandler getDecoder() {
+ return decoder;
+ }
+
+ @Sharable
+ private class InternalEncoder extends OneToOneEncoder {
+
+ @Override
+ protected Object encode(ChannelHandlerContext ctx, Channel ch, Object msg) throws Exception {
+ UnsafeByteArrayOutputStream os = new UnsafeByteArrayOutputStream(1024); // 不需要关闭
+ NettyChannel channel = NettyChannel.getOrAddChannel(ch, url, handler);
+ try {
+ if(!(msg instanceof Response)){
+ downstreamCodec.encode(channel, os, msg);
+ }else {
+ upstreamCodec.encode(channel, os, msg);
+ }
+
+ } finally {
+ NettyChannel.removeChannelIfDisconnected(ch);
+ }
+ return ChannelBuffers.wrappedBuffer(os.toByteBuffer());
+ }
+ }
+
+ private class InternalDecoder extends SimpleChannelUpstreamHandler {
+
+ private int mOffset = 0, mLimit = 0;
+
+ private byte[] mBuffer = null;
+
+ @Override
+ public void messageReceived(ChannelHandlerContext ctx, MessageEvent event) throws Exception {
+ Object o = event.getMessage();
+ if (! (o instanceof ChannelBuffer)) {
+ ctx.sendUpstream(event);
+ return;
+ }
+
+ ChannelBuffer input = (ChannelBuffer) o;
+ int readable = input.readableBytes();
+ if (readable <= 0) {
+ return;
+ }
+
+ int off, limit;
+ byte[] buf = mBuffer;
+ if (buf == null) {
+ buf = new byte[bufferSize];
+ off = limit = 0;
+ } else {
+ off = mOffset;
+ limit = mLimit;
+ }
+
+ NettyChannel channel = NettyChannel.getOrAddChannel(ctx.getChannel(), url, handler);
+ boolean remaining = true;
+ Object msg;
+ UnsafeByteArrayInputStream bis;
+ try {
+ do {
+ // read data into buffer.
+ int read = Math.min(readable, buf.length - limit);
+ input.readBytes(buf, limit, read);
+ limit += read;
+ readable -= read;
+ bis = new UnsafeByteArrayInputStream(buf, off, limit - off); // 不需要关闭
+ // decode object.
+ do {
+ try {
+ msg = upstreamCodec.decode(channel, bis);
+ } catch (IOException e) {
+ remaining = false;
+ throw e;
+ }
+ if (msg == Codec.NEED_MORE_INPUT) {
+ if (off == 0) {
+ if (readable > 0) {
+ buf = Bytes.copyOf(buf, buf.length << 1);
+ }
+ } else {
+ int len = limit - off;
+ System.arraycopy(buf, off, buf, 0, len); // adjust buffer.
+ off = 0;
+ limit = len;
+ }
+ break;
+ } else {
+ int pos = bis.position();
+ if (off == pos) {
+ remaining = false;
+ throw new IOException("Decode without read data.");
+ }
+ if (msg != null) {
+ Channels.fireMessageReceived(ctx, msg, event.getRemoteAddress());
+ }
+ off = pos;
+ }
+ } while (bis.available() > 0);
+ } while (readable > 0);
+ } finally {
+ if (remaining) {
+ int len = limit - off;
+ if (len < buf.length / 2) {
+ System.arraycopy(buf, off, buf, 0, len);
+ off = 0;
+ limit = len;
+ }
+ mBuffer = buf;
+ mOffset = off;
+ mLimit = limit;
+ } else {
+ mBuffer = null;
+ mOffset = mLimit = 0;
+ }
+ NettyChannel.removeChannelIfDisconnected(ctx.getChannel());
+ }
+ }
+
+ @Override
+ public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) throws Exception {
+ ctx.sendUpstream(e);
+ }
+ }
+}
\ No newline at end of file
diff --git a/dubbo-remoting-netty/src/main/java/com/alibaba/dubbo/remoting/transport/netty/NettyHandler.java b/dubbo-remoting-netty/src/main/java/com/alibaba/dubbo/remoting/transport/netty/NettyHandler.java
new file mode 100644
index 0000000..1a05bd1
--- /dev/null
+++ b/dubbo-remoting-netty/src/main/java/com/alibaba/dubbo/remoting/transport/netty/NettyHandler.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting.transport.netty;
+
+import java.net.InetSocketAddress;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.jboss.netty.channel.ChannelHandler.Sharable;
+import org.jboss.netty.channel.ChannelHandlerContext;
+import org.jboss.netty.channel.ChannelStateEvent;
+import org.jboss.netty.channel.ExceptionEvent;
+import org.jboss.netty.channel.MessageEvent;
+import org.jboss.netty.channel.SimpleChannelHandler;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.utils.NetUtils;
+import com.alibaba.dubbo.remoting.Channel;
+import com.alibaba.dubbo.remoting.ChannelHandler;
+
+/**
+ * NettyHandler
+ *
+ * @author william.liangf
+ */
+@Sharable
+public class NettyHandler extends SimpleChannelHandler {
+
+ private final Map<String, Channel> channels = new ConcurrentHashMap<String, Channel>(); // <ip:port, channel>
+
+ private final URL url;
+
+ private final ChannelHandler handler;
+
+ public NettyHandler(URL url, ChannelHandler handler){
+ if (url == null) {
+ throw new IllegalArgumentException("url == null");
+ }
+ if (handler == null) {
+ throw new IllegalArgumentException("handler == null");
+ }
+ this.url = url;
+ this.handler = handler;
+ }
+
+ public Map<String, Channel> getChannels() {
+ return channels;
+ }
+
+ @Override
+ public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {
+ NettyChannel channel = NettyChannel.getOrAddChannel(ctx.getChannel(), url, handler);
+ try {
+ if (channel != null) {
+ channels.put(NetUtils.toAddressString((InetSocketAddress) ctx.getChannel().getRemoteAddress()), channel);
+ }
+ handler.connected(channel);
+ } finally {
+ NettyChannel.removeChannelIfDisconnected(ctx.getChannel());
+ }
+ }
+
+ @Override
+ public void channelDisconnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {
+ NettyChannel channel = NettyChannel.getOrAddChannel(ctx.getChannel(), url, handler);
+ try {
+ channels.remove(NetUtils.toAddressString((InetSocketAddress) ctx.getChannel().getRemoteAddress()));
+ handler.disconnected(channel);
+ } finally {
+ NettyChannel.removeChannelIfDisconnected(ctx.getChannel());
+ }
+ }
+
+ @Override
+ public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception {
+ NettyChannel channel = NettyChannel.getOrAddChannel(ctx.getChannel(), url, handler);
+ try {
+ handler.received(channel, e.getMessage());
+ } finally {
+ NettyChannel.removeChannelIfDisconnected(ctx.getChannel());
+ }
+ }
+
+ @Override
+ public void writeRequested(ChannelHandlerContext ctx, MessageEvent e) throws Exception {
+ super.writeRequested(ctx, e);
+ NettyChannel channel = NettyChannel.getOrAddChannel(ctx.getChannel(), url, handler);
+ try {
+ handler.sent(channel, e.getMessage());
+ } finally {
+ NettyChannel.removeChannelIfDisconnected(ctx.getChannel());
+ }
+ }
+
+ @Override
+ public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) throws Exception {
+ NettyChannel channel = NettyChannel.getOrAddChannel(ctx.getChannel(), url, handler);
+ try {
+ handler.caught(channel, e.getCause());
+ } finally {
+ NettyChannel.removeChannelIfDisconnected(ctx.getChannel());
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting-netty/src/main/java/com/alibaba/dubbo/remoting/transport/netty/NettyServer.java b/dubbo-remoting-netty/src/main/java/com/alibaba/dubbo/remoting/transport/netty/NettyServer.java
new file mode 100644
index 0000000..1990ec4
--- /dev/null
+++ b/dubbo-remoting-netty/src/main/java/com/alibaba/dubbo/remoting/transport/netty/NettyServer.java
@@ -0,0 +1,156 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting.transport.netty;
+
+import java.net.InetSocketAddress;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+import org.jboss.netty.bootstrap.ServerBootstrap;
+import org.jboss.netty.channel.ChannelFactory;
+import org.jboss.netty.channel.ChannelPipeline;
+import org.jboss.netty.channel.ChannelPipelineFactory;
+import org.jboss.netty.channel.Channels;
+import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.logger.Logger;
+import com.alibaba.dubbo.common.logger.LoggerFactory;
+import com.alibaba.dubbo.common.utils.NamedThreadFactory;
+import com.alibaba.dubbo.common.utils.NetUtils;
+import com.alibaba.dubbo.remoting.Channel;
+import com.alibaba.dubbo.remoting.ChannelHandler;
+import com.alibaba.dubbo.remoting.RemotingException;
+import com.alibaba.dubbo.remoting.Server;
+import com.alibaba.dubbo.remoting.transport.AbstractServer;
+import com.alibaba.dubbo.remoting.transport.handler.ChannelHandlers;
+
+/**
+ * NettyServer
+ *
+ * @author qian.lei
+ * @author chao.liuc
+ */
+public class NettyServer extends AbstractServer implements Server {
+
+ private static final Logger logger = LoggerFactory.getLogger(NettyServer.class);
+
+ private Map<String, Channel> channels; // <ip:port, channel>
+
+ private ServerBootstrap bootstrap;
+
+ private org.jboss.netty.channel.Channel channel;
+
+ public NettyServer(URL url, ChannelHandler handler) throws RemotingException{
+ super(url, ChannelHandlers.wrap(handler, url.addParameter(Constants.THREAD_NAME_KEY, SERVER_THREAD_POOL_NAME)));
+ }
+
+ @Override
+ protected void doOpen() throws Throwable {
+ ExecutorService boss = Executors.newCachedThreadPool(new NamedThreadFactory("NettyServerBoss", true));
+ ExecutorService worker = Executors.newCachedThreadPool(new NamedThreadFactory("NettyServerWorker", true));
+ ChannelFactory channelFactory = new NioServerSocketChannelFactory(boss, worker, getUrl().getPositiveParameter(Constants.IO_THREADS_KEY, Constants.DEFAULT_IO_THREADS));
+ bootstrap = new ServerBootstrap(channelFactory);
+
+ final NettyHandler nettyHandler = new NettyHandler(getUrl(), this);
+ channels = nettyHandler.getChannels();
+ // https://issues.jboss.org/browse/NETTY-365
+ // https://issues.jboss.org/browse/NETTY-379
+ // final Timer timer = new HashedWheelTimer(new NamedThreadFactory("NettyIdleTimer", true));
+ bootstrap.setPipelineFactory(new ChannelPipelineFactory() {
+ public ChannelPipeline getPipeline() {
+ NettyCodecAdapter adapter = new NettyCodecAdapter(getCodec() ,getDownstreamCodec(), getUrl(), NettyServer.this);
+ ChannelPipeline pipeline = Channels.pipeline();
+ /*int idleTimeout = getIdleTimeout();
+ if (idleTimeout > 10000) {
+ pipeline.addLast("timer", new IdleStateHandler(timer, idleTimeout / 1000, 0, 0));
+ }*/
+ pipeline.addLast("decoder", adapter.getDecoder());
+ pipeline.addLast("encoder", adapter.getEncoder());
+ pipeline.addLast("handler", nettyHandler);
+ return pipeline;
+ }
+ });
+ // bind
+ channel = bootstrap.bind(getBindAddress());
+ }
+
+ @Override
+ protected void doClose() throws Throwable {
+ try {
+ if (channel != null) {
+ // unbind.
+ channel.close();
+ }
+ } catch (Throwable e) {
+ logger.warn(e.getMessage(), e);
+ }
+ try {
+ Collection<com.alibaba.dubbo.remoting.Channel> channels = getChannels();
+ if (channels != null && channels.size() > 0) {
+ for (com.alibaba.dubbo.remoting.Channel channel : channels) {
+ try {
+ channel.close();
+ } catch (Throwable e) {
+ logger.warn(e.getMessage(), e);
+ }
+ }
+ }
+ } catch (Throwable e) {
+ logger.warn(e.getMessage(), e);
+ }
+ try {
+ if (bootstrap != null) {
+ // release external resource.
+ bootstrap.releaseExternalResources();
+ }
+ } catch (Throwable e) {
+ logger.warn(e.getMessage(), e);
+ }
+ try {
+ if (channels != null) {
+ channels.clear();
+ }
+ } catch (Throwable e) {
+ logger.warn(e.getMessage(), e);
+ }
+ }
+
+ public Collection<Channel> getChannels() {
+ Collection<Channel> chs = new HashSet<Channel>();
+ for (Channel channel : this.channels.values()) {
+ if (channel.isConnected()) {
+ chs.add(channel);
+ } else {
+ channels.remove(NetUtils.toAddressString(channel.getRemoteAddress()));
+ }
+ }
+ return chs;
+ }
+
+ public Channel getChannel(InetSocketAddress remoteAddress) {
+ return channels.get(NetUtils.toAddressString(remoteAddress));
+ }
+
+ public boolean isBound() {
+ return channel.isBound();
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting-netty/src/main/java/com/alibaba/dubbo/remoting/transport/netty/NettyTransporter.java b/dubbo-remoting-netty/src/main/java/com/alibaba/dubbo/remoting/transport/netty/NettyTransporter.java
new file mode 100644
index 0000000..787d73f
--- /dev/null
+++ b/dubbo-remoting-netty/src/main/java/com/alibaba/dubbo/remoting/transport/netty/NettyTransporter.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting.transport.netty;
+
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.remoting.ChannelHandler;
+import com.alibaba.dubbo.remoting.Client;
+import com.alibaba.dubbo.remoting.RemotingException;
+import com.alibaba.dubbo.remoting.Server;
+import com.alibaba.dubbo.remoting.Transporter;
+
+/**
+ * @author ding.lid
+ */
+@Extension(NettyTransporter.NAME)
+public class NettyTransporter implements Transporter {
+
+ public static final String NAME = "netty";
+
+ public Server bind(URL url, ChannelHandler listener) throws RemotingException {
+ return new NettyServer(url, listener);
+ }
+
+ public Client connect(URL url, ChannelHandler listener) throws RemotingException {
+ return new NettyClient(url, listener);
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting-netty/src/main/resources/META-INF/services/com.alibaba.dubbo.remoting.Transporter b/dubbo-remoting-netty/src/main/resources/META-INF/services/com.alibaba.dubbo.remoting.Transporter
new file mode 100644
index 0000000..d90141b
--- /dev/null
+++ b/dubbo-remoting-netty/src/main/resources/META-INF/services/com.alibaba.dubbo.remoting.Transporter
@@ -0,0 +1 @@
+com.alibaba.dubbo.remoting.transport.netty.NettyTransporter
\ No newline at end of file
diff --git a/dubbo-remoting-netty/src/test/java/com/alibaba/dubbo/remoting/transport/netty/ClientReconnectTest.java b/dubbo-remoting-netty/src/test/java/com/alibaba/dubbo/remoting/transport/netty/ClientReconnectTest.java
new file mode 100644
index 0000000..5afb8d0
--- /dev/null
+++ b/dubbo-remoting-netty/src/test/java/com/alibaba/dubbo/remoting/transport/netty/ClientReconnectTest.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.transport.netty;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+import com.alibaba.dubbo.common.Constants;
+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();
+ server.close();
+ Thread.sleep(1000);
+ }
+ {
+ 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();
+ server.close();
+ }
+ }
+
+ public Client startClient(int port , int reconnectPeriod) throws RemotingException{
+ final String url = "exchange://127.0.0.1:"+port + "/client.reconnect.test?check=false&"+Constants.RECONNECT_KEY+"="+reconnectPeriod;
+ return Exchangers.connect(url);
+ }
+
+ public Server startServer(int port) throws RemotingException{
+ final String url = "exchange://127.0.0.1:"+port +"/client.reconnect.test";
+ return Exchangers.bind(url, new HandlerAdapter());
+ }
+
+ static class HandlerAdapter extends ExchangeHandlerAdapter{
+ public void connected(Channel channel) throws RemotingException {
+ }
+ public void disconnected(Channel channel) throws RemotingException {
+ }
+ public void caught(Channel channel, Throwable exception) throws RemotingException {
+ }
+ }
+}
\ No newline at end of file
diff --git a/dubbo-remoting-netty/src/test/java/com/alibaba/dubbo/remoting/transport/netty/ClientToServerTest.java b/dubbo-remoting-netty/src/test/java/com/alibaba/dubbo/remoting/transport/netty/ClientToServerTest.java
new file mode 100644
index 0000000..09e6652
--- /dev/null
+++ b/dubbo-remoting-netty/src/test/java/com/alibaba/dubbo/remoting/transport/netty/ClientToServerTest.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting.transport.netty;
+
+import junit.framework.Assert;
+import junit.framework.TestCase;
+
+import org.junit.Test;
+
+import com.alibaba.dubbo.remoting.RemotingException;
+import com.alibaba.dubbo.remoting.exchange.ExchangeChannel;
+import com.alibaba.dubbo.remoting.exchange.ExchangeServer;
+import com.alibaba.dubbo.remoting.exchange.ResponseFuture;
+import com.alibaba.dubbo.remoting.exchange.support.Replier;
+
+/**
+ * ClientToServer
+ *
+ * @author william.liangf
+ */
+public abstract class ClientToServerTest extends TestCase {
+
+ protected static final String LOCALHOST = "127.0.0.1";
+
+ protected ExchangeServer server;
+
+ protected ExchangeChannel client;
+
+ protected WorldHandler handler = new WorldHandler();
+
+ protected abstract ExchangeServer newServer(int port, Replier<?> receiver) throws RemotingException;
+
+ protected abstract ExchangeChannel newClient(int port) throws RemotingException;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ int port = (int) (1000 * Math.random() + 10000);
+ server = newServer(port, handler);
+ client = newClient(port);
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ super.tearDown();
+ try {
+ if (server != null)
+ server.close();
+ } finally {
+ if (client != null)
+ client.close();
+ }
+ }
+
+ @Test
+ public void testFuture() throws Exception {
+ ResponseFuture future = client.request(new World("world"));
+ Hello result = (Hello)future.get();
+ Assert.assertEquals("hello,world", result.getName());
+ }
+
+// @Test
+// public void testCallback() throws Exception {
+// final Object waitter = new Object();
+// client.invoke(new World("world"), new InvokeCallback<Hello>() {
+// public void callback(Hello result) {
+// Assert.assertEquals("hello,world", result.getName());
+// synchronized (waitter) {
+// waitter.notifyAll();
+// }
+// }
+// public void onException(Throwable exception) {
+// }
+// });
+// synchronized (waitter) {
+// waitter.wait();
+// }
+// }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting-netty/src/test/java/com/alibaba/dubbo/remoting/transport/netty/ClientsTest.java b/dubbo-remoting-netty/src/test/java/com/alibaba/dubbo/remoting/transport/netty/ClientsTest.java
new file mode 100644
index 0000000..0493ed8
--- /dev/null
+++ b/dubbo-remoting-netty/src/test/java/com/alibaba/dubbo/remoting/transport/netty/ClientsTest.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting.transport.netty;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.fail;
+import static org.junit.matchers.JUnitMatchers.containsString;
+
+import org.junit.Test;
+
+import com.alibaba.dubbo.common.ExtensionLoader;
+import com.alibaba.dubbo.remoting.Transporter;
+
+/**
+ * @author ding.lid
+ */
+public class ClientsTest {
+
+ @Test
+ public void testGetTransportEmpty() {
+ try {
+ ExtensionLoader.getExtensionLoader(Transporter.class).getExtension("");
+ fail();
+ } catch (IllegalArgumentException expected) {
+ assertThat(expected.getMessage(), containsString("Extension name == null"));
+ }
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testGetTransportNull() {
+ String name = null;
+ ExtensionLoader.getExtensionLoader(Transporter.class).getExtension(name);
+ }
+
+ @Test
+ public void testGetTransport3() {
+ String name = "netty";
+ assertEquals(NettyTransporter.class, ExtensionLoader.getExtensionLoader(Transporter.class).getExtension(name).getClass());
+ }
+
+ @Test(expected = IllegalStateException.class)
+ public void testGetTransportWrong() {
+ String name = "nety";
+ assertNull(ExtensionLoader.getExtensionLoader(Transporter.class).getExtension(name).getClass());
+ }
+}
\ No newline at end of file
diff --git a/dubbo-remoting-netty/src/test/java/com/alibaba/dubbo/remoting/transport/netty/Hello.java b/dubbo-remoting-netty/src/test/java/com/alibaba/dubbo/remoting/transport/netty/Hello.java
new file mode 100644
index 0000000..0af10cb
--- /dev/null
+++ b/dubbo-remoting-netty/src/test/java/com/alibaba/dubbo/remoting/transport/netty/Hello.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting.transport.netty;
+
+import java.io.Serializable;
+
+/**
+ * Result
+ *
+ * @author william.liangf
+ */
+public class Hello implements Serializable {
+
+ private static final long serialVersionUID = 8563900571013747774L;
+
+ private String name;
+
+ public Hello() {
+ }
+
+ public Hello(String name) {
+ this.name = name;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting-netty/src/test/java/com/alibaba/dubbo/remoting/transport/netty/NettyClientTest.java b/dubbo-remoting-netty/src/test/java/com/alibaba/dubbo/remoting/transport/netty/NettyClientTest.java
new file mode 100644
index 0000000..ecd8695
--- /dev/null
+++ b/dubbo-remoting-netty/src/test/java/com/alibaba/dubbo/remoting/transport/netty/NettyClientTest.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting.transport.netty;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.remoting.Server;
+import com.alibaba.dubbo.remoting.exchange.ExchangeChannel;
+import com.alibaba.dubbo.remoting.exchange.Exchangers;
+
+/**
+ * User: heyman
+ * Date: 5/3/11
+ * Time: 5:47 PM
+ */
+public class NettyClientTest {
+ static Server server;
+
+
+ @BeforeClass
+ public static void setUp() throws Exception {
+ /*int port = (int) (1000 * Math.random() + 10000);
+ //System.out.println(port);*/
+ server = Exchangers.bind(URL.valueOf("exchange://localhost:10001?server=netty"), new TelnetServerHandler());
+ }
+
+ @Test
+ public void testClientClose() throws Exception {
+ List<ExchangeChannel> clients = new ArrayList<ExchangeChannel>(100);
+ for (int i = 0; i < 100; i++) {
+ ExchangeChannel client = Exchangers.connect(URL.valueOf("exchange://localhost:10001?client=netty"));
+ Thread.sleep(5);
+ clients.add(client);
+ }
+ for (ExchangeChannel client : clients){
+ client.close();
+ }
+ Thread.sleep(1000);
+ }
+
+ @Test
+ public void testServerClose() throws Exception {
+ for (int i = 0; i < 100; i++) {
+ Server aServer = Exchangers.bind(URL.valueOf("exchange://localhost:" + (5000 + i) + "?client=netty"), new TelnetServerHandler());
+ System.out.println(i);
+ aServer.close();
+ }
+ }
+
+ @AfterClass
+ public static void tearDown() throws Exception {
+ try {
+ if (server != null)
+ server.close();
+ } finally {
+
+ }
+ }
+}
\ No newline at end of file
diff --git a/dubbo-remoting-netty/src/test/java/com/alibaba/dubbo/remoting/transport/netty/NettyClientToServerTest.java b/dubbo-remoting-netty/src/test/java/com/alibaba/dubbo/remoting/transport/netty/NettyClientToServerTest.java
new file mode 100644
index 0000000..ea42871
--- /dev/null
+++ b/dubbo-remoting-netty/src/test/java/com/alibaba/dubbo/remoting/transport/netty/NettyClientToServerTest.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting.transport.netty;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.remoting.RemotingException;
+import com.alibaba.dubbo.remoting.exchange.ExchangeChannel;
+import com.alibaba.dubbo.remoting.exchange.ExchangeServer;
+import com.alibaba.dubbo.remoting.exchange.Exchangers;
+import com.alibaba.dubbo.remoting.exchange.support.Replier;
+
+/**
+ * NettyClientToServerTest
+ *
+ * @author william.liangf
+ */
+public class NettyClientToServerTest extends ClientToServerTest {
+
+ protected ExchangeServer newServer(int port, Replier<?> receiver) throws RemotingException {
+ return Exchangers.bind(URL.valueOf("exchange://localhost:" + port + "?server=netty"), receiver);
+ }
+
+ protected ExchangeChannel newClient(int port) throws RemotingException {
+ return Exchangers.connect(URL.valueOf("exchange://localhost:" + port + "?client=netty"));
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting-netty/src/test/java/com/alibaba/dubbo/remoting/transport/netty/NettyStringTest.java b/dubbo-remoting-netty/src/test/java/com/alibaba/dubbo/remoting/transport/netty/NettyStringTest.java
new file mode 100644
index 0000000..dfb8405
--- /dev/null
+++ b/dubbo-remoting-netty/src/test/java/com/alibaba/dubbo/remoting/transport/netty/NettyStringTest.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting.transport.netty;
+
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.remoting.exchange.ExchangeChannel;
+import com.alibaba.dubbo.remoting.exchange.ExchangeServer;
+import com.alibaba.dubbo.remoting.exchange.Exchangers;
+
+/**
+ * User: heyman
+ * Date: 4/26/11
+ * Time: 4:13 PM
+ */
+public class NettyStringTest {
+ static ExchangeServer server;
+ static ExchangeChannel client;
+
+ @BeforeClass
+ public static void setUp() throws Exception {
+ //int port = (int) (1000 * Math.random() + 10000);
+ int port = 10001;
+ System.out.println(port);
+ server = Exchangers.bind(URL.valueOf("telnet://0.0.0.0:" + port + "?server=netty"), new TelnetServerHandler());
+ client = Exchangers.connect(URL.valueOf("telnet://127.0.0.1:" + port + "?client=netty"), new TelnetClientHandler());
+ }
+
+ @Test
+ public void testHandler() throws Exception {
+ //Thread.sleep(20000);
+ /*client.request("world\r\n");
+ Future future = client.request("world", 10000);
+ String result = (String)future.get();
+ Assert.assertEquals("Did you say 'world'?\r\n",result);*/
+ }
+
+ @AfterClass
+ public static void tearDown() throws Exception {
+ try {
+ if (server != null)
+ server.close();
+ } finally {
+ if (client != null)
+ client.close();
+ }
+ }
+}
\ No newline at end of file
diff --git a/dubbo-remoting-netty/src/test/java/com/alibaba/dubbo/remoting/transport/netty/TelnetClientHandler.java b/dubbo-remoting-netty/src/test/java/com/alibaba/dubbo/remoting/transport/netty/TelnetClientHandler.java
new file mode 100644
index 0000000..9f1aae5
--- /dev/null
+++ b/dubbo-remoting-netty/src/test/java/com/alibaba/dubbo/remoting/transport/netty/TelnetClientHandler.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting.transport.netty;
+
+import com.alibaba.dubbo.remoting.RemotingException;
+import com.alibaba.dubbo.remoting.exchange.ExchangeChannel;
+import com.alibaba.dubbo.remoting.exchange.support.Replier;
+
+/**
+ * User: heyman
+ * Date: 4/28/11
+ * Time: 11:15 AM
+ */
+public class TelnetClientHandler implements Replier<String> {
+
+ public Class<String> interest() {
+ return String.class;
+ }
+
+ public Object reply(ExchangeChannel channel, String msg) throws RemotingException {
+ return msg;
+ }
+}
\ No newline at end of file
diff --git a/dubbo-remoting-netty/src/test/java/com/alibaba/dubbo/remoting/transport/netty/TelnetServerHandler.java b/dubbo-remoting-netty/src/test/java/com/alibaba/dubbo/remoting/transport/netty/TelnetServerHandler.java
new file mode 100644
index 0000000..ac5701b
--- /dev/null
+++ b/dubbo-remoting-netty/src/test/java/com/alibaba/dubbo/remoting/transport/netty/TelnetServerHandler.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting.transport.netty;
+
+import com.alibaba.dubbo.remoting.RemotingException;
+import com.alibaba.dubbo.remoting.exchange.ExchangeChannel;
+import com.alibaba.dubbo.remoting.exchange.support.Replier;
+
+/**
+ * User: heyman
+ * Date: 4/26/11
+ * Time: 4:29 PM
+ */
+public class TelnetServerHandler implements Replier<String> {
+
+ public Class<String> interest() {
+ return String.class;
+ }
+
+ public Object reply(ExchangeChannel channel, String msg) throws RemotingException {
+ // Generate and write a response.
+
+ String response;
+ if (msg.length() == 0) {
+ response = "Please type something.\r\n";
+ } else {
+ response = "Did you say '" + msg + "'?\r\n";
+ }
+ //System.out.println(response);
+ return response;
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting-netty/src/test/java/com/alibaba/dubbo/remoting/transport/netty/World.java b/dubbo-remoting-netty/src/test/java/com/alibaba/dubbo/remoting/transport/netty/World.java
new file mode 100644
index 0000000..eca2cd85
--- /dev/null
+++ b/dubbo-remoting-netty/src/test/java/com/alibaba/dubbo/remoting/transport/netty/World.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting.transport.netty;
+
+import java.io.Serializable;
+
+/**
+ * Data
+ *
+ * @author william.liangf
+ */
+public class World implements Serializable {
+
+ private static final long serialVersionUID = 8563900571013747774L;
+
+ private String name;
+
+ public World() {
+ }
+
+ public World(String name) {
+ this.name = name;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting-netty/src/test/java/com/alibaba/dubbo/remoting/transport/netty/WorldHandler.java b/dubbo-remoting-netty/src/test/java/com/alibaba/dubbo/remoting/transport/netty/WorldHandler.java
new file mode 100644
index 0000000..4e257bc
--- /dev/null
+++ b/dubbo-remoting-netty/src/test/java/com/alibaba/dubbo/remoting/transport/netty/WorldHandler.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting.transport.netty;
+
+import com.alibaba.dubbo.remoting.RemotingException;
+import com.alibaba.dubbo.remoting.exchange.ExchangeChannel;
+import com.alibaba.dubbo.remoting.exchange.support.Replier;
+
+/**
+ * DataHandler
+ *
+ * @author william.liangf
+ */
+public class WorldHandler implements Replier<World> {
+
+ public Class<World> interest() {
+ return World.class;
+ }
+
+ public Object reply(ExchangeChannel channel, World msg) throws RemotingException {
+ return new Hello("hello," + msg.getName());
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting/pom.xml b/dubbo-remoting/pom.xml
new file mode 100644
index 0000000..f530f0b
--- /dev/null
+++ b/dubbo-remoting/pom.xml
@@ -0,0 +1,38 @@
+<!--
+ - Copyright 1999-2011 Alibaba Group.
+ -
+ - Licensed under the Apache License, Version 2.0 (the "License");
+ - you may not use this file except in compliance with the License.
+ - You may obtain a copy of the License at
+ -
+ - http://www.apache.org/licenses/LICENSE-2.0
+ -
+ - Unless required by applicable law or agreed to in writing, software
+ - distributed under the License is distributed on an "AS IS" BASIS,
+ - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ - See the License for the specific language governing permissions and
+ - limitations under the License.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>com.alibaba</groupId>
+ <artifactId>dubbo-parent</artifactId>
+ <version>2.0.8</version>
+ </parent>
+ <artifactId>dubbo-remoting</artifactId>
+ <packaging>jar</packaging>
+ <name>Dubbo Remoting Module</name>
+ <description>The remoting module of dubbo project</description>
+ <properties>
+ <skip_maven_deploy>false</skip_maven_deploy>
+ </properties>
+ <dependencies>
+ <dependency>
+ <groupId>com.alibaba</groupId>
+ <artifactId>dubbo-common</artifactId>
+ <version>${project.parent.version}</version>
+ </dependency>
+ </dependencies>
+</project>
\ No newline at end of file
diff --git a/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/Channel.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/Channel.java
new file mode 100644
index 0000000..8a4e4bb
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/Channel.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting;
+
+import java.net.InetSocketAddress;
+
+/**
+ * Channel. (API/SPI, Prototype, ThreadSafe)
+ *
+ * @see com.alibaba.dubbo.remoting.Client
+ * @see com.alibaba.dubbo.remoting.Server#getChannels()
+ * @see com.alibaba.dubbo.remoting.Server#getChannel(InetSocketAddress)
+ * @author qian.lei
+ * @author william.liangf
+ */
+public interface Channel extends Endpoint {
+
+ /**
+ * get remote address.
+ *
+ * @return remote address.
+ */
+ InetSocketAddress getRemoteAddress();
+
+ /**
+ * is connected.
+ *
+ * @return connected
+ */
+ boolean isConnected();
+
+ /**
+ * has attribute.
+ *
+ * @param key key.
+ * @return has or has not.
+ */
+ boolean hasAttribute(String key);
+
+ /**
+ * get attribute.
+ *
+ * @param key key.
+ * @return value.
+ */
+ Object getAttribute(String key);
+
+ /**
+ * set attribute.
+ *
+ * @param key key.
+ * @param value value.
+ */
+ void setAttribute(String key,Object value);
+
+ /**
+ * remove attribute.
+ *
+ * @param key key.
+ */
+ void removeAttribute(String key);
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/ChannelHandler.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/ChannelHandler.java
new file mode 100644
index 0000000..18578a2
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/ChannelHandler.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.remoting;
+
+
+/**
+ * ChannelHandler. (API, Prototype, ThreadSafe)
+ *
+ * @see com.alibaba.dubbo.remoting.exchange.Exchangers#bind(com.alibaba.dubbo.common.URL, ChannelHandler...)
+ * @see com.alibaba.dubbo.remoting.Transporter#bind(com.alibaba.dubbo.common.URL, ChannelHandler)
+ * @see com.alibaba.dubbo.remoting.exchange.Exchangers#connect(com.alibaba.dubbo.common.URL, ChannelHandler...)
+ * @see com.alibaba.dubbo.remoting.Transporter#connect(com.alibaba.dubbo.common.URL, ChannelHandler)
+ * @author qian.lei
+ * @author william.liangf
+ */
+public interface ChannelHandler {
+
+ /**
+ * on channel connected.
+ *
+ * @param channel channel.
+ */
+ void connected(Channel channel) throws RemotingException;
+
+ /**
+ * on channel disconnected.
+ *
+ * @param channel channel.
+ */
+ void disconnected(Channel channel) throws RemotingException;
+
+ /**
+ * on message sent.
+ *
+ * @param channel channel.
+ * @param message message.
+ */
+ void sent(Channel channel, Object message) throws RemotingException;
+
+ /**
+ * on message received.
+ *
+ * @param channel channel.
+ * @param message message.
+ */
+ void received(Channel channel, Object message) throws RemotingException;
+
+ /**
+ * on exception caught.
+ *
+ * @param channel channel.
+ * @param exception exception.
+ */
+ void caught(Channel channel, Throwable exception) throws RemotingException;
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/ChannelHandlerWrapper.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/ChannelHandlerWrapper.java
new file mode 100644
index 0000000..5b783e4
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/ChannelHandlerWrapper.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting;
+
+import com.alibaba.dubbo.common.Adaptive;
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.remoting.transport.handler.DefaultChannelHandlerWrapper;
+
+/**
+ * ChannelHandlerWrapper (SPI, Singleton, ThreadSafe)
+ *
+ * @author chao.liuc
+ */
+@Extension(DefaultChannelHandlerWrapper.NAME)
+public interface ChannelHandlerWrapper {
+
+ /**
+ * wrap.
+ *
+ * @param handler
+ * @param url
+ * @return
+ */
+ @Adaptive({Constants.CHANNEL_HANDLER_KEY})
+ ChannelHandler wrap(ChannelHandler handler, URL url);
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/Client.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/Client.java
new file mode 100644
index 0000000..69fb2a3
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/Client.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;
+
+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.exchange.Exchangers#connect(com.alibaba.dubbo.common.URL, ChannelHandler...)
+ * @see com.alibaba.dubbo.remoting.Transporter#connect(com.alibaba.dubbo.common.URL, ChannelHandler)
+ * @author qian.lei
+ */
+public interface Client extends Endpoint, Channel, Resetable {
+
+ /**
+ * reconnect.
+ */
+ void reconnect() throws RemotingException;
+
+ @Deprecated
+ void reset(com.alibaba.dubbo.common.Parameters parameters);
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/Codec.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/Codec.java
new file mode 100644
index 0000000..3157650
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/Codec.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import com.alibaba.dubbo.common.Adaptive;
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.Extension;
+
+/**
+ * Codec. (SPI, Singleton, ThreadSafe)
+ *
+ * @author qianlei
+ * @author ding.lid
+ * @author william.liangf
+ */
+@Extension
+public interface Codec {
+
+ /**
+ * Need more input poison.
+ *
+ * @see #decode(Channel, InputStream)
+ */
+ Object NEED_MORE_INPUT = new Object();
+
+ /**
+ * Encode message.
+ *
+ * @param channel channel.
+ * @param output output stream.
+ * @param message message.
+ */
+ @Adaptive({Constants.CODEC_KEY})
+ void encode(Channel channel, OutputStream output, Object message) throws IOException;
+
+ /**
+ * Decode message.
+ *
+ * @see #NEED_MORE_INPUT
+ * @param channel channel.
+ * @param input input stream.
+ * @return message or <code>NEED_MORE_INPUT</code> poison.
+ */
+ @Adaptive({Constants.CODEC_KEY})
+ Object decode(Channel channel, InputStream input) throws IOException;
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/Endpoint.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/Endpoint.java
new file mode 100644
index 0000000..267ad30
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/Endpoint.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting;
+
+import java.net.InetSocketAddress;
+
+import com.alibaba.dubbo.common.URL;
+
+/**
+ * Endpoint. (API/SPI, Prototype, ThreadSafe)
+ *
+ * @see com.alibaba.dubbo.remoting.Channel
+ * @see com.alibaba.dubbo.remoting.Client
+ * @see com.alibaba.dubbo.remoting.Server
+ * @author william.liangf
+ */
+public interface Endpoint {
+
+ /**
+ * get url.
+ *
+ * @return url
+ */
+ URL getUrl();
+
+ /**
+ * get channel handler.
+ *
+ * @return channel handler
+ */
+ ChannelHandler getChannelHandler();
+
+ /**
+ * get local address.
+ *
+ * @return local address.
+ */
+ InetSocketAddress getLocalAddress();
+
+ /**
+ * send message.
+ *
+ * @param message
+ * @throws RemotingException
+ */
+ void send(Object message) throws RemotingException;
+
+ /**
+ * send message.
+ *
+ * @param message
+ * @param sent 是否已发送完成
+ */
+ void send(Object message, boolean sent) throws RemotingException;
+
+ /**
+ * close the channel.
+ */
+ void close();
+
+ /**
+ * Graceful close the channel.
+ */
+ void close(int timeout);
+
+ /**
+ * is closed.
+ *
+ * @return closed
+ */
+ boolean isClosed();
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/ExecutionException.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/ExecutionException.java
new file mode 100644
index 0000000..c54f6e4
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/ExecutionException.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting;
+
+import java.net.InetSocketAddress;
+
+/**
+ * ReceiveException
+ *
+ * @author william.liangf
+ */
+public class ExecutionException extends RemotingException {
+
+ private static final long serialVersionUID = -2531085236111056860L;
+
+ private final Object request;
+
+ public ExecutionException(Object request, Channel channel, String message, Throwable cause){
+ super(channel, message, cause);
+ this.request = request;
+ }
+
+ public ExecutionException(Object request, Channel channel, String msg){
+ super(channel, msg);
+ this.request = request;
+ }
+
+ public ExecutionException(Object request, Channel channel, Throwable cause){
+ super(channel, cause);
+ this.request = request;
+ }
+
+ public ExecutionException(Object request, InetSocketAddress localAddress, InetSocketAddress remoteAddress, String message,
+ Throwable cause){
+ super(localAddress, remoteAddress, message, cause);
+ this.request = request;
+ }
+
+ public ExecutionException(Object request, InetSocketAddress localAddress, InetSocketAddress remoteAddress, String message){
+ super(localAddress, remoteAddress, message);
+ this.request = request;
+ }
+
+ public ExecutionException(Object request, InetSocketAddress localAddress, InetSocketAddress remoteAddress, Throwable cause){
+ super(localAddress, remoteAddress, cause);
+ this.request = request;
+ }
+
+
+ public Object getRequest() {
+ return request;
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/RemotingException.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/RemotingException.java
new file mode 100644
index 0000000..e7339e9
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/RemotingException.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting;
+
+import java.net.InetSocketAddress;
+
+/**
+ * RemotingException. (API, Prototype, ThreadSafe)
+ *
+ * @see com.alibaba.dubbo.remoting.exchange.ResponseFuture#get()
+ * @see com.alibaba.dubbo.remoting.exchange.ResponseFuture#get(int)
+ * @see com.alibaba.dubbo.remoting.Channel#send(Object, boolean)
+ * @see com.alibaba.dubbo.remoting.Channel#request(Object)
+ * @see com.alibaba.dubbo.remoting.Channel#request(Object, int)
+ * @see com.alibaba.dubbo.remoting.Transporter#bind(com.alibaba.dubbo.common.URL, ChannelHandler)
+ * @see com.alibaba.dubbo.remoting.Transporter#connect(com.alibaba.dubbo.common.URL, ChannelHandler)
+ * @author qian.lei
+ */
+public class RemotingException extends Exception {
+
+ private static final long serialVersionUID = -3160452149606778709L;
+
+ private InetSocketAddress localAddress;
+
+ private InetSocketAddress remoteAddress;
+
+ public RemotingException(Channel channel, String msg){
+ this(channel == null ? null : channel.getLocalAddress(), channel == null ? null : channel.getRemoteAddress(),
+ msg);
+ }
+
+ public RemotingException(InetSocketAddress localAddress, InetSocketAddress remoteAddress, String message){
+ super(message);
+
+ this.localAddress = localAddress;
+ this.remoteAddress = remoteAddress;
+ }
+
+ public RemotingException(Channel channel, Throwable cause){
+ this(channel == null ? null : channel.getLocalAddress(), channel == null ? null : channel.getRemoteAddress(),
+ cause);
+ }
+
+ public RemotingException(InetSocketAddress localAddress, InetSocketAddress remoteAddress, Throwable cause){
+ super(cause);
+
+ this.localAddress = localAddress;
+ this.remoteAddress = remoteAddress;
+ }
+
+ public RemotingException(Channel channel, String message, Throwable cause){
+ this(channel == null ? null : channel.getLocalAddress(), channel == null ? null : channel.getRemoteAddress(),
+ message, cause);
+ }
+
+ public RemotingException(InetSocketAddress localAddress, InetSocketAddress remoteAddress, String message,
+ Throwable cause){
+ super(message, cause);
+
+ this.localAddress = localAddress;
+ this.remoteAddress = remoteAddress;
+ }
+
+ public InetSocketAddress getLocalAddress() {
+ return localAddress;
+ }
+
+ public InetSocketAddress getRemoteAddress() {
+ return remoteAddress;
+ }
+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/Server.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/Server.java
new file mode 100644
index 0000000..909cf0d
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/Server.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;
+
+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.exchange.Exchangers#bind(com.alibaba.dubbo.common.URL, ChannelHandler...)
+ * @see com.alibaba.dubbo.remoting.Transporter#bind(com.alibaba.dubbo.common.URL, ChannelHandler)
+ * @author qian.lei
+ */
+public interface Server extends Endpoint, Resetable {
+
+ /**
+ * is bound.
+ *
+ * @return bound
+ */
+ boolean isBound();
+
+ /**
+ * get channels.
+ *
+ * @return channels
+ */
+ Collection<Channel> getChannels();
+
+ /**
+ * get channel.
+ *
+ * @param remoteAddress
+ * @return channel
+ */
+ Channel getChannel(InetSocketAddress remoteAddress);
+
+ @Deprecated
+ void reset(com.alibaba.dubbo.common.Parameters parameters);
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/TimeoutException.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/TimeoutException.java
new file mode 100644
index 0000000..1b20999
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/TimeoutException.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting;
+
+import java.net.InetSocketAddress;
+
+/**
+ * TimeoutException. (API, Prototype, ThreadSafe)
+ *
+ * @see com.alibaba.dubbo.remoting.exchange.ResponseFuture#get()
+ * @see com.alibaba.dubbo.remoting.exchange.ResponseFuture#get(int)
+ * @author qian.lei
+ */
+public class TimeoutException extends RemotingException {
+
+ private static final long serialVersionUID = 3122966731958222692L;
+
+ public static final int CLIENT_SIDE = 0;
+
+ public static final int SERVER_SIDE = 1;
+
+ private final int phase;
+
+ public TimeoutException(boolean serverSide, Channel channel, String message){
+ super(channel, message);
+ this.phase = serverSide ? SERVER_SIDE : CLIENT_SIDE;
+ }
+
+ public TimeoutException(boolean serverSide, InetSocketAddress localAddress,
+ InetSocketAddress remoteAddress, String message) {
+ super(localAddress, remoteAddress, message);
+ this.phase = serverSide ? SERVER_SIDE : CLIENT_SIDE;
+ }
+
+ public int getPhase() {
+ return phase;
+ }
+
+ public boolean isServerSide() {
+ return phase == 1;
+ }
+
+ public boolean isClientSide() {
+ return phase == 0;
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/Transporter.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/Transporter.java
new file mode 100644
index 0000000..1bf197c
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/Transporter.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting;
+
+import com.alibaba.dubbo.common.Adaptive;
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.common.URL;
+
+/**
+ * Transporter. (SPI, Singleton, ThreadSafe)
+ *
+ * <a href="http://en.wikipedia.org/wiki/Transport_Layer">Transport Layer</a>
+ * <a href="http://en.wikipedia.org/wiki/Client%E2%80%93server_model">Client/Server</a>
+ *
+ * @see com.alibaba.dubbo.remoting.exchange.Exchangers
+ * @author ding.lid
+ * @author william.liangf
+ */
+@Extension("netty")
+public interface Transporter {
+
+ /**
+ * Bind a server.
+ *
+ * @see com.alibaba.dubbo.remoting.exchange.Exchangers#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.Remoting#connect(URL, Receiver, ChannelListener)
+ * @param url server url
+ * @param handler
+ * @return client
+ * @throws RemotingException
+ */
+ @Adaptive({Constants.CLIENT_KEY, Constants.TRANSPORTER_KEY})
+ Client connect(URL url, ChannelHandler handler) throws RemotingException;
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/Transporters.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/Transporters.java
new file mode 100644
index 0000000..0c56d05
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/Transporters.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting;
+
+import com.alibaba.dubbo.common.ExtensionLoader;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.Version;
+import com.alibaba.dubbo.remoting.transport.ChannelHandlerAdapter;
+import com.alibaba.dubbo.remoting.transport.ChannelHandlerDispatcher;
+
+/**
+ * Transporter facade. (API, Static, ThreadSafe)
+ *
+ * @author william.liangf
+ */
+public class Transporters {
+
+ public static Server bind(String url, ChannelHandler... handler) throws RemotingException {
+ return bind(URL.valueOf(url), handler);
+ }
+
+ public static Server bind(URL url, ChannelHandler... handlers) throws RemotingException {
+ if (url == null) {
+ throw new IllegalArgumentException("url == null");
+ }
+ if (handlers == null || handlers.length == 0) {
+ throw new IllegalArgumentException("handlers == null");
+ }
+ ChannelHandler handler;
+ if (handlers.length == 1) {
+ handler = handlers[0];
+ } else {
+ handler = new ChannelHandlerDispatcher(handlers);
+ }
+ return getTransporter().bind(url, handler);
+ }
+
+ public static Client connect(String url, ChannelHandler... handler) throws RemotingException {
+ return connect(URL.valueOf(url), handler);
+ }
+
+ public static Client connect(URL url, ChannelHandler... handlers) throws RemotingException {
+ if (url == null) {
+ throw new IllegalArgumentException("url == null");
+ }
+ ChannelHandler handler;
+ if (handlers == null || handlers.length == 0) {
+ handler = new ChannelHandlerAdapter();
+ } else if (handlers.length == 1) {
+ handler = handlers[0];
+ } else {
+ handler = new ChannelHandlerDispatcher(handlers);
+ }
+ return getTransporter().connect(url, handler);
+ }
+
+ public static Transporter getTransporter() {
+ return ExtensionLoader.getExtensionLoader(Transporter.class).getAdaptiveExtension();
+ }
+
+ static {
+ // check duplicate jar package
+ Version.checkDuplicate(Transporters.class);
+ Version.checkDuplicate(RemotingException.class);
+ }
+
+ private Transporters(){
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/exchange/ExchangeChannel.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/exchange/ExchangeChannel.java
new file mode 100644
index 0000000..f59777b
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/exchange/ExchangeChannel.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting.exchange;
+
+import com.alibaba.dubbo.remoting.Channel;
+import com.alibaba.dubbo.remoting.RemotingException;
+
+/**
+ * ExchangeChannel. (API/SPI, Prototype, ThreadSafe)
+ *
+ * @author william.liangf
+ */
+public interface ExchangeChannel extends Channel {
+
+ /**
+ * send request.
+ *
+ * @param request
+ * @return
+ * @throws RemotingException
+ */
+ ResponseFuture request(Object request) throws RemotingException;
+
+ /**
+ * send request.
+ *
+ * @param request
+ * @param timeout
+ * @return
+ * @throws RemotingException
+ */
+ ResponseFuture request(Object request, int timeout) throws RemotingException;
+
+ /**
+ * get message handler.
+ *
+ * @return message handler
+ */
+ ExchangeHandler getExchangeHandler();
+
+ /**
+ * graceful close.
+ *
+ * @param timeout
+ */
+ void close(int timeout);
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/exchange/ExchangeClient.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/exchange/ExchangeClient.java
new file mode 100644
index 0000000..6e0d61c
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/exchange/ExchangeClient.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting.exchange;
+
+import com.alibaba.dubbo.remoting.Client;
+
+/**
+ * ExchangeClient. (API/SPI, Prototype, ThreadSafe)
+ *
+ * @author william.liangf
+ */
+public interface ExchangeClient extends Client, ExchangeChannel {
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/exchange/ExchangeHandler.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/exchange/ExchangeHandler.java
new file mode 100644
index 0000000..b5e183b
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/exchange/ExchangeHandler.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting.exchange;
+
+import com.alibaba.dubbo.remoting.ChannelHandler;
+import com.alibaba.dubbo.remoting.RemotingException;
+import com.alibaba.dubbo.remoting.telnet.TelnetHandler;
+
+/**
+ * ExchangeHandler. (API, Prototype, ThreadSafe)
+ *
+ * @author william.liangf
+ */
+public interface ExchangeHandler extends ChannelHandler, TelnetHandler {
+
+ /**
+ * reply.
+ *
+ * @param channel
+ * @param request
+ * @return response
+ * @throws RemotingException
+ */
+ Object reply(ExchangeChannel channel, Object request) throws RemotingException;
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/exchange/ExchangeServer.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/exchange/ExchangeServer.java
new file mode 100644
index 0000000..55ff1ba
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/exchange/ExchangeServer.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting.exchange;
+
+import java.net.InetSocketAddress;
+import java.util.Collection;
+
+import com.alibaba.dubbo.remoting.Server;
+
+/**
+ * ExchangeServer. (API/SPI, Prototype, ThreadSafe)
+ *
+ * @author william.liangf
+ */
+public interface ExchangeServer extends Server {
+
+ /**
+ * get channels.
+ *
+ * @return channels
+ */
+ Collection<ExchangeChannel> getExchangeChannels();
+
+ /**
+ * get channel.
+ *
+ * @param remoteAddress
+ * @return channel
+ */
+ ExchangeChannel getExchangeChannel(InetSocketAddress remoteAddress);
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/exchange/Exchanger.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/exchange/Exchanger.java
new file mode 100644
index 0000000..10ab3ed
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/exchange/Exchanger.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting.exchange;
+
+import com.alibaba.dubbo.common.Adaptive;
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.remoting.RemotingException;
+import com.alibaba.dubbo.remoting.exchange.support.header.HeaderExchanger;
+
+/**
+ * Exchanger. (SPI, Singleton, ThreadSafe)
+ *
+ * <a href="http://en.wikipedia.org/wiki/Message_Exchange_Pattern">Message Exchange Pattern</a>
+ * <a href="http://en.wikipedia.org/wiki/Request-response">Request-Response</a>
+ *
+ * @author william.liangf
+ */
+@Extension(HeaderExchanger.NAME)
+public interface Exchanger {
+
+ /**
+ * bind.
+ *
+ * @param url
+ * @param handler
+ * @return message server
+ */
+ @Adaptive({Constants.EXCHANGER_KEY})
+ ExchangeServer bind(URL url, ExchangeHandler handler) throws RemotingException;
+
+ /**
+ * connect.
+ *
+ * @param url
+ * @param handler
+ * @return message channel
+ */
+ @Adaptive({Constants.EXCHANGER_KEY})
+ ExchangeClient connect(URL url, ExchangeHandler handler) throws RemotingException;
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/exchange/Exchangers.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/exchange/Exchangers.java
new file mode 100644
index 0000000..f2cd282
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/exchange/Exchangers.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting.exchange;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.ExtensionLoader;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.Version;
+import com.alibaba.dubbo.remoting.ChannelHandler;
+import com.alibaba.dubbo.remoting.RemotingException;
+import com.alibaba.dubbo.remoting.exchange.support.ExchangeHandlerDispatcher;
+import com.alibaba.dubbo.remoting.exchange.support.Replier;
+import com.alibaba.dubbo.remoting.transport.ChannelHandlerAdapter;
+
+/**
+ * Exchanger facade. (API, Static, ThreadSafe)
+ *
+ * @author william.liangf
+ */
+public class Exchangers {
+
+ public static ExchangeServer bind(String url, Replier<?> replier) throws RemotingException {
+ return bind(URL.valueOf(url), replier);
+ }
+
+ public static ExchangeServer bind(URL url, Replier<?> replier) throws RemotingException {
+ return bind(url, new ChannelHandlerAdapter(), replier);
+ }
+
+ public static ExchangeServer bind(String url, ChannelHandler handler, Replier<?> replier) throws RemotingException {
+ return bind(URL.valueOf(url), handler, replier);
+ }
+
+ public static ExchangeServer bind(URL url, ChannelHandler handler, Replier<?> replier) throws RemotingException {
+ return bind(url, new ExchangeHandlerDispatcher(replier, handler));
+ }
+
+ public static ExchangeServer bind(String url, ExchangeHandler handler) throws RemotingException {
+ return bind(URL.valueOf(url), handler);
+ }
+
+ public static ExchangeServer bind(URL url, ExchangeHandler handler) throws RemotingException {
+ if (url == null) {
+ throw new IllegalArgumentException("url == null");
+ }
+ if (handler == null) {
+ throw new IllegalArgumentException("handler == null");
+ }
+ url = url.addParameterIfAbsent(Constants.CODEC_KEY, "exchange");
+ return getExchanger(url).bind(url, handler);
+ }
+
+ public static ExchangeClient connect(String url) throws RemotingException {
+ return connect(URL.valueOf(url));
+ }
+
+ public static ExchangeClient connect(URL url) throws RemotingException {
+ return connect(url, new ChannelHandlerAdapter(), null);
+ }
+
+ public static ExchangeClient connect(String url, Replier<?> replier) throws RemotingException {
+ return connect(URL.valueOf(url), new ChannelHandlerAdapter(), replier);
+ }
+
+ public static ExchangeClient connect(URL url, Replier<?> replier) throws RemotingException {
+ return connect(url, new ChannelHandlerAdapter(), replier);
+ }
+
+ public static ExchangeClient connect(String url, ChannelHandler handler, Replier<?> replier) throws RemotingException {
+ return connect(URL.valueOf(url), handler, replier);
+ }
+
+ public static ExchangeClient connect(URL url, ChannelHandler handler, Replier<?> replier) throws RemotingException {
+ return connect(url, new ExchangeHandlerDispatcher(replier, handler));
+ }
+
+ public static ExchangeClient connect(String url, ExchangeHandler handler) throws RemotingException {
+ return connect(URL.valueOf(url), handler);
+ }
+
+ public static ExchangeClient connect(URL url, ExchangeHandler handler) throws RemotingException {
+ if (url == null) {
+ throw new IllegalArgumentException("url == null");
+ }
+ if (handler == null) {
+ throw new IllegalArgumentException("handler == null");
+ }
+ url = url.addParameterIfAbsent(Constants.CODEC_KEY, "exchange");
+ return getExchanger(url).connect(url, handler);
+ }
+
+ public static Exchanger getExchanger(URL url) {
+ String type = url.getParameter(Constants.EXCHANGER_KEY, Constants.DEFAULT_EXCHANGER);
+ return getExchanger(type);
+ }
+
+ public static Exchanger getExchanger(String type) {
+ return ExtensionLoader.getExtensionLoader(Exchanger.class).getExtension(type);
+ }
+
+ static {
+ // check duplicate jar package
+ Version.checkDuplicate(Exchangers.class);
+ }
+
+ private Exchangers(){
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/exchange/Request.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/exchange/Request.java
new file mode 100644
index 0000000..60e8328
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/exchange/Request.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.remoting.exchange;
+
+import java.util.concurrent.atomic.AtomicLong;
+
+/**
+ * Request.
+ *
+ * @author qian.lei
+ * @author william.liangf
+ */
+public class Request {
+
+ private static final AtomicLong INVOKE_ID = new AtomicLong(0);
+
+ private final long mId;
+
+ private String mVersion;
+
+ private boolean mTwoWay = true;
+
+ private boolean mHeatbeat = 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 isHeartbeat() {
+ return mHeatbeat;
+ }
+
+ public void setHeartbeat(boolean isHeartbeat) {
+ this.mHeatbeat = isHeartbeat;
+ }
+
+ 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;
+ }
+
+ 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 + ", heatbeat=" + mHeatbeat
+ + ", broken=" + mBroken + ", data=" + (mData == this ? "this" : mData) + "]";
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/exchange/Response.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/exchange/Response.java
new file mode 100644
index 0000000..7cac992
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/exchange/Response.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.remoting.exchange;
+
+/**
+ * Response
+ *
+ * @author qian.lei
+ * @author william.liangf
+ */
+public class Response {
+
+ /**
+ * 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;
+
+ /**
+ * 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 mHeatbeat = 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 isHeartbeat() {
+ return mHeatbeat;
+ }
+
+ public void setHeartbeat(boolean isHeatbeat) {
+ this.mHeatbeat = isHeatbeat;
+ }
+
+ 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 + ", heatbeat=" + mHeatbeat
+ + ", error=" + mErrorMsg + ", result=" + (mResult == this ? "this" : mResult) + "]";
+ }
+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/exchange/ResponseCallback.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/exchange/ResponseCallback.java
new file mode 100644
index 0000000..efe3d8b
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/exchange/ResponseCallback.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting.exchange;
+
+/**
+ * Callback
+ *
+ * @author william.liangf
+ */
+public interface ResponseCallback {
+
+ /**
+ * done.
+ *
+ * @param response
+ */
+ void done(Object response);
+
+ /**
+ * caught exception.
+ *
+ * @param exception
+ */
+ void caught(Throwable exception);
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/exchange/ResponseFuture.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/exchange/ResponseFuture.java
new file mode 100644
index 0000000..aabee8c
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/exchange/ResponseFuture.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting.exchange;
+
+import com.alibaba.dubbo.remoting.RemotingException;
+
+/**
+ * Future. (API/SPI, Prototype, ThreadSafe)
+ *
+ * @see com.alibaba.dubbo.remoting.Channel#request(Object)
+ * @see com.alibaba.dubbo.remoting.Channel#request(Object, int)
+ * @author qian.lei
+ * @author william.liangf
+ */
+public interface ResponseFuture {
+
+ /**
+ * get result.
+ *
+ * @return result.
+ */
+ Object get() throws RemotingException;
+
+ /**
+ * get result with the specified timeout.
+ *
+ * @param timeoutInMillis timeout.
+ * @return result.
+ */
+ Object get(int timeoutInMillis) throws RemotingException;
+
+ /**
+ * set callback.
+ *
+ * @param callback
+ */
+ void setCallback(ResponseCallback callback);
+
+ /**
+ * check is done.
+ *
+ * @return done or not.
+ */
+ boolean isDone();
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/exchange/codec/ExchangeCodec.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/exchange/codec/ExchangeCodec.java
new file mode 100644
index 0000000..881b6b3
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/exchange/codec/ExchangeCodec.java
@@ -0,0 +1,362 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting.exchange.codec;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.common.ExtensionLoader;
+import com.alibaba.dubbo.common.io.Bytes;
+import com.alibaba.dubbo.common.io.StreamUtils;
+import com.alibaba.dubbo.common.io.UnsafeByteArrayInputStream;
+import com.alibaba.dubbo.common.io.UnsafeByteArrayOutputStream;
+import com.alibaba.dubbo.common.logger.Logger;
+import com.alibaba.dubbo.common.logger.LoggerFactory;
+import com.alibaba.dubbo.common.serialize.ObjectInput;
+import com.alibaba.dubbo.common.serialize.ObjectOutput;
+import com.alibaba.dubbo.common.serialize.Serialization;
+import com.alibaba.dubbo.common.utils.StringUtils;
+import com.alibaba.dubbo.remoting.Channel;
+import com.alibaba.dubbo.remoting.exchange.Request;
+import com.alibaba.dubbo.remoting.exchange.Response;
+import com.alibaba.dubbo.remoting.telnet.codec.TelnetCodec;
+
+/**
+ * ExchangeCodec.
+ *
+ * @author qianlei
+ * @author william.liangf
+ */
+@Extension("exchange")
+public class ExchangeCodec extends TelnetCodec {
+
+ private static final Logger logger = LoggerFactory.getLogger(ExchangeCodec.class);
+
+ // header length.
+ protected static final int HEADER_LENGTH = 16;
+
+ // magic header.
+ protected static final short MAGIC = (short) 0xdabb;
+
+ protected static final byte MAGIC_HIGH = (byte) Bytes.short2bytes(MAGIC)[0];
+
+ protected static final byte MAGIC_LOW = (byte) 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_HEARTBEAT = (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);
+ res.setHeartbeat( ( flag & FLAG_HEARTBEAT ) != 0 );
+ // get status.
+ byte status = header[3];
+ res.setStatus(status);
+ if( status == Response.OK ) {
+ try {
+ Object data;
+ if (res.isHeartbeat()) {
+ data = decodeHeartbeatData(channel, in);
+ } else {
+ data = decodeResponseData(channel, in);
+ }
+ 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 );
+ req.setHeartbeat( ( flag & FLAG_HEARTBEAT ) != 0 );
+ try {
+ Object data;
+ if (req.isHeartbeat()) {
+ data = decodeHeartbeatData(channel, in);
+ } else {
+ data = decodeRequestData(channel, in);
+ }
+ req.setData(data);
+ } catch (Throwable t) {
+ // bad request
+ req.setBroken(true);
+ req.setData(t);
+ }
+ return req;
+ }
+ }
+
+ 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.isHeartbeat()) header[2] |= FLAG_HEARTBEAT;
+
+ // 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.isHeartbeat()) {
+ encodeHeartbeatData(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 {
+ 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_HEARTBEAT;
+ // 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.
+ }
+
+ private static final Serialization getSerializationById(Byte id) {
+ return ID_SERIALIZATION_MAP.get(id);
+ }
+
+ @Override
+ protected Object decodeData(ObjectInput in) throws IOException {
+ return decodeRequestData(in);
+ }
+
+ 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);
+ }
+
+ protected void encodeHeartbeatData(ObjectOutput out, Object data) throws IOException {
+ out.writeObject(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);
+ }
+
+ 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);
+ }
+
+ @Override
+ protected void encodeData(Channel channel, ObjectOutput out, Object data) throws IOException {
+ encodeRequestData(channel, out, data);
+ }
+
+ protected void encodeHeartbeatData(Channel channel, ObjectOutput out, Object data) throws IOException {
+ encodeHeartbeatData(out, data);
+ }
+
+ protected void encodeRequestData(Channel channel, ObjectOutput out, Object data) throws IOException {
+ encodeRequestData(out, data);
+ }
+
+ protected void encodeResponseData(Channel channel, ObjectOutput out, Object data) throws IOException {
+ encodeResponseData(out, data);
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/exchange/support/DefaultFuture.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/exchange/support/DefaultFuture.java
new file mode 100644
index 0000000..1b979d9
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/exchange/support/DefaultFuture.java
@@ -0,0 +1,310 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.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 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 reponse 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(), "remoting-invocation-timeout-scan");
+ th.setDaemon(true);
+ th.start();
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/exchange/support/ExchangeHandlerAdapter.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/exchange/support/ExchangeHandlerAdapter.java
new file mode 100644
index 0000000..378d7fb
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/exchange/support/ExchangeHandlerAdapter.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting.exchange.support;
+
+import com.alibaba.dubbo.remoting.RemotingException;
+import com.alibaba.dubbo.remoting.exchange.ExchangeChannel;
+import com.alibaba.dubbo.remoting.exchange.ExchangeHandler;
+import com.alibaba.dubbo.remoting.telnet.support.TelnetHandlerAdapter;
+
+/**
+ * ExchangeHandlerAdapter
+ *
+ * @author william.liangf
+ */
+public abstract class ExchangeHandlerAdapter extends TelnetHandlerAdapter implements ExchangeHandler {
+
+ public Object reply(ExchangeChannel channel, Object msg) throws RemotingException {
+ return null;
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/exchange/support/ExchangeHandlerDispatcher.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/exchange/support/ExchangeHandlerDispatcher.java
new file mode 100644
index 0000000..895b190
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/exchange/support/ExchangeHandlerDispatcher.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting.exchange.support;
+
+import com.alibaba.dubbo.remoting.Channel;
+import com.alibaba.dubbo.remoting.ChannelHandler;
+import com.alibaba.dubbo.remoting.RemotingException;
+import com.alibaba.dubbo.remoting.exchange.ExchangeChannel;
+import com.alibaba.dubbo.remoting.exchange.ExchangeHandler;
+import com.alibaba.dubbo.remoting.telnet.TelnetHandler;
+import com.alibaba.dubbo.remoting.telnet.support.TelnetHandlerAdapter;
+import com.alibaba.dubbo.remoting.transport.ChannelHandlerDispatcher;
+
+/**
+ * ExchangeHandlerDispatcher
+ *
+ * @author william.liangf
+ */
+public class ExchangeHandlerDispatcher implements ExchangeHandler {
+
+ private final ReplierDispatcher replierDispatcher;
+
+ private final ChannelHandlerDispatcher handlerDispatcher;
+
+ private final TelnetHandler telnetHandler;
+
+ public ExchangeHandlerDispatcher() {
+ replierDispatcher = new ReplierDispatcher();
+ handlerDispatcher = new ChannelHandlerDispatcher();
+ telnetHandler = new TelnetHandlerAdapter();
+ }
+
+ public ExchangeHandlerDispatcher(Replier<?> replier){
+ replierDispatcher = new ReplierDispatcher(replier);
+ handlerDispatcher = new ChannelHandlerDispatcher();
+ telnetHandler = new TelnetHandlerAdapter();
+ }
+
+ public ExchangeHandlerDispatcher(ChannelHandler... handlers){
+ replierDispatcher = new ReplierDispatcher();
+ handlerDispatcher = new ChannelHandlerDispatcher(handlers);
+ telnetHandler = new TelnetHandlerAdapter();
+ }
+
+ public ExchangeHandlerDispatcher(Replier<?> replier, ChannelHandler... handlers){
+ replierDispatcher = new ReplierDispatcher(replier);
+ handlerDispatcher = new ChannelHandlerDispatcher(handlers);
+ telnetHandler = new TelnetHandlerAdapter();
+ }
+
+ public ExchangeHandlerDispatcher addChannelHandler(ChannelHandler handler) {
+ handlerDispatcher.addChannelHandler(handler);
+ return this;
+ }
+
+ public ExchangeHandlerDispatcher removeChannelHandler(ChannelHandler handler) {
+ handlerDispatcher.removeChannelHandler(handler);
+ return this;
+ }
+
+ public <T> ExchangeHandlerDispatcher addReplier(Class<T> type, Replier<T> replier) {
+ replierDispatcher.addReplier(type, replier);
+ return this;
+ }
+
+ public <T> ExchangeHandlerDispatcher removeReplier(Class<T> type) {
+ replierDispatcher.removeReplier(type);
+ return this;
+ }
+
+ @SuppressWarnings({ "unchecked", "rawtypes" })
+ public Object reply(ExchangeChannel channel, Object request) throws RemotingException {
+ return ((Replier)replierDispatcher).reply(channel, request);
+ }
+
+ public void connected(Channel channel) {
+ handlerDispatcher.connected(channel);
+ }
+
+ public void disconnected(Channel channel) {
+ handlerDispatcher.disconnected(channel);
+ }
+
+ public void sent(Channel channel, Object message) {
+ handlerDispatcher.sent(channel, message);
+ }
+
+ public void received(Channel channel, Object message) {
+ handlerDispatcher.received(channel, message);
+ }
+
+ public void caught(Channel channel, Throwable exception) {
+ handlerDispatcher.caught(channel, exception);
+ }
+
+ public String telnet(Channel channel, String message) throws RemotingException {
+ return telnetHandler.telnet(channel, message);
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/exchange/support/ExchangeServerDelegate.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/exchange/support/ExchangeServerDelegate.java
new file mode 100644
index 0000000..b15efcd
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/exchange/support/ExchangeServerDelegate.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting.exchange.support;
+
+import java.net.InetSocketAddress;
+import java.util.Collection;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.remoting.Channel;
+import com.alibaba.dubbo.remoting.ChannelHandler;
+import com.alibaba.dubbo.remoting.RemotingException;
+import com.alibaba.dubbo.remoting.exchange.ExchangeChannel;
+import com.alibaba.dubbo.remoting.exchange.ExchangeServer;
+
+/**
+ * ExchangeServerDelegate
+ *
+ * @author william.liangf
+ */
+public class ExchangeServerDelegate implements ExchangeServer {
+
+ private transient ExchangeServer server;
+
+ public ExchangeServerDelegate() {
+ }
+
+ public ExchangeServerDelegate(ExchangeServer server){
+ setServer(server);
+ }
+
+ public ExchangeServer getServer() {
+ return server;
+ }
+
+ public void setServer(ExchangeServer server) {
+ this.server = server;
+ }
+
+ public boolean isBound() {
+ return server.isBound();
+ }
+
+ public void reset(URL url) {
+ server.reset(url);
+ }
+
+ @Deprecated
+ public void reset(com.alibaba.dubbo.common.Parameters parameters){
+ reset(getUrl().addParameters(parameters.getParameters()));
+ }
+
+ public Collection<Channel> getChannels() {
+ return server.getChannels();
+ }
+
+ public Channel getChannel(InetSocketAddress remoteAddress) {
+ return server.getChannel(remoteAddress);
+ }
+
+ public URL getUrl() {
+ return server.getUrl();
+ }
+
+ public ChannelHandler getChannelHandler() {
+ return server.getChannelHandler();
+ }
+
+ public InetSocketAddress getLocalAddress() {
+ return server.getLocalAddress();
+ }
+
+ public void send(Object message) throws RemotingException {
+ server.send(message);
+ }
+
+ public void send(Object message, boolean sent) throws RemotingException {
+ server.send(message, sent);
+ }
+
+ public void close() {
+ server.close();
+ }
+
+ public boolean isClosed() {
+ return server.isClosed();
+ }
+
+ public Collection<ExchangeChannel> getExchangeChannels() {
+ return server.getExchangeChannels();
+ }
+
+ public ExchangeChannel getExchangeChannel(InetSocketAddress remoteAddress) {
+ return server.getExchangeChannel(remoteAddress);
+ }
+
+ public void close(int timeout) {
+ server.close(timeout);
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/exchange/support/Replier.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/exchange/support/Replier.java
new file mode 100644
index 0000000..01a3c95
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/exchange/support/Replier.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting.exchange.support;
+
+import com.alibaba.dubbo.remoting.RemotingException;
+import com.alibaba.dubbo.remoting.exchange.ExchangeChannel;
+
+/**
+ * Replier. (API, Prototype, ThreadSafe)
+ *
+ * @author william.liangf
+ */
+public interface Replier<T> {
+
+ /**
+ * reply.
+ *
+ * @param channel
+ * @param request
+ * @return response
+ * @throws RemotingException
+ */
+ Object reply(ExchangeChannel channel, T request) throws RemotingException;
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/exchange/support/ReplierDispatcher.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/exchange/support/ReplierDispatcher.java
new file mode 100644
index 0000000..8a7ff7c
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/exchange/support/ReplierDispatcher.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting.exchange.support;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import com.alibaba.dubbo.remoting.RemotingException;
+import com.alibaba.dubbo.remoting.exchange.ExchangeChannel;
+
+/**
+ * ReplierDispatcher
+ *
+ * @author william.liangf
+ */
+public class ReplierDispatcher implements Replier<Object> {
+
+ private final Replier<?> defaultReplier;
+
+ private final Map<Class<?>, Replier<?>> repliers = new ConcurrentHashMap<Class<?>, Replier<?>>();
+
+ public ReplierDispatcher(){
+ this(null, null);
+ }
+
+ public ReplierDispatcher(Replier<?> defaultReplier){
+ this(defaultReplier, null);
+ }
+
+ public ReplierDispatcher(Replier<?> defaultReplier, Map<Class<?>, Replier<?>> repliers){
+ this.defaultReplier = defaultReplier;
+ if (repliers != null && repliers.size() > 0) {
+ this.repliers.putAll(repliers);
+ }
+ }
+
+ public <T> ReplierDispatcher addReplier(Class<T> type, Replier<T> replier) {
+ repliers.put(type, replier);
+ return this;
+ }
+
+ public <T> ReplierDispatcher removeReplier(Class<T> type) {
+ repliers.remove(type);
+ return this;
+ }
+
+ private Replier<?> getReplier(Class<?> type) {
+ for(Map.Entry<Class<?>, Replier<?>> entry : repliers.entrySet()) {
+ if(entry.getKey().isAssignableFrom(type)) {
+ return entry.getValue();
+ }
+ }
+ if (defaultReplier != null) {
+ return defaultReplier;
+ }
+ throw new IllegalStateException("Replier not found, Unsupported message object: " + type);
+ }
+
+ @SuppressWarnings({ "unchecked", "rawtypes" })
+ public Object reply(ExchangeChannel channel, Object request) throws RemotingException {
+ return ((Replier)getReplier(request.getClass())).reply(channel, request);
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/exchange/support/SimpleFuture.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/exchange/support/SimpleFuture.java
new file mode 100644
index 0000000..c09a0f0
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/exchange/support/SimpleFuture.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting.exchange.support;
+
+import com.alibaba.dubbo.remoting.RemotingException;
+import com.alibaba.dubbo.remoting.exchange.ResponseCallback;
+import com.alibaba.dubbo.remoting.exchange.ResponseFuture;
+
+/**
+ * SimpleFuture
+ *
+ * @author william.liangf
+ */
+public class SimpleFuture implements ResponseFuture {
+
+ private final Object value;
+
+ public SimpleFuture(Object value){
+ this.value = value;
+ }
+
+ public Object get() throws RemotingException {
+ return value;
+ }
+
+ public Object get(int timeoutInMillis) throws RemotingException {
+ return value;
+ }
+
+ public void setCallback(ResponseCallback callback) {
+ callback.done(value);
+ }
+
+ public boolean isDone() {
+ return true;
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/exchange/support/header/HeaderExchangeChannel.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/exchange/support/header/HeaderExchangeChannel.java
new file mode 100644
index 0000000..aa8341b
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/exchange/support/header/HeaderExchangeChannel.java
@@ -0,0 +1,217 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting.exchange.support.header;
+
+import java.net.InetSocketAddress;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.logger.Logger;
+import com.alibaba.dubbo.common.logger.LoggerFactory;
+import com.alibaba.dubbo.remoting.Channel;
+import com.alibaba.dubbo.remoting.ChannelHandler;
+import com.alibaba.dubbo.remoting.RemotingException;
+import com.alibaba.dubbo.remoting.exchange.ExchangeChannel;
+import com.alibaba.dubbo.remoting.exchange.ExchangeHandler;
+import com.alibaba.dubbo.remoting.exchange.Request;
+import com.alibaba.dubbo.remoting.exchange.Response;
+import com.alibaba.dubbo.remoting.exchange.ResponseFuture;
+import com.alibaba.dubbo.remoting.exchange.support.DefaultFuture;
+
+/**
+ * ExchangeReceiver
+ *
+ * @author william.liangf
+ */
+final class HeaderExchangeChannel implements ExchangeChannel {
+
+ private static final Logger logger = LoggerFactory.getLogger(HeaderExchangeChannel.class);
+
+ private static final String CHANNEL_KEY = HeaderExchangeChannel.class.getName() + ".CHANNEL";
+
+ private final Channel channel;
+
+ private volatile boolean closed = false;
+
+ HeaderExchangeChannel(Channel channel){
+ if (channel == null) {
+ throw new IllegalArgumentException("channel == null");
+ }
+ this.channel = channel;
+ }
+
+ static HeaderExchangeChannel getOrAddChannel(Channel ch) {
+ if (ch == null) {
+ return null;
+ }
+ HeaderExchangeChannel ret = (HeaderExchangeChannel) ch.getAttribute(CHANNEL_KEY);
+ if (ret == null) {
+ ret = new HeaderExchangeChannel(ch);
+ if (ch.isConnected()) {
+ ch.setAttribute(CHANNEL_KEY, ret);
+ }
+ }
+ return ret;
+ }
+
+ static void removeChannelIfDisconnected(Channel ch) {
+ if (ch != null && ! ch.isConnected()) {
+ ch.removeAttribute(CHANNEL_KEY);
+ }
+ }
+
+ public void send(Object message) throws RemotingException {
+ send(message, getUrl().getParameter(Constants.SENT_KEY, false));
+ }
+
+ public void send(Object message, boolean sent) throws RemotingException {
+ if (closed) {
+ throw new RemotingException(this.getLocalAddress(), null, "Failed to send message " + message + ", cause: The channel " + this + " is closed!");
+ }
+ if (message instanceof Request
+ || message instanceof Response
+ || message instanceof String) {
+ channel.send(message, sent);
+ } else {
+ Request request = new Request();
+ request.setVersion("2.0.0");
+ request.setTwoWay(false);
+ request.setData(message);
+ channel.send(request, sent);
+ }
+ }
+
+ public ResponseFuture request(Object request) throws RemotingException {
+ return request(request, channel.getUrl().getPositiveParameter(Constants.TIMEOUT_KEY, Constants.DEFAULT_TIMEOUT));
+ }
+
+ public ResponseFuture request(Object request, int timeout) throws RemotingException {
+ if (closed) {
+ throw new RemotingException(this.getLocalAddress(), null, "Failed to send request " + request + ", cause: The channel " + this + " is closed!");
+ }
+ // create request.
+ Request req = new Request();
+ req.setVersion("2.0.0");
+ req.setTwoWay(true);
+ req.setData(request);
+ DefaultFuture future = new DefaultFuture(channel, req, timeout);
+ try{
+ channel.send(req);
+ }catch (RemotingException e) {
+ future.cancel();
+ throw e;
+ }
+ return future;
+ }
+
+ public boolean isClosed() {
+ return closed;
+ }
+
+ public void close() {
+ try {
+ channel.close();
+ } catch (Throwable e) {
+ logger.warn(e.getMessage(), e);
+ }
+ }
+
+ // graceful close
+ public void close(int timeout) {
+ if (closed) {
+ return;
+ }
+ closed = true;
+ if (timeout > 0) {
+ long start = System.currentTimeMillis();
+ while (DefaultFuture.hasFuture(HeaderExchangeChannel.this)
+ && System.currentTimeMillis() - start < timeout) {
+ try {
+ Thread.sleep(10);
+ } catch (InterruptedException e) {
+ logger.warn(e.getMessage(), e);
+ }
+ }
+ }
+ close();
+ }
+
+ public InetSocketAddress getLocalAddress() {
+ return channel.getLocalAddress();
+ }
+
+ public InetSocketAddress getRemoteAddress() {
+ return channel.getRemoteAddress();
+ }
+
+ public URL getUrl() {
+ return channel.getUrl();
+ }
+
+ public boolean isConnected() {
+ return channel.isConnected();
+ }
+
+ public ChannelHandler getChannelHandler() {
+ return channel.getChannelHandler();
+ }
+
+ public ExchangeHandler getExchangeHandler() {
+ return (ExchangeHandler) channel.getChannelHandler();
+ }
+
+ public Object getAttribute(String key) {
+ return channel.getAttribute(key);
+ }
+
+ public void setAttribute(String key, Object value) {
+ channel.setAttribute(key, value);
+ }
+
+ public void removeAttribute(String key) {
+ channel.removeAttribute(key);
+ }
+
+ public boolean hasAttribute(String key) {
+ return channel.hasAttribute(key);
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((channel == null) ? 0 : channel.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) return true;
+ if (obj == null) return false;
+ if (getClass() != obj.getClass()) return false;
+ HeaderExchangeChannel other = (HeaderExchangeChannel) obj;
+ if (channel == null) {
+ if (other.channel != null) return false;
+ } else if (!channel.equals(other.channel)) return false;
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ return channel.toString();
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/exchange/support/header/HeaderExchangeClient.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/exchange/support/header/HeaderExchangeClient.java
new file mode 100644
index 0000000..c942282
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/exchange/support/header/HeaderExchangeClient.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting.exchange.support.header;
+
+import java.net.InetSocketAddress;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.remoting.ChannelHandler;
+import com.alibaba.dubbo.remoting.Client;
+import com.alibaba.dubbo.remoting.RemotingException;
+import com.alibaba.dubbo.remoting.exchange.ExchangeChannel;
+import com.alibaba.dubbo.remoting.exchange.ExchangeClient;
+import com.alibaba.dubbo.remoting.exchange.ExchangeHandler;
+import com.alibaba.dubbo.remoting.exchange.ResponseFuture;
+
+/**
+ * DefaultMessageClient
+ *
+ * @author william.liangf
+ */
+public class HeaderExchangeClient implements ExchangeClient {
+
+ private final Client client;
+
+ private final ExchangeChannel channel;
+
+ public HeaderExchangeClient(Client client){
+ if (client == null) {
+ throw new IllegalArgumentException("client == null");
+ }
+ this.client = client;
+ this.channel = new HeaderExchangeChannel(client);
+ }
+
+ public ResponseFuture request(Object request) throws RemotingException {
+ return channel.request(request);
+ }
+
+ public URL getUrl() {
+ return channel.getUrl();
+ }
+
+ public InetSocketAddress getRemoteAddress() {
+ return channel.getRemoteAddress();
+ }
+
+ public ResponseFuture request(Object request, int timeout) throws RemotingException {
+ return channel.request(request, timeout);
+ }
+
+ public ChannelHandler getChannelHandler() {
+ return channel.getChannelHandler();
+ }
+
+ public boolean isConnected() {
+ return channel.isConnected();
+ }
+
+ public InetSocketAddress getLocalAddress() {
+ return channel.getLocalAddress();
+ }
+
+ public ExchangeHandler getExchangeHandler() {
+ return channel.getExchangeHandler();
+ }
+
+ public void send(Object message) throws RemotingException {
+ channel.send(message);
+ }
+
+ public void send(Object message, boolean sent) throws RemotingException {
+ channel.send(message, sent);
+ }
+
+ public boolean isClosed() {
+ return channel.isClosed();
+ }
+
+ public void close() {
+ channel.close();
+ }
+
+ public void close(int timeout) {
+ channel.close(timeout);
+ }
+
+ public void reset(URL url) {
+ client.reset(url);
+ }
+
+ @Deprecated
+ public void reset(com.alibaba.dubbo.common.Parameters parameters){
+ reset(getUrl().addParameters(parameters.getParameters()));
+ }
+
+ public void reconnect() throws RemotingException {
+ client.reconnect();
+ }
+
+ public Object getAttribute(String key) {
+ return channel.getAttribute(key);
+ }
+
+ public void setAttribute(String key, Object value) {
+ channel.setAttribute(key, value);
+ }
+
+ public void removeAttribute(String key) {
+ channel.removeAttribute(key);
+ }
+
+ public boolean hasAttribute(String key) {
+ return channel.hasAttribute(key);
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/exchange/support/header/HeaderExchangeHandler.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/exchange/support/header/HeaderExchangeHandler.java
new file mode 100644
index 0000000..9ecc697
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/exchange/support/header/HeaderExchangeHandler.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.exchange.support.header;
+
+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.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;
+
+/**
+ * ExchangeReceiver
+ *
+ * @author william.liangf
+ */
+public class HeaderExchangeHandler implements ChannelHandler {
+
+ 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;
+ }
+
+ Response handleRequest(ExchangeChannel channel, Request req) throws RemotingException {
+ Response res = new Response(req.getId(), req.getVersion());
+ if (req.isHeartbeat()) {
+ res.setHeartbeat(true);
+ return res;
+ }
+
+ 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();
+ if (handler == null) {// no handler.
+ res.setStatus(Response.SERVICE_NOT_FOUND);
+ res.setErrorMessage("InvokeHandler not found, Unsupported protocol object: " + msg);
+ } else {
+ 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);
+ }
+ }
+ }
+
+ 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.isTwoWay()) {
+ Response response = handleRequest(exchangeChannel, request);
+ if (response == null) {
+ throw new RemotingException(channel, "Response is null.");
+ }
+ channel.send(response);
+ } else {
+ handler.received(exchangeChannel, request.getData());
+ }
+ } else if (message instanceof Response) {
+ handleResponse(channel, (Response) message);
+ } else if (message instanceof String) {
+ 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);
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/exchange/support/header/HeaderExchangeServer.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/exchange/support/header/HeaderExchangeServer.java
new file mode 100644
index 0000000..9017465
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/exchange/support/header/HeaderExchangeServer.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.remoting.exchange.support.header;
+
+import java.net.InetSocketAddress;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.logger.Logger;
+import com.alibaba.dubbo.common.logger.LoggerFactory;
+import com.alibaba.dubbo.common.utils.NamedThreadFactory;
+import com.alibaba.dubbo.remoting.Channel;
+import com.alibaba.dubbo.remoting.ChannelHandler;
+import com.alibaba.dubbo.remoting.RemotingException;
+import com.alibaba.dubbo.remoting.Server;
+import com.alibaba.dubbo.remoting.exchange.ExchangeChannel;
+import com.alibaba.dubbo.remoting.exchange.ExchangeServer;
+import com.alibaba.dubbo.remoting.exchange.Request;
+import com.alibaba.dubbo.remoting.exchange.support.DefaultFuture;
+
+/**
+ * ExchangeServerImpl
+ *
+ * @author william.liangf
+ */
+public class HeaderExchangeServer implements ExchangeServer {
+
+ protected final Logger logger = LoggerFactory.getLogger(getClass());
+
+ private final ScheduledExecutorService scheduled = Executors.newScheduledThreadPool(1,
+ new NamedThreadFactory(
+ "dubbo-remoting-server-heartbeat",
+ true));
+
+ // 心跳定时器
+ private ScheduledFuture<?> heatbeatTimer;
+
+ // 心跳超时,毫秒。缺省0,不会执行心跳。
+ private int heartbeat;
+
+ private int heartbeatTimeout;
+
+ private final Server server;
+
+ private volatile boolean closed = false;
+
+ public HeaderExchangeServer(Server server) {
+ if (server == null) {
+ throw new IllegalArgumentException("server == null");
+ }
+ this.server = server;
+ this.heartbeat = server.getUrl().getParameter(Constants.HEARTBEAT_KEY, Constants.DEFAULT_HEARTBEAT);
+ this.heartbeatTimeout = server.getUrl().getParameter(Constants.HEARTBEAT_TIMEOUT_KEY, heartbeat * 3);
+ if (heartbeatTimeout < heartbeat * 2) {
+ throw new IllegalStateException("heartbeatTimeout < heartbeatInterval * 2");
+ }
+ startHeatbeatTimer();
+ }
+
+ public Server getServer() {
+ return server;
+ }
+
+ public boolean isClosed() {
+ return server.isClosed();
+ }
+
+ private boolean isRunning() {
+ Collection<Channel> channels = getChannels();
+ for (Channel channel : channels) {
+ if (DefaultFuture.hasFuture(channel)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public void close() {
+ doClose();
+ server.close();
+ }
+
+ public void close(final int timeout) {
+ if (timeout > 0) {
+ final long max = (long) timeout;
+ final long start = System.currentTimeMillis();
+ 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 doClose() {
+ if (closed) {
+ return;
+ }
+ closed = true;
+ try {
+ if (null != heatbeatTimer) {
+ heatbeatTimer.cancel(true);
+ heatbeatTimer = null;
+ }
+ } catch (Throwable t) {
+ logger.warn(t.getMessage(), t);
+ }
+ try {
+ scheduled.shutdown();
+ } catch (Throwable t) {
+ logger.warn(t.getMessage(), t);
+ }
+ }
+
+ public Collection<ExchangeChannel> getExchangeChannels() {
+ Collection<ExchangeChannel> exchangeChannels = new ArrayList<ExchangeChannel>();
+ Collection<Channel> channels = server.getChannels();
+ if (channels != null && channels.size() > 0) {
+ for (Channel channel : channels) {
+ exchangeChannels.add(HeaderExchangeChannel.getOrAddChannel(channel));
+ }
+ }
+ return exchangeChannels;
+ }
+
+ public ExchangeChannel getExchangeChannel(InetSocketAddress remoteAddress) {
+ Channel channel = server.getChannel(remoteAddress);
+ return HeaderExchangeChannel.getOrAddChannel(channel);
+ }
+
+ @SuppressWarnings({ "unchecked", "rawtypes" })
+ public Collection<Channel> getChannels() {
+ return (Collection)getExchangeChannels();
+ }
+
+ public Channel getChannel(InetSocketAddress remoteAddress) {
+ return getExchangeChannel(remoteAddress);
+ }
+
+ public boolean isBound() {
+ return server.isBound();
+ }
+
+ public InetSocketAddress getLocalAddress() {
+ return server.getLocalAddress();
+ }
+
+ public URL getUrl() {
+ return server.getUrl();
+ }
+
+ public ChannelHandler getChannelHandler() {
+ return server.getChannelHandler();
+ }
+
+ public void reset(URL url) {
+ server.reset(url);
+ try {
+ if (url.hasParameter(Constants.HEARTBEAT_KEY)
+ || url.hasParameter(Constants.HEARTBEAT_TIMEOUT_KEY)) {
+ int h = url.getParameter(Constants.HEARTBEAT_KEY, heartbeat);
+ int t = url.getParameter(Constants.HEARTBEAT_TIMEOUT_KEY, h * 3);
+ if (t < h * 2) {
+ throw new IllegalStateException("heartbeatTimeout < heartbeatInterval * 2");
+ }
+ if (h != heartbeat || t != heartbeatTimeout) {
+ heartbeat = h;
+ heartbeatTimeout = t;
+ startHeatbeatTimer();
+ }
+ }
+ } catch (Throwable t) {
+ logger.error(t.getMessage(), t);
+ }
+ }
+
+ @Deprecated
+ public void reset(com.alibaba.dubbo.common.Parameters parameters){
+ reset(getUrl().addParameters(parameters.getParameters()));
+ }
+
+ public void send(Object message) throws RemotingException {
+ if (closed) {
+ throw new RemotingException(this.getLocalAddress(), null, "Failed to send message " + message + ", cause: The server " + getLocalAddress() + " is closed!");
+ }
+ server.send(message);
+ }
+
+ public void send(Object message, boolean sent) throws RemotingException {
+ if (closed) {
+ throw new RemotingException(this.getLocalAddress(), null, "Failed to send message " + message + ", cause: The server " + getLocalAddress() + " is closed!");
+ }
+ server.send(message, sent);
+ }
+
+ private void startHeatbeatTimer() {
+ try {
+ ScheduledFuture<?> timer = heatbeatTimer;
+ if (timer != null && ! timer.isCancelled()) {
+ timer.cancel(true);
+ }
+ } catch (Throwable t) {
+ logger.warn(t.getMessage(), t);
+ }
+ if (heartbeat > 0) {
+ heatbeatTimer = scheduled.scheduleWithFixedDelay(new HeartBeatTask(), heartbeat, heartbeat,
+ TimeUnit.MILLISECONDS);
+ }
+ }
+
+ private class HeartBeatTask implements Runnable {
+ public void run() {
+ try {
+ long now = System.currentTimeMillis();
+ for (Channel channel : getChannels()) {
+ try {
+ Long lastRead = (Long) channel.getAttribute(HeaderExchangeHandler.KEY_READ_TIMESTAMP);
+ Long lastWrite = (Long) channel.getAttribute(HeaderExchangeHandler.KEY_WRITE_TIMESTAMP);
+ if ((lastRead != null && now - lastRead > heartbeat)
+ || (lastWrite != null && now - lastWrite > heartbeat)) {
+ Request req = new Request();
+ req.setVersion("2.0.0");
+ req.setTwoWay(true);
+ req.setHeartbeat(true);
+ channel.send(req);
+ if (logger.isDebugEnabled()) {
+ logger.debug("Send heartbeat to client " + channel.getRemoteAddress() + ".");
+ }
+ }
+ if (lastRead != null && now - lastRead > heartbeatTimeout) {
+ logger.warn("Close remote client " + channel.getRemoteAddress()
+ + ", because heartbeat read idle time out.");
+ channel.close();
+ }
+ } catch (Throwable t) {
+ logger.warn("Exception when heartbeat to client " + (InetSocketAddress) channel.getRemoteAddress(), t);
+ }
+ }
+ } catch (Throwable t) {
+ logger.info("Exception when heartbeat to clients: ", t);
+ }
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/exchange/support/header/HeaderExchanger.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/exchange/support/header/HeaderExchanger.java
new file mode 100644
index 0000000..c1506ad
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/exchange/support/header/HeaderExchanger.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting.exchange.support.header;
+
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.remoting.RemotingException;
+import com.alibaba.dubbo.remoting.Transporters;
+import com.alibaba.dubbo.remoting.exchange.ExchangeClient;
+import com.alibaba.dubbo.remoting.exchange.ExchangeHandler;
+import com.alibaba.dubbo.remoting.exchange.ExchangeServer;
+import com.alibaba.dubbo.remoting.exchange.Exchanger;
+
+/**
+ * DefaultMessenger
+ *
+ * @author william.liangf
+ */
+@Extension(HeaderExchanger.NAME)
+public class HeaderExchanger implements Exchanger {
+
+ public static final String NAME = "header";
+
+ public ExchangeClient connect(URL url, ExchangeHandler handler) throws RemotingException {
+ return new HeaderExchangeClient(Transporters.connect(url, new HeaderExchangeHandler(handler)));
+ }
+
+ public ExchangeServer bind(URL url, ExchangeHandler handler) throws RemotingException {
+ return new HeaderExchangeServer(Transporters.bind(url, new HeaderExchangeHandler(handler)));
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/p2p/Group.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/p2p/Group.java
new file mode 100644
index 0000000..60d0031
--- /dev/null
+++ b/dubbo-remoting/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/src/main/java/com/alibaba/dubbo/remoting/p2p/Networker.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/p2p/Networker.java
new file mode 100644
index 0000000..c7fafe1
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/p2p/Networker.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting.p2p;
+
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.remoting.RemotingException;
+
+/**
+ * Networker. (SPI, Singleton, ThreadSafe)
+ *
+ * <a href="http://en.wikipedia.org/wiki/Peer-to-peer">Peer-to-peer</a>
+ *
+ * @author william.liangf
+ */
+@Extension
+public interface Networker {
+
+ /**
+ * lookup group.
+ *
+ * @param url group url
+ * @return group.
+ */
+ Group lookup(URL url) throws RemotingException;
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/p2p/Networkers.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/p2p/Networkers.java
new file mode 100644
index 0000000..46c1ce6
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/p2p/Networkers.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting.p2p;
+
+import com.alibaba.dubbo.common.ExtensionLoader;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.remoting.ChannelHandler;
+import com.alibaba.dubbo.remoting.RemotingException;
+
+/**
+ * Networkers. (API, Static, ThreadSafe)
+ *
+ * <a href="http://en.wikipedia.org/wiki/Peer-to-peer">Peer-to-peer</a>
+ *
+ * @author william.liangf
+ */
+public class Networkers {
+
+ public static Peer join(String group, String peer, ChannelHandler handler) throws RemotingException {
+ return join(URL.valueOf(group), URL.valueOf(peer), handler);
+ }
+
+ public static Peer join(URL group, URL peer, ChannelHandler handler) throws RemotingException {
+ return lookup(group).join(peer, handler);
+ }
+
+ public static Group lookup(String group) throws RemotingException {
+ return lookup(URL.valueOf(group));
+ }
+
+ public static Group lookup(URL group) throws RemotingException {
+ Networker networker = ExtensionLoader.getExtensionLoader(Networker.class).getExtension(group.getProtocol());
+ return networker.lookup(group);
+ }
+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/p2p/Peer.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/p2p/Peer.java
new file mode 100644
index 0000000..5f51174
--- /dev/null
+++ b/dubbo-remoting/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/src/main/java/com/alibaba/dubbo/remoting/p2p/exchange/ExchangeGroup.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/p2p/exchange/ExchangeGroup.java
new file mode 100644
index 0000000..a72587c
--- /dev/null
+++ b/dubbo-remoting/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/src/main/java/com/alibaba/dubbo/remoting/p2p/exchange/ExchangeNetworker.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/p2p/exchange/ExchangeNetworker.java
new file mode 100644
index 0000000..8437119
--- /dev/null
+++ b/dubbo-remoting/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/src/main/java/com/alibaba/dubbo/remoting/p2p/exchange/ExchangeNetworkers.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/p2p/exchange/ExchangeNetworkers.java
new file mode 100644
index 0000000..8627608
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/p2p/exchange/ExchangeNetworkers.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting.p2p.exchange;
+
+import com.alibaba.dubbo.common.ExtensionLoader;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.remoting.RemotingException;
+import com.alibaba.dubbo.remoting.exchange.ExchangeHandler;
+
+/**
+ * Networkers
+ *
+ * @author william.liangf
+ */
+public class ExchangeNetworkers {
+
+ public static ExchangePeer join(String group, String peer, ExchangeHandler handler) throws RemotingException {
+ return join(URL.valueOf(group), URL.valueOf(peer), handler);
+ }
+
+ public static ExchangePeer join(URL group, URL peer, ExchangeHandler handler) throws RemotingException {
+ return lookup(group).join(peer, handler);
+ }
+
+ public static ExchangeGroup lookup(String group) throws RemotingException {
+ return lookup(URL.valueOf(group));
+ }
+
+ public static ExchangeGroup lookup(URL group) throws RemotingException {
+ ExchangeNetworker networker = ExtensionLoader.getExtensionLoader(ExchangeNetworker.class).getExtension(group.getProtocol());
+ return networker.lookup(group);
+ }
+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/p2p/exchange/ExchangePeer.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/p2p/exchange/ExchangePeer.java
new file mode 100644
index 0000000..9a68b48
--- /dev/null
+++ b/dubbo-remoting/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/src/main/java/com/alibaba/dubbo/remoting/p2p/exchange/support/AbstractExchangeGroup.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/p2p/exchange/support/AbstractExchangeGroup.java
new file mode 100644
index 0000000..d79ecd0
--- /dev/null
+++ b/dubbo-remoting/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/src/main/java/com/alibaba/dubbo/remoting/p2p/exchange/support/ExchangeServerPeer.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/p2p/exchange/support/ExchangeServerPeer.java
new file mode 100644
index 0000000..f7d3bad
--- /dev/null
+++ b/dubbo-remoting/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.getHost().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/src/main/java/com/alibaba/dubbo/remoting/p2p/exchange/support/FileExchangeGroup.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/p2p/exchange/support/FileExchangeGroup.java
new file mode 100644
index 0000000..8375534
--- /dev/null
+++ b/dubbo-remoting/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/src/main/java/com/alibaba/dubbo/remoting/p2p/exchange/support/FileExchangeNetworker.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/p2p/exchange/support/FileExchangeNetworker.java
new file mode 100644
index 0000000..a5835ca
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/p2p/exchange/support/FileExchangeNetworker.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting.p2p.exchange.support;
+
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.remoting.RemotingException;
+import com.alibaba.dubbo.remoting.p2p.exchange.ExchangeGroup;
+import com.alibaba.dubbo.remoting.p2p.exchange.ExchangeNetworker;
+
+/**
+ * FileNetworker
+ *
+ * @author william.liangf
+ */
+@Extension("file")
+public class FileExchangeNetworker implements ExchangeNetworker {
+
+ public ExchangeGroup lookup(URL url) throws RemotingException {
+ return new FileExchangeGroup(url);
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/p2p/exchange/support/MulticastExchangeGroup.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/p2p/exchange/support/MulticastExchangeGroup.java
new file mode 100644
index 0000000..3850261
--- /dev/null
+++ b/dubbo-remoting/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/src/main/java/com/alibaba/dubbo/remoting/p2p/exchange/support/MulticastExchangeNetworker.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/p2p/exchange/support/MulticastExchangeNetworker.java
new file mode 100644
index 0000000..3cd808e
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/p2p/exchange/support/MulticastExchangeNetworker.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting.p2p.exchange.support;
+
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.remoting.RemotingException;
+import com.alibaba.dubbo.remoting.p2p.exchange.ExchangeGroup;
+import com.alibaba.dubbo.remoting.p2p.exchange.ExchangeNetworker;
+
+/**
+ * MulticastNetworker
+ *
+ * @author william.liangf
+ */
+@Extension("multicast")
+public class MulticastExchangeNetworker implements ExchangeNetworker {
+
+ public ExchangeGroup lookup(URL url) throws RemotingException {
+ return new MulticastExchangeGroup(url);
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/p2p/support/AbstractGroup.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/p2p/support/AbstractGroup.java
new file mode 100644
index 0000000..2fff7ba
--- /dev/null
+++ b/dubbo-remoting/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/src/main/java/com/alibaba/dubbo/remoting/p2p/support/FileGroup.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/p2p/support/FileGroup.java
new file mode 100644
index 0000000..1ad645b
--- /dev/null
+++ b/dubbo-remoting/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/src/main/java/com/alibaba/dubbo/remoting/p2p/support/FileNetworker.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/p2p/support/FileNetworker.java
new file mode 100644
index 0000000..6998a4a
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/p2p/support/FileNetworker.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting.p2p.support;
+
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.remoting.RemotingException;
+import com.alibaba.dubbo.remoting.p2p.Group;
+import com.alibaba.dubbo.remoting.p2p.Networker;
+
+/**
+ * FileNetworker
+ *
+ * @author william.liangf
+ */
+@Extension("file")
+public class FileNetworker implements Networker {
+
+ public Group lookup(URL url) throws RemotingException {
+ return new FileGroup(url);
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/p2p/support/MulticastGroup.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/p2p/support/MulticastGroup.java
new file mode 100644
index 0000000..beca3ba
--- /dev/null
+++ b/dubbo-remoting/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/src/main/java/com/alibaba/dubbo/remoting/p2p/support/MulticastNetworker.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/p2p/support/MulticastNetworker.java
new file mode 100644
index 0000000..caf2b3f
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/p2p/support/MulticastNetworker.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting.p2p.support;
+
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.remoting.RemotingException;
+import com.alibaba.dubbo.remoting.p2p.Group;
+import com.alibaba.dubbo.remoting.p2p.Networker;
+
+/**
+ * MulticastNetworker
+ *
+ * @author william.liangf
+ */
+@Extension("multicast")
+public class MulticastNetworker implements Networker {
+
+ public Group lookup(URL url) throws RemotingException {
+ return new MulticastGroup(url);
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/p2p/support/ServerPeer.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/p2p/support/ServerPeer.java
new file mode 100644
index 0000000..16b3bef
--- /dev/null
+++ b/dubbo-remoting/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.getHost().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/src/main/java/com/alibaba/dubbo/remoting/telnet/TelnetHandler.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/telnet/TelnetHandler.java
new file mode 100644
index 0000000..cca6c56
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/telnet/TelnetHandler.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting.telnet;
+
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.remoting.Channel;
+import com.alibaba.dubbo.remoting.RemotingException;
+
+/**
+ * TelnetHandler
+ *
+ * @author william.liangf
+ */
+@Extension
+public interface TelnetHandler {
+
+ /**
+ * telnet.
+ *
+ * @param channel
+ * @param message
+ */
+ String telnet(Channel channel, String message) throws RemotingException;
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/telnet/codec/TelnetCodec.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/telnet/codec/TelnetCodec.java
new file mode 100644
index 0000000..59e870a
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/telnet/codec/TelnetCodec.java
@@ -0,0 +1,280 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting.telnet.codec;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.UnsupportedEncodingException;
+import java.net.InetSocketAddress;
+import java.nio.charset.Charset;
+import java.util.Arrays;
+import java.util.LinkedList;
+import java.util.List;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.logger.Logger;
+import com.alibaba.dubbo.common.logger.LoggerFactory;
+import com.alibaba.dubbo.common.utils.NetUtils;
+import com.alibaba.dubbo.common.utils.StringUtils;
+import com.alibaba.dubbo.remoting.Channel;
+import com.alibaba.dubbo.remoting.RemotingException;
+import com.alibaba.dubbo.remoting.transport.codec.TransportCodec;
+
+/**
+ * TelnetCodec
+ *
+ * @author heyman
+ * @author william.liangf
+ * @author chao.liuc
+ */
+@Extension("telnet")
+public class TelnetCodec extends TransportCodec {
+
+ private static final Logger logger = LoggerFactory.getLogger(TelnetCodec.class);
+
+ private static final String HISTORY_LIST_KEY = "telnet.history.list";
+
+ private static final String HISTORY_INDEX_KEY = "telnet.history.index";
+
+ private static final byte[] UP = new byte[] {27, 91, 65};
+
+ private static final byte[] DOWN = new byte[] {27, 91, 66};
+
+ private static final List<?> ENTER = Arrays.asList(new Object[] { new byte[] { '\r', '\n' } /* Windows Enter */, new byte[] { '\n' } /* Linux Enter */ });
+
+ private static final List<?> EXIT = Arrays.asList(new Object[] { new byte[] { 3 } /* Windows Ctrl+C */, new byte[] { -1, -12, -1, -3, 6 } /* Linux Ctrl+C */, new byte[] { -1, -19, -1, -3, 6 } /* Linux Pause */ });
+
+ public void encode(Channel channel, OutputStream output, Object message) throws IOException {
+ if (message instanceof String) {
+ if (isClientSide(channel)) {
+ message = message + "\r\n";
+ }
+ byte[] msgData = ((String) message).getBytes(getCharset(channel).name());
+ output.write(msgData);
+ output.flush();
+ } else {
+ super.encode(channel, output, message);
+ }
+ }
+
+ public Object decode(Channel channel, InputStream is) throws IOException {
+ int readable = is.available();
+ byte[] message = new byte[readable];
+ is.read(message);
+ return decode(channel, is, readable, message);
+ }
+
+ @SuppressWarnings("unchecked")
+ protected Object decode(Channel channel, InputStream is, int readable, byte[] message) throws IOException {
+ if (isClientSide(channel)) {
+ return toString(message, getCharset(channel));
+ }
+ checkPayload(channel, readable);
+ if (message == null || message.length == 0) {
+ return NEED_MORE_INPUT;
+ }
+
+ if (message[message.length - 1] == '\b') { // Windows backspace echo
+ try {
+ boolean doublechar = message.length >= 3 && message[message.length - 3] < 0; // double byte char
+ channel.send(new String(doublechar ? new byte[] {32, 32, 8, 8} : new byte[] {32, 8}, getCharset(channel).name()));
+ } catch (RemotingException e) {
+ throw new IOException(StringUtils.toString(e));
+ }
+ return NEED_MORE_INPUT;
+ }
+
+ for (Object command : EXIT) {
+ if (isEquals(message, (byte[]) command)) {
+ if (logger.isInfoEnabled()) {
+ logger.info(new Exception("Close channel " + channel + " on exit command: " + Arrays.toString((byte[])command)));
+ }
+ channel.close();
+ return null;
+ }
+ }
+
+ boolean up = endsWith(message, UP);
+ boolean down = endsWith(message, DOWN);
+ if (up || down) {
+ LinkedList<String> history = (LinkedList<String>) channel.getAttribute(HISTORY_LIST_KEY);
+ if (history == null || history.size() == 0) {
+ return NEED_MORE_INPUT;
+ }
+ byte[] input = new byte[message.length - (up? UP.length:DOWN.length) ];
+ System.arraycopy(message, 0, input, 0, input.length);
+ Integer currentIndex = (Integer) channel.getAttribute(HISTORY_INDEX_KEY);
+ Integer targetIndex = currentIndex;
+ if (currentIndex == null) {
+ currentIndex = history.size() - 1;
+ //first up need point to the last history item
+ targetIndex = history.size();
+ }
+ if (up) {
+ if (targetIndex > 0 ){
+ targetIndex = Math.min(targetIndex - 1, history.size() -1);
+ }
+ } else {
+ if (targetIndex < history.size() - 1){
+ targetIndex = Math.min(targetIndex + 1, history.size() -1);
+ }
+ //else current input?? message-down
+ }
+ String value = history.get(targetIndex);
+ String out = getbackSpaceString(toString(input, getCharset(channel)));
+ value = out + value;
+ try {
+ channel.send(value);
+ } catch (RemotingException e) {
+ throw new IOException(StringUtils.toString(e));
+ }
+ channel.setAttribute(HISTORY_INDEX_KEY, targetIndex);
+ return NEED_MORE_INPUT;
+ } else {
+ 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);
+ if (history == null ) {
+ //telnet ignore concurrent
+ history = new LinkedList<String>();
+ channel.setAttribute(HISTORY_LIST_KEY, history);
+ }
+ String result = toString(message, getCharset(channel));
+ history.add(result);
+ return result;
+ }
+
+
+ }
+ private static String getbackSpaceString(String input){
+ StringBuffer buf = new StringBuffer(input.length()*3);
+ for (int i = 0; i < input.length(); i ++) {
+ buf.append("\b");
+ }
+ for (int i = 0; i < input.length(); i ++) {
+ buf.append(" ");
+ }
+ for (int i = 0; i < input.length(); i ++) {
+ buf.append("\b");
+ }
+ buf.append(input);
+ return buf.toString();
+ }
+
+ private static boolean isClientSide(Channel channel) {
+ InetSocketAddress address = channel.getRemoteAddress();
+ URL url = channel.getUrl();
+ return url.getPort() == address.getPort() &&
+ NetUtils.filterLocalHost(url.getHost())
+ .equals(NetUtils.filterLocalHost(address.getAddress().getHostAddress()));
+ }
+
+ private static Charset getCharset(Channel channel) {
+ if (channel != null) {
+ Object attribute = channel.getAttribute(Constants.CHARSET_KEY);
+ if (attribute instanceof String) {
+ try {
+ return Charset.forName((String) attribute);
+ } catch (Throwable t) {
+ logger.warn(t.getMessage(), t);
+ }
+ } else if (attribute instanceof Charset) {
+ return (Charset) attribute;
+ }
+ URL url = channel.getUrl();
+ if (url != null) {
+ String parameter = url.getParameter(Constants.CHARSET_KEY);
+ if (parameter != null && parameter.length() > 0) {
+ try {
+ return Charset.forName(parameter);
+ } catch (Throwable t) {
+ logger.warn(t.getMessage(), t);
+ }
+ }
+ }
+ }
+ try {
+ return Charset.forName("GBK");
+ } catch (Throwable t) {
+ logger.warn(t.getMessage(), t);
+ }
+ return Charset.defaultCharset();
+ }
+
+ private static String toString(byte[] message, Charset charset) throws UnsupportedEncodingException {
+ byte[] copy = new byte[message.length];
+ int index = 0;
+ for (int i = 0; i < message.length; i ++) {
+ byte b = message[i] ;
+ if (b == '\b') { // backspace
+ if (index > 0) {
+ index --;
+ }
+ if (i > 2 && message[i - 2] < 0) { // double byte char
+ if (index > 0) {
+ index --;
+ }
+ }
+ } else if (b == 27) { // escape
+ if (i < message.length - 4 && message[i + 4] == 126) {
+ i = i + 4;
+ } else if (i < message.length - 3 && message[i + 3] == 126) {
+ i = i + 3;
+ } else if (i < message.length - 2) {
+ i = i + 2;
+ }
+ } else if (b == -1 && i < message.length - 2
+ && (message[i + 1] == -3 || message[i + 1] == -5)) { // handshake
+ i = i + 2;
+ } else {
+ copy[index ++] = message[i];
+ }
+ }
+ if (index == 0) {
+ return "";
+ }
+ return new String(copy, 0, index, charset.name()).trim();
+ }
+
+ private static boolean isEquals(byte[] message, byte[] command) throws IOException {
+ return message.length == command.length && endsWith(message, command);
+ }
+
+ private static boolean endsWith(byte[] message, byte[] command) throws IOException {
+ if (message.length < command.length) {
+ return false;
+ }
+ int offset = message.length - command.length;
+ for (int i = command.length - 1; i >= 0 ; i --) {
+ if (message[offset + i] != command[i]) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/telnet/support/Help.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/telnet/support/Help.java
new file mode 100644
index 0000000..8b6c1a9
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/telnet/support/Help.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting.telnet.support;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Help
+ *
+ * @author william.liangf
+ */
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.TYPE})
+public @interface Help {
+
+ String parameter() default "";
+
+ String summary();
+
+ String detail() default "";
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/telnet/support/TelnetHandlerAdapter.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/telnet/support/TelnetHandlerAdapter.java
new file mode 100644
index 0000000..a80a921
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/telnet/support/TelnetHandlerAdapter.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.remoting.telnet.support;
+
+import java.util.List;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.ExtensionLoader;
+import com.alibaba.dubbo.common.utils.ConfigUtils;
+import com.alibaba.dubbo.remoting.Channel;
+import com.alibaba.dubbo.remoting.RemotingException;
+import com.alibaba.dubbo.remoting.telnet.TelnetHandler;
+import com.alibaba.dubbo.remoting.transport.ChannelHandlerAdapter;
+
+/**
+ * TelnetHandlerDispather
+ *
+ * @author william.liangf
+ */
+public class TelnetHandlerAdapter extends ChannelHandlerAdapter implements TelnetHandler {
+
+ public String telnet(Channel channel, String message) throws RemotingException {
+ String telnet = channel.getUrl().getParameter("telnet");
+ String prompt = channel.getUrl().getParameter("prompt");
+ List<String> commands = ConfigUtils.mergeValues(TelnetHandler.class, telnet, Constants.DEFAULT_TELNET_COMMANDS);
+ StringBuilder buf = new StringBuilder();
+ if (commands != null && commands.size() > 0) {
+ message = message.trim();
+ String command;
+ if (message.length() > 0) {
+ int i = message.indexOf(' ');
+ if (i > 0) {
+ command = message.substring(0, i).trim();
+ message = message.substring(i + 1).trim();
+ } else {
+ command = message;
+ message = "";
+ }
+ } else {
+ command = "";
+ }
+ if (commands.contains(command)) {
+ TelnetHandler handler = ExtensionLoader.getExtensionLoader(TelnetHandler.class).getExtension(command);
+ try {
+ String result = handler.telnet(channel, message);
+ if (result != null) {
+ buf.append(result);
+ }
+ } catch (Throwable t) {
+ buf.append(t.getMessage());
+ }
+ } else if (command.length() > 0) {
+ buf.append("Unsupported command: ");
+ buf.append(command);
+ }
+ if (buf.length() > 0) {
+ buf.append("\r\n");
+ }
+ }
+ if (prompt != null && prompt.length() > 0) {
+ buf.append(prompt);
+ buf.append(">");
+ }
+ return buf.toString();
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/telnet/support/TelnetUtils.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/telnet/support/TelnetUtils.java
new file mode 100644
index 0000000..02aaa63
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/telnet/support/TelnetUtils.java
@@ -0,0 +1,160 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting.telnet.support;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * TelnetUtils
+ *
+ * @author william.liangf
+ */
+public class TelnetUtils {
+
+ public static String toList(List<List<String>> table) {
+ int[] widths = new int[table.get(0).size()];
+ for (int j = 0; j < widths.length; j ++) {
+ for (List<String> row : table) {
+ widths[j] = Math.max(widths[j], row.get(j).length());
+ }
+ }
+ StringBuilder buf = new StringBuilder();
+ for (List<String> row : table) {
+ if (buf.length() > 0) {
+ buf.append("\r\n");
+ }
+ for (int j = 0; j < widths.length; j ++) {
+ if (j > 0) {
+ buf.append(" - ");
+ }
+ String value = row.get(j);
+ buf.append(value);
+ if (j < widths.length - 1) {
+ int pad = widths[j] - value.length();
+ if (pad > 0) {
+ for (int k = 0; k < pad; k ++) {
+ buf.append(" ");
+ }
+ }
+ }
+ }
+ }
+ return buf.toString();
+ }
+
+ public static String toTable(String[] header, List<List<String>> table) {
+ return toTable(Arrays.asList(header), table);
+ }
+
+ public static String toTable(List<String> header, List<List<String>> table) {
+ int totalWidth = 0 ;
+ int[] widths = new int[header.size()];
+ int maxwidth = 70;
+ int maxcountbefore = 0;
+ for (int j = 0; j < widths.length; j ++) {
+ widths[j] = Math.max(widths[j], header.get(j).length());
+ }
+ for (List<String> row : table) {
+ int countbefore = 0;
+ for (int j = 0; j < widths.length; j ++) {
+ widths[j] = Math.max(widths[j], row.get(j).length() );
+ totalWidth = (totalWidth + widths[j])> maxwidth ? maxwidth : (totalWidth + widths[j]);
+ if (j<widths.length -1){
+ countbefore = countbefore + widths[j];
+ }
+ }
+ maxcountbefore = Math.max(countbefore, maxcountbefore);
+ }
+ widths[widths.length-1] = Math.min(widths[widths.length-1], maxwidth-maxcountbefore);
+ StringBuilder buf = new StringBuilder();
+ //line
+ buf.append("+");
+ for (int j = 0; j < widths.length; j ++) {
+ for (int k = 0; k < widths[j] + 2; k ++) {
+ buf.append("-");
+ }
+ buf.append("+");
+ }
+ buf.append("\r\n");
+ //header
+ buf.append("|");
+ for (int j = 0; j < widths.length; j ++) {
+ String cell = header.get(j);
+ buf.append(" ");
+ buf.append(cell);
+ int pad = widths[j] - cell.length();
+ if (pad > 0) {
+ for (int k = 0; k < pad; k ++) {
+ buf.append(" ");
+ }
+ }
+ buf.append(" |");
+ }
+ buf.append("\r\n");
+ //line
+ buf.append("+");
+ for (int j = 0; j < widths.length; j ++) {
+ for (int k = 0; k < widths[j] + 2; k ++) {
+ buf.append("-");
+ }
+ buf.append("+");
+ }
+ buf.append("\r\n");
+ //content
+ for (List<String> row : table) {
+ StringBuffer rowbuf = new StringBuffer();
+ rowbuf.append("|");
+ for (int j = 0; j < widths.length; j ++) {
+ String cell = row.get(j);
+ rowbuf.append(" ");
+ int remaing = cell.length();
+ while (remaing > 0){
+
+ if (rowbuf.length() >= totalWidth ){
+ buf.append(rowbuf.toString());
+ rowbuf = new StringBuffer();
+// for(int m = 0;m < maxcountbefore && maxcountbefore < totalWidth ; m++){
+// rowbuf.append(" ");
+// }
+ }
+
+ rowbuf.append(cell.substring(cell.length()-remaing, cell.length()-remaing +1));
+ remaing -- ;
+ }
+ int pad = widths[j] - cell.length();
+ if (pad > 0) {
+ for (int k = 0; k < pad; k ++) {
+ rowbuf.append(" ");
+ }
+ }
+ rowbuf.append(" |");
+ }
+ buf.append(rowbuf).append("\r\n");
+ }
+ //line
+ buf.append("+");
+ for (int j = 0; j < widths.length; j ++) {
+ for (int k = 0; k < widths[j] + 2; k ++) {
+ buf.append("-");
+ }
+ buf.append("+");
+ }
+ buf.append("\r\n");
+ return buf.toString();
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/telnet/support/command/ClearTelnetHandler.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/telnet/support/command/ClearTelnetHandler.java
new file mode 100644
index 0000000..a87d050
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/telnet/support/command/ClearTelnetHandler.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting.telnet.support.command;
+
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.common.utils.StringUtils;
+import com.alibaba.dubbo.remoting.Channel;
+import com.alibaba.dubbo.remoting.telnet.TelnetHandler;
+import com.alibaba.dubbo.remoting.telnet.support.Help;
+
+/**
+ * ClearTelnetHandler
+ *
+ * @author william.liangf
+ */
+@Help(parameter = "[lines]", summary = "Clear screen.", detail = "Clear screen.")
+@Extension("clear")
+public class ClearTelnetHandler implements TelnetHandler {
+
+ public String telnet(Channel channel, String message) {
+ int lines = 100;
+ if (message.length() > 0) {
+ if (! StringUtils.isInteger(message)) {
+ return "Illegal lines " + message + ", must be integer.";
+ }
+ lines = Integer.parseInt(message);
+ }
+ StringBuilder buf = new StringBuilder();
+ for (int i = 0; i < lines; i ++) {
+ buf.append("\r\n");
+ }
+ return buf.toString();
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/telnet/support/command/ExitTelnetHandler.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/telnet/support/command/ExitTelnetHandler.java
new file mode 100644
index 0000000..ce933e3
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/telnet/support/command/ExitTelnetHandler.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting.telnet.support.command;
+
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.remoting.Channel;
+import com.alibaba.dubbo.remoting.telnet.TelnetHandler;
+import com.alibaba.dubbo.remoting.telnet.support.Help;
+
+/**
+ * ExitTelnetHandler
+ *
+ * @author william.liangf
+ */
+@Help(parameter = "", summary = "Exit the telnet.", detail = "Exit the telnet.")
+@Extension("exit")
+public class ExitTelnetHandler implements TelnetHandler {
+
+ public String telnet(Channel channel, String message) {
+ channel.close();
+ return null;
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/telnet/support/command/HelpTelnetHandler.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/telnet/support/command/HelpTelnetHandler.java
new file mode 100644
index 0000000..4903eeb
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/telnet/support/command/HelpTelnetHandler.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting.telnet.support.command;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.common.ExtensionLoader;
+import com.alibaba.dubbo.remoting.Channel;
+import com.alibaba.dubbo.remoting.telnet.TelnetHandler;
+import com.alibaba.dubbo.remoting.telnet.support.Help;
+import com.alibaba.dubbo.remoting.telnet.support.TelnetUtils;
+
+/**
+ * HelpTelnetHandler
+ *
+ * @author william.liangf
+ */
+@Help(parameter = "[command]", summary = "Show help.", detail = "Show help.")
+@Extension("help")
+public class HelpTelnetHandler implements TelnetHandler {
+
+ public String telnet(Channel channel, String message) {
+ if (message.length() > 0) {
+ if (! ExtensionLoader.getExtensionLoader(TelnetHandler.class).hasExtension(message)) {
+ return "No such command " + message;
+ }
+ TelnetHandler handler = ExtensionLoader.getExtensionLoader(TelnetHandler.class).getExtension(message);
+ Help help = handler.getClass().getAnnotation(Help.class);
+ StringBuilder buf = new StringBuilder();
+ buf.append("Command:\r\n ");
+ buf.append(message + " " + help.parameter().replace("\r\n", " ").replace("\n", " "));
+ buf.append("\r\nSummary:\r\n ");
+ buf.append(help.summary().replace("\r\n", " ").replace("\n", " "));
+ buf.append("\r\nDetail:\r\n ");
+ buf.append(help.detail().replace("\r\n", " \r\n").replace("\n", " \n"));
+ return buf.toString();
+ } else {
+ List<List<String>> table = new ArrayList<List<String>>();
+ String telnet = channel.getUrl().getParameter("telnet");
+ if (telnet != null && telnet.length() > 0) {
+ for (String cmd : Constants.COMMA_SPLIT_PATTERN.split(telnet)) {
+ TelnetHandler handler = ExtensionLoader.getExtensionLoader(TelnetHandler.class).getExtension(cmd);
+ Help help = handler.getClass().getAnnotation(Help.class);
+ List<String> row = new ArrayList<String>();
+ String parameter = " " + cmd + " " + (help != null ? help.parameter().replace("\r\n", " ").replace("\n", " ") : "");
+ row.add(parameter.length() > 50 ? parameter.substring(0, 50) + "..." : parameter);
+ String summary = help != null ? help.summary().replace("\r\n", " ").replace("\n", " ") : "";
+ row.add(summary.length() > 50 ? summary.substring(0, 50) + "..." : summary);
+ table.add(row);
+ }
+ }
+ return "Please input \"help [command]\" show detail.\r\n" + TelnetUtils.toList(table);
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/telnet/support/command/StatusTelnetHandler.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/telnet/support/command/StatusTelnetHandler.java
new file mode 100644
index 0000000..164db61
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/telnet/support/command/StatusTelnetHandler.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting.telnet.support.command;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.common.ExtensionLoader;
+import com.alibaba.dubbo.common.status.Status;
+import com.alibaba.dubbo.common.status.StatusChecker;
+import com.alibaba.dubbo.common.status.support.StatusUtils;
+import com.alibaba.dubbo.common.utils.ConfigUtils;
+import com.alibaba.dubbo.remoting.Channel;
+import com.alibaba.dubbo.remoting.telnet.TelnetHandler;
+import com.alibaba.dubbo.remoting.telnet.support.Help;
+import com.alibaba.dubbo.remoting.telnet.support.TelnetUtils;
+
+/**
+ * StatusTelnetHandler
+ *
+ * @author william.liangf
+ */
+@Help(parameter = "[-l]", summary = "Show status.", detail = "Show status.")
+@Extension("status")
+public class StatusTelnetHandler implements TelnetHandler {
+
+ public String telnet(Channel channel, String message) {
+ if (message.equals("-l")) {
+ String status = channel.getUrl().getParameter("status");
+ List<String> ss = ConfigUtils.mergeValues(StatusChecker.class, status, Constants.DEFAULT_CHECK_STATUSES);
+ String[] header = new String[] {"resource", "status", "message"};
+ List<List<String>> table = new ArrayList<List<String>>();
+ Map<String, Status> statuses = new HashMap<String, Status>();
+ if (ss != null && ss.size() > 0) {
+ for (String s : ss) {
+ StatusChecker handler = ExtensionLoader.getExtensionLoader(StatusChecker.class).getExtension(s);
+ Status stat;
+ try {
+ stat = handler.check();
+ } catch (Throwable t) {
+ stat = new Status(Status.Level.ERROR, t.getMessage());
+ }
+ statuses.put(s, stat);
+ if (stat.getLevel() != null && stat.getLevel() != Status.Level.UNKNOWN) {
+ List<String> row = new ArrayList<String>();
+ row.add(s);
+ row.add(String.valueOf(stat.getLevel()));
+ row.add(stat.getMessage() == null ? "" : stat.getMessage());
+ table.add(row);
+ }
+ }
+ }
+ Status stat= StatusUtils.getSummaryStatus(statuses);
+ List<String> row = new ArrayList<String>();
+ row.add("summary");
+ row.add(String.valueOf(stat.getLevel()));
+ row.add(stat.getMessage());
+ table.add(row);
+ return TelnetUtils.toTable(header, table);
+ } else if (message.length() > 0) {
+ return "Unsupported parameter " + message + " for status.";
+ }
+ String status = channel.getUrl().getParameter("status");
+ Map<String, Status> statuses = new HashMap<String, Status>();
+ if (status != null && status.length() > 0) {
+ String[] ss = Constants.COMMA_SPLIT_PATTERN.split(status);
+ for (String s : ss) {
+ StatusChecker handler = ExtensionLoader.getExtensionLoader(StatusChecker.class).getExtension(s);
+ Status stat;
+ try {
+ stat = handler.check();
+ } catch (Throwable t) {
+ stat = new Status(Status.Level.ERROR, t.getMessage());
+ }
+ statuses.put(s, stat);
+ }
+ }
+ Status stat= StatusUtils.getSummaryStatus(statuses);
+ return String.valueOf(stat.getLevel());
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/AbstractChannel.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/AbstractChannel.java
new file mode 100644
index 0000000..0bd04ba
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/AbstractChannel.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting.transport;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.remoting.Channel;
+import com.alibaba.dubbo.remoting.ChannelHandler;
+import com.alibaba.dubbo.remoting.RemotingException;
+
+/**
+ * AbstractChannel
+ *
+ * @author william.liangf
+ */
+public abstract class AbstractChannel extends AbstractPeer implements Channel {
+
+ public AbstractChannel(URL url, ChannelHandler handler){
+ super(url, handler);
+ }
+
+ public void send(Object message, boolean sent) throws RemotingException {
+ if (isClosed()) {
+ throw new RemotingException(this, "Failed to send message "
+ + (message == null ? "" : message.getClass().getName()) + ":" + message
+ + ", cause: Channel closed. channel: " + getLocalAddress() + " -> " + getRemoteAddress());
+ }
+ }
+
+ @Override
+ public String toString() {
+ return getLocalAddress() + " -> " + getRemoteAddress();
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/AbstractClient.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/AbstractClient.java
new file mode 100644
index 0000000..4505c1a
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/AbstractClient.java
@@ -0,0 +1,355 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.AtomicInteger;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.Version;
+import com.alibaba.dubbo.common.logger.Logger;
+import com.alibaba.dubbo.common.logger.LoggerFactory;
+import com.alibaba.dubbo.common.utils.ExecutorUtil;
+import com.alibaba.dubbo.common.utils.NamedThreadFactory;
+import com.alibaba.dubbo.common.utils.NetUtils;
+import com.alibaba.dubbo.remoting.Channel;
+import com.alibaba.dubbo.remoting.ChannelHandler;
+import com.alibaba.dubbo.remoting.Client;
+import com.alibaba.dubbo.remoting.RemotingException;
+import com.alibaba.dubbo.remoting.transport.handler.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("client-connect-check-timer", true));
+
+ private volatile ScheduledFuture<?> reconnectExecutorFuture = null;
+
+ protected volatile ExecutorService executor;
+
+ private final boolean send_reconnect ;
+
+ //the last successed connected time
+ private long lastConnectedTime = System.currentTimeMillis();
+
+ private final int 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);
+
+ 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();
+ }
+ }
+
+ /**
+ * init reconnect thread
+ */
+ private synchronized void initConnectStatusCheckCommand(){
+ //reconnect=false to close reconnect
+ int reconnect = getReconnectParam(getUrl());
+ if(reconnect > 0 && reconnectExecutorFuture == null){
+ Runnable connectStatusCheckCommand = new Runnable() {
+ String errorMsg = "Unexpected error occur at client reconnect";
+ public void run() {
+ try {
+ if (! isConnected()) {
+ connect();
+ }
+ } catch (Throwable t) {
+ // wait registry sync provider list
+ if (System.currentTimeMillis() - lastConnectedTime > shutdown_timeout){
+ logger.warn(errorMsg, t);
+ } else {
+ logger.error(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();
+ if (channel == null || ! channel.isConnected()) {
+ throw new RemotingException(this, channel == null ? "channel is null " : (" channel is closed ") +". url:" + getUrl());
+ }
+ channel.send(message, sent);
+ }
+
+ private void connect() throws RemotingException {
+ connectLock.lock();
+ try {
+ if (isConnected()) {
+ return;
+ }
+ initConnectStatusCheckCommand();
+ doConnect();
+ if (! isConnected()) {
+ throw new RemotingException(this, "Failed connect to server " + getRemoteAddress() + " from " + getClass().getSimpleName() + " "
+ + NetUtils.getLocalHost() + " using dubbo version " + Version.getVersion()
+ + ", cause: Connect wait timeout: " + getTimeout() + "ms.");
+ }
+ lastConnectedTime = System.currentTimeMillis();
+ } catch (RemotingException e) {
+ throw e;
+ } catch (Throwable e) {
+ throw new RemotingException(this, "Failed connect to server " + getRemoteAddress() + " from " + getClass().getSimpleName() + " "
+ + NetUtils.getLocalHost() + " using dubbo version " + Version.getVersion()
+ + ", cause: " + e.getMessage(), e);
+ } finally {
+ connectLock.unlock();
+ }
+ }
+
+ public void disconnect() {
+ connectLock.lock();
+ try {
+ destroyConnectStatusCheckCommand();
+ try {
+ Channel channel = getChannel();
+ if (channel != null) {
+ channel.close();
+ }
+ } catch (Throwable e) {
+ logger.warn(e.getMessage(), e);
+ }
+ try {
+ doDisConnect();
+ } catch (Throwable e) {
+ logger.warn(e.getMessage(), e);
+ }
+ } finally {
+ connectLock.unlock();
+ }
+ }
+
+ public void reconnect() throws RemotingException {
+ disconnect();
+ connect();
+ }
+ public void close() {
+ ExecutorUtil.shutdownNow(executor, 100);
+ try {
+ super.close();
+ } catch (Throwable e) {
+ logger.warn(e.getMessage(), e);
+ }
+ disconnect();
+ try {
+ doClose();
+ } catch (Throwable e) {
+ logger.warn(e.getMessage(), e);
+ }
+ }
+
+ public void close(int timeout) {
+ ExecutorUtil.gracefulShutdown(executor ,timeout);
+ close();
+ }
+
+ @Override
+ public String toString() {
+ return getClass().getName() + " [" + getLocalAddress() + " -> " + getRemoteAddress() + "]";
+ }
+
+ /**
+ * Open client.
+ *
+ * @throws Throwable
+ */
+ protected abstract void doOpen() throws Throwable;
+
+ /**
+ * Close client.
+ *
+ * @throws Throwable
+ */
+ protected abstract void doClose() throws Throwable;
+
+ /**
+ * Connect to server.
+ *
+ * @throws Throwable
+ */
+ protected abstract void doConnect() throws Throwable;
+
+ /**
+ * disConnect to server.
+ *
+ * @throws Throwable
+ */
+ protected abstract void doDisConnect() throws Throwable;
+
+ /**
+ * Get the connected channel.
+ *
+ * @return channel
+ */
+ protected abstract Channel getChannel();
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/AbstractCodec.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/AbstractCodec.java
new file mode 100644
index 0000000..a4c8895
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/AbstractCodec.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting.transport;
+
+import java.io.IOException;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.ExtensionLoader;
+import com.alibaba.dubbo.common.serialize.Serialization;
+import com.alibaba.dubbo.remoting.Channel;
+import com.alibaba.dubbo.remoting.Codec;
+
+/**
+ * AbstractCodec
+ *
+ * @author william.liangf
+ */
+public abstract class AbstractCodec implements Codec {
+
+ protected Serialization getSerialization(Channel channel) {
+ Serialization serialization = ExtensionLoader.getExtensionLoader(Serialization.class).getExtension(channel.getUrl().getParameter(Constants.SERIALIZATION_KEY, Constants.DEFAULT_REMOTING_SERIALIZATION));
+ return serialization;
+ }
+
+ protected void checkPayload(Channel channel, long size) throws IOException {
+ int payload = Constants.DEFAULT_PAYLOAD;
+ if (channel != null && channel.getUrl() != null) {
+ payload = channel.getUrl().getPositiveParameter(Constants.PAYLOAD_KEY, Constants.DEFAULT_PAYLOAD);
+ }
+ if (size > payload) {
+ throw new IOException("Data length too large: " + size + ", max payload: " + payload + ", channel: " + channel);
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/AbstractEndpoint.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/AbstractEndpoint.java
new file mode 100644
index 0000000..a220a74
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/AbstractEndpoint.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting.transport;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.ExtensionLoader;
+import com.alibaba.dubbo.common.Resetable;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.logger.Logger;
+import com.alibaba.dubbo.common.logger.LoggerFactory;
+import com.alibaba.dubbo.remoting.ChannelHandler;
+import com.alibaba.dubbo.remoting.Codec;
+
+/**
+ * AbstractEndpoint
+ *
+ * @author william.liangf
+ */
+public abstract class AbstractEndpoint extends AbstractPeer implements Resetable {
+
+ private static final Logger logger = LoggerFactory.getLogger(AbstractEndpoint.class);
+
+ private Codec codec;
+
+ private int timeout;
+
+ private int connectTimeout;
+
+ public AbstractEndpoint(URL url, ChannelHandler handler) {
+ super(url, handler);
+ this.codec = ExtensionLoader.getExtensionLoader(Codec.class).getExtension(url.getParameter(Constants.CODEC_KEY, "telnet"));
+ this.timeout = url.getPositiveParameter(Constants.TIMEOUT_KEY, Constants.DEFAULT_TIMEOUT);
+ this.connectTimeout = url.getPositiveParameter(Constants.CONNECT_TIMEOUT_KEY, timeout);
+ }
+
+ public void reset(URL url) {
+ if (isClosed()) {
+ throw new IllegalStateException("Failed to reset parameters "
+ + url + ", cause: Channel closed. channel: " + getLocalAddress());
+ }
+ try {
+ if (url.hasParameter(Constants.HEARTBEAT_KEY)) {
+ int t = url.getParameter(Constants.TIMEOUT_KEY, 0);
+ if (t > 0) {
+ this.timeout = t;
+ }
+ }
+ } catch (Throwable t) {
+ logger.error(t.getMessage(), t);
+ }
+ try {
+ if (url.hasParameter(Constants.CONNECT_TIMEOUT_KEY)) {
+ int t = url.getParameter(Constants.CONNECT_TIMEOUT_KEY, 0);
+ if (t > 0) {
+ this.connectTimeout = t;
+ }
+ }
+ } catch (Throwable t) {
+ logger.error(t.getMessage(), t);
+ }
+ try {
+ if (url.hasParameter(Constants.CODEC_KEY)) {
+ String c = url.getParameter(Constants.CODEC_KEY);
+ this.codec = ExtensionLoader.getExtensionLoader(Codec.class).getExtension(c);
+ }
+ } catch (Throwable t) {
+ logger.error(t.getMessage(), t);
+ }
+ }
+
+ @Deprecated
+ public void reset(com.alibaba.dubbo.common.Parameters parameters){
+ reset(getUrl().addParameters(parameters.getParameters()));
+ }
+
+ protected Codec getCodec() {
+ return codec;
+ }
+
+ protected int getTimeout() {
+ return timeout;
+ }
+
+ protected int getConnectTimeout() {
+ return connectTimeout;
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/AbstractPeer.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/AbstractPeer.java
new file mode 100644
index 0000000..dc7dda1
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/AbstractPeer.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.transport;
+
+import java.io.IOException;
+
+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.Endpoint;
+import com.alibaba.dubbo.remoting.RemotingException;
+
+/**
+ * AbstractPeer
+ *
+ * @author qian.lei
+ * @author william.liangf
+ */
+public abstract class AbstractPeer implements Endpoint, ChannelHandler {
+
+ private static final Logger logger = LoggerFactory.getLogger(AbstractPeer.class);
+
+ 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() {
+ 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 {
+ if (ex instanceof IOException || ex instanceof RemotingException) {
+ logger.warn("IOException on channel " + ch, ex);
+ } else {
+ logger.error("Exception on channel " + ch, ex);
+ }
+ handler.caught(ch, ex);
+ }
+
+ public ChannelHandler getHandler() {
+ return handler;
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/AbstractServer.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/AbstractServer.java
new file mode 100644
index 0000000..67b6710
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/AbstractServer.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.remoting.transport;
+
+import java.net.InetSocketAddress;
+import java.util.Collection;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.ThreadPoolExecutor;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.ExtensionLoader;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.logger.Logger;
+import com.alibaba.dubbo.common.logger.LoggerFactory;
+import com.alibaba.dubbo.common.utils.ExecutorUtil;
+import com.alibaba.dubbo.common.utils.NetUtils;
+import com.alibaba.dubbo.remoting.Channel;
+import com.alibaba.dubbo.remoting.ChannelHandler;
+import com.alibaba.dubbo.remoting.Codec;
+import com.alibaba.dubbo.remoting.RemotingException;
+import com.alibaba.dubbo.remoting.Server;
+import com.alibaba.dubbo.remoting.transport.handler.WrappedChannelHandler;
+
+/**
+ * AbstractServer
+ *
+ * @author qian.lei
+ * @author ding.lid
+ */
+public abstract class AbstractServer extends AbstractEndpoint implements Server {
+
+ private static final Logger logger = LoggerFactory.getLogger(AbstractServer.class);
+
+ private InetSocketAddress localAddress;
+
+ private InetSocketAddress bindAddress;
+
+ private int accepts;
+
+ private int idleTimeout = 600; //600 seconds
+
+ protected static final String SERVER_THREAD_POOL_NAME ="DubboServerHandler";
+
+ ExecutorService executor;
+
+ public AbstractServer(URL url, ChannelHandler handler) throws RemotingException {
+ super(url, handler);
+ localAddress = getUrl().toInetSocketAddress();
+ String host = url.getParameter(Constants.ANYHOST_KEY, false)
+ || NetUtils.isInvalidLocalHost(getUrl().getHost())
+ ? NetUtils.ANYHOST : getUrl().getHost();
+ bindAddress = new InetSocketAddress(host, getUrl().getPort());
+ this.accepts = url.getParameter(Constants.ACCEPTS_KEY, Constants.DEFAULT_ACCEPTS);
+ this.idleTimeout = url.getParameter(Constants.IDLE_TIMEOUT_KEY, Constants.DEFAULT_IDLE_TIMEOUT);
+ try {
+ doOpen();
+ if (logger.isInfoEnabled()) {
+ logger.info("Start " + getClass().getSimpleName() + " bind " + getBindAddress() + ", export " + getLocalAddress());
+ }
+ } catch (Throwable t) {
+ throw new RemotingException(url.toInetSocketAddress(), null, "Failed to bind " + getClass().getSimpleName()
+ + " on " + getLocalAddress() + ", cause: " + t.getMessage(), t);
+ }
+ if (handler instanceof WrappedChannelHandler ){
+ executor = ((WrappedChannelHandler)handler).getExecutor();
+ }
+ }
+
+ protected abstract void doOpen() throws Throwable;
+
+ protected abstract void doClose() throws Throwable;
+
+ public void reset(URL url) {
+ if (url == null) {
+ return;
+ }
+ try {
+ if (url.hasParameter(Constants.ACCEPTS_KEY)) {
+ int a = url.getParameter(Constants.ACCEPTS_KEY, 0);
+ if (a > 0) {
+ this.accepts = a;
+ }
+ }
+ } catch (Throwable t) {
+ logger.error(t.getMessage(), t);
+ }
+ try {
+ if (url.hasParameter(Constants.IDLE_TIMEOUT_KEY)) {
+ int t = url.getParameter(Constants.IDLE_TIMEOUT_KEY, 0);
+ if (t > 0) {
+ this.idleTimeout = t;
+ }
+ }
+ } catch (Throwable t) {
+ logger.error(t.getMessage(), t);
+ }
+ try {
+ if (url.hasParameter(Constants.THREADS_KEY)
+ && executor instanceof ThreadPoolExecutor && !executor.isShutdown()) {
+ ThreadPoolExecutor threadPoolExecutor = (ThreadPoolExecutor) executor;
+ int threads = url.getParameter(Constants.THREADS_KEY, 0);
+ int max = threadPoolExecutor.getMaximumPoolSize();
+ int core = threadPoolExecutor.getCorePoolSize();
+ if (threads > 0 && (threads != max || threads != core)) {
+ if (threads < core) {
+ threadPoolExecutor.setCorePoolSize(threads);
+ if (core == max) {
+ threadPoolExecutor.setMaximumPoolSize(threads);
+ }
+ } else {
+ threadPoolExecutor.setMaximumPoolSize(threads);
+ if (core == max) {
+ threadPoolExecutor.setCorePoolSize(threads);
+ }
+ }
+ }
+ }
+ } catch (Throwable t) {
+ logger.error(t.getMessage(), t);
+ }
+ super.setUrl(getUrl().addParameters(url.getParameters()));
+ }
+
+ public void send(Object message, boolean sent) throws RemotingException {
+ Collection<Channel> channels = getChannels();
+ for (Channel channel : channels) {
+ channel.send(message, sent);
+ }
+ }
+
+ public void close() {
+ ExecutorUtil.shutdownNow(executor ,100);
+ try {
+ super.close();
+ } catch (Throwable e) {
+ logger.warn(e.getMessage(), e);
+ }
+ try {
+ doClose();
+ } catch (Throwable e) {
+ logger.warn(e.getMessage(), e);
+ }
+ }
+
+ public void close(int timeout) {
+ ExecutorUtil.gracefulShutdown(executor ,timeout);
+ close();
+ }
+
+ public InetSocketAddress getLocalAddress() {
+ return localAddress;
+ }
+
+ public InetSocketAddress getBindAddress() {
+ return bindAddress;
+ }
+
+ public int getAccepts() {
+ return accepts;
+ }
+
+ public int getIdleTimeout() {
+ return idleTimeout;
+ }
+
+ @Override
+ public void connected(Channel ch) throws RemotingException {
+ Collection<Channel> channels = getChannels();
+ if (accepts > 0 && channels.size() > accepts) {
+ logger.error("Close channel " + ch + ", cause: The server " + ch.getLocalAddress() + " connections greater than max config " + accepts);
+ ch.close();
+ return;
+ }
+ super.connected(ch);
+ }
+
+ @Override
+ public void disconnected(Channel ch) throws RemotingException {
+ Collection<Channel> channels = getChannels();
+ if (channels.size() == 0){
+ logger.warn("All clients has discontected from " + ch.getLocalAddress() + ". You can graceful shutdown now.");
+ }
+ super.disconnected(ch);
+ }
+
+ protected Codec getDownstreamCodec() {
+ Codec downstreamCodec = getCodec();
+ String downstreamCodecStr = getUrl().getParameter(Constants.DOWNSTREAM_CODEC_KEY);
+ if(downstreamCodecStr != null ){
+ downstreamCodec = ExtensionLoader.getExtensionLoader(Codec.class).getExtension(downstreamCodecStr);
+ }
+ return downstreamCodec;
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/ChannelDelegate.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/ChannelDelegate.java
new file mode 100644
index 0000000..639f95f
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/ChannelDelegate.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting.transport;
+
+import java.net.InetSocketAddress;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.remoting.Channel;
+import com.alibaba.dubbo.remoting.ChannelHandler;
+import com.alibaba.dubbo.remoting.RemotingException;
+
+/**
+ * ChannelDelegate
+ *
+ * @author william.liangf
+ */
+public class ChannelDelegate implements Channel {
+
+ private transient Channel channel;
+
+ public ChannelDelegate() {
+ }
+
+ public ChannelDelegate(Channel channel) {
+ setChannel(channel);
+ }
+
+ public Channel getChannel() {
+ return channel;
+ }
+
+ public void setChannel(Channel channel) {
+ if (channel == null) {
+ throw new IllegalArgumentException("channel == null");
+ }
+ this.channel = channel;
+ }
+
+ public URL getUrl() {
+ return channel.getUrl();
+ }
+
+ public InetSocketAddress getRemoteAddress() {
+ return channel.getRemoteAddress();
+ }
+
+ public ChannelHandler getChannelHandler() {
+ return channel.getChannelHandler();
+ }
+
+ public boolean isConnected() {
+ return channel.isConnected();
+ }
+
+ public InetSocketAddress getLocalAddress() {
+ return channel.getLocalAddress();
+ }
+
+ public boolean hasAttribute(String key) {
+ return channel.hasAttribute(key);
+ }
+
+ public void send(Object message) throws RemotingException {
+ channel.send(message);
+ }
+
+ public Object getAttribute(String key) {
+ return channel.getAttribute(key);
+ }
+
+ public void setAttribute(String key, Object value) {
+ channel.setAttribute(key, value);
+ }
+
+ public void send(Object message, boolean sent) throws RemotingException {
+ channel.send(message, sent);
+ }
+
+ public void removeAttribute(String key) {
+ channel.removeAttribute(key);
+ }
+
+ public void close() {
+ channel.close();
+ }
+ public void close(int timeout) {
+ channel.close(timeout);
+ }
+
+ public boolean isClosed() {
+ return channel.isClosed();
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/ChannelHandlerAdapter.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/ChannelHandlerAdapter.java
new file mode 100644
index 0000000..f77605e
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/ChannelHandlerAdapter.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting.transport;
+
+import com.alibaba.dubbo.remoting.Channel;
+import com.alibaba.dubbo.remoting.ChannelHandler;
+import com.alibaba.dubbo.remoting.RemotingException;
+
+/**
+ * ChannelHandlerAdapter.
+ *
+ * @author qian.lei
+ */
+public class ChannelHandlerAdapter implements ChannelHandler {
+
+ public void connected(Channel channel) throws RemotingException {
+ }
+
+ public void disconnected(Channel channel) throws RemotingException {
+ }
+
+ public void sent(Channel channel, Object message) throws RemotingException {
+ }
+
+ public void received(Channel channel, Object message) throws RemotingException {
+ }
+
+ public void caught(Channel channel, Throwable exception) throws RemotingException {
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/ChannelHandlerDispatcher.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/ChannelHandlerDispatcher.java
new file mode 100644
index 0000000..76fdd56
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/ChannelHandlerDispatcher.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting.transport;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.concurrent.CopyOnWriteArraySet;
+
+import com.alibaba.dubbo.common.logger.Logger;
+import com.alibaba.dubbo.common.logger.LoggerFactory;
+import com.alibaba.dubbo.remoting.Channel;
+import com.alibaba.dubbo.remoting.ChannelHandler;
+
+/**
+ * ChannelListenerDispatcher
+ *
+ * @author william.liangf
+ */
+public class ChannelHandlerDispatcher implements ChannelHandler {
+
+ private static final Logger logger = LoggerFactory.getLogger(ChannelHandlerDispatcher.class);
+
+ private final Collection<ChannelHandler> channelHandlers = new CopyOnWriteArraySet<ChannelHandler>();
+
+ public ChannelHandlerDispatcher() {
+ }
+
+ public ChannelHandlerDispatcher(ChannelHandler... handlers) {
+ this(handlers == null ? null : Arrays.asList(handlers));
+ }
+
+ public ChannelHandlerDispatcher(Collection<ChannelHandler> handlers) {
+ if (handlers != null && handlers.size() > 0) {
+ this.channelHandlers.addAll(handlers);
+ }
+ }
+
+ public Collection<ChannelHandler> getChannelHandlers() {
+ return channelHandlers;
+ }
+
+ public ChannelHandlerDispatcher addChannelHandler(ChannelHandler handler) {
+ this.channelHandlers.add(handler);
+ return this;
+ }
+
+ public ChannelHandlerDispatcher removeChannelHandler(ChannelHandler handler) {
+ this.channelHandlers.remove(handler);
+ return this;
+ }
+
+ public void connected(Channel channel) {
+ for (ChannelHandler listener : channelHandlers) {
+ try {
+ listener.connected(channel);
+ } catch (Throwable t) {
+ logger.error(t.getMessage(), t);
+ }
+ }
+ }
+
+ public void disconnected(Channel channel) {
+ for (ChannelHandler listener : channelHandlers) {
+ try {
+ listener.disconnected(channel);
+ } catch (Throwable t) {
+ logger.error(t.getMessage(), t);
+ }
+ }
+ }
+
+ public void sent(Channel channel, Object message) {
+ for (ChannelHandler listener : channelHandlers) {
+ try {
+ listener.sent(channel, message);
+ } catch (Throwable t) {
+ logger.error(t.getMessage(), t);
+ }
+ }
+ }
+
+ public void received(Channel channel, Object message) {
+ for (ChannelHandler listener : channelHandlers) {
+ try {
+ listener.received(channel, message);
+ } catch (Throwable t) {
+ logger.error(t.getMessage(), t);
+ }
+ }
+ }
+
+ public void caught(Channel channel, Throwable exception) {
+ for (ChannelHandler listener : channelHandlers) {
+ try {
+ listener.caught(channel, exception);
+ } catch (Throwable t) {
+ logger.error(t.getMessage(), t);
+ }
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/ClientDelegate.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/ClientDelegate.java
new file mode 100644
index 0000000..6bb445d
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/ClientDelegate.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting.transport;
+
+import java.net.InetSocketAddress;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.remoting.ChannelHandler;
+import com.alibaba.dubbo.remoting.Client;
+import com.alibaba.dubbo.remoting.RemotingException;
+
+/**
+ * ClientDelegate
+ *
+ * @author william.liangf
+ */
+public class ClientDelegate implements Client {
+
+ private transient Client client;
+
+ public ClientDelegate() {
+ }
+
+ public ClientDelegate(Client client){
+ setClient(client);
+ }
+
+ public Client getClient() {
+ return client;
+ }
+
+ public void setClient(Client client) {
+ if (client == null) {
+ throw new IllegalArgumentException("client == null");
+ }
+ this.client = client;
+ }
+
+ public void reset(URL url) {
+ client.reset(url);
+ }
+
+ @Deprecated
+ public void reset(com.alibaba.dubbo.common.Parameters parameters){
+ reset(getUrl().addParameters(parameters.getParameters()));
+ }
+
+ public URL getUrl() {
+ return client.getUrl();
+ }
+
+ public InetSocketAddress getRemoteAddress() {
+ return client.getRemoteAddress();
+ }
+
+ public void reconnect() throws RemotingException {
+ client.reconnect();
+ }
+
+ public ChannelHandler getChannelHandler() {
+ return client.getChannelHandler();
+ }
+
+ public boolean isConnected() {
+ return client.isConnected();
+ }
+
+ public InetSocketAddress getLocalAddress() {
+ return client.getLocalAddress();
+ }
+
+ public boolean hasAttribute(String key) {
+ return client.hasAttribute(key);
+ }
+
+ public void send(Object message) throws RemotingException {
+ client.send(message);
+ }
+
+ public Object getAttribute(String key) {
+ return client.getAttribute(key);
+ }
+
+ public void setAttribute(String key, Object value) {
+ client.setAttribute(key, value);
+ }
+
+ public void send(Object message, boolean sent) throws RemotingException {
+ client.send(message, sent);
+ }
+
+ public void removeAttribute(String key) {
+ client.removeAttribute(key);
+ }
+
+ public void close() {
+ client.close();
+ }
+ public void close(int timeout) {
+ client.close(timeout);
+ }
+
+ public boolean isClosed() {
+ return client.isClosed();
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/ServerDelegate.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/ServerDelegate.java
new file mode 100644
index 0000000..e91572e
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/ServerDelegate.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting.transport;
+
+import java.net.InetSocketAddress;
+import java.util.Collection;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.remoting.Channel;
+import com.alibaba.dubbo.remoting.ChannelHandler;
+import com.alibaba.dubbo.remoting.RemotingException;
+import com.alibaba.dubbo.remoting.Server;
+
+/**
+ * ServerDelegate
+ *
+ * @author william.liangf
+ */
+public class ServerDelegate implements Server {
+
+ private transient Server server;
+
+ public ServerDelegate() {
+ }
+
+ public ServerDelegate(Server server){
+ setServer(server);
+ }
+
+ public Server getServer() {
+ return server;
+ }
+
+ public void setServer(Server server) {
+ this.server = server;
+ }
+
+ public boolean isBound() {
+ return server.isBound();
+ }
+
+ public void reset(URL url) {
+ server.reset(url);
+ }
+
+ @Deprecated
+ public void reset(com.alibaba.dubbo.common.Parameters parameters){
+ reset(getUrl().addParameters(parameters.getParameters()));
+ }
+
+ public Collection<Channel> getChannels() {
+ return server.getChannels();
+ }
+
+ public Channel getChannel(InetSocketAddress remoteAddress) {
+ return server.getChannel(remoteAddress);
+ }
+
+ public URL getUrl() {
+ return server.getUrl();
+ }
+
+ public ChannelHandler getChannelHandler() {
+ return server.getChannelHandler();
+ }
+
+ public InetSocketAddress getLocalAddress() {
+ return server.getLocalAddress();
+ }
+
+ public void send(Object message) throws RemotingException {
+ server.send(message);
+ }
+
+ public void send(Object message, boolean sent) throws RemotingException {
+ server.send(message, sent);
+ }
+
+ public void close() {
+ server.close();
+ }
+
+ public void close(int timeout) {
+ server.close(timeout);
+ }
+
+ public boolean isClosed() {
+ return server.isClosed();
+ }
+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/codec/TransportCodec.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/codec/TransportCodec.java
new file mode 100644
index 0000000..3ac299d
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/codec/TransportCodec.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting.transport.codec;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.common.serialize.ObjectInput;
+import com.alibaba.dubbo.common.serialize.ObjectOutput;
+import com.alibaba.dubbo.common.utils.StringUtils;
+import com.alibaba.dubbo.remoting.Channel;
+import com.alibaba.dubbo.remoting.transport.AbstractCodec;
+
+/**
+ * TransportCodec
+ *
+ * @author william.liangf
+ */
+@Extension("transport")
+public class TransportCodec extends AbstractCodec {
+
+ public void encode(Channel channel, OutputStream output, Object message) throws IOException {
+ ObjectOutput objectOutput = getSerialization(channel).serialize(channel.getUrl(), output);
+ encodeData(channel, objectOutput, message);
+ objectOutput.flushBuffer();
+ }
+
+ public Object decode(Channel channel, InputStream input) throws IOException {
+ return decodeData(channel, getSerialization(channel).deserialize(channel.getUrl(), input));
+ }
+
+ protected void encodeData(Channel channel, ObjectOutput output, Object message) throws IOException {
+ encodeData(output, message);
+ }
+
+ protected Object decodeData(Channel channel, ObjectInput input) throws IOException {
+ return decodeData(input);
+ }
+
+ protected void encodeData(ObjectOutput output, Object message) throws IOException {
+ output.writeObject(message);
+ }
+
+ protected Object decodeData(ObjectInput input) throws IOException {
+ try {
+ return input.readObject();
+ } catch (ClassNotFoundException e) {
+ throw new IOException("ClassNotFoundException: " + StringUtils.toString(e));
+ }
+ }
+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/handler/ChannelEventRunnable.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/handler/ChannelEventRunnable.java
new file mode 100644
index 0000000..03aae7a
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/handler/ChannelEventRunnable.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.transport.handler;
+
+import com.alibaba.dubbo.remoting.Channel;
+import com.alibaba.dubbo.remoting.ChannelHandler;
+
+/**
+ * @author chao.liuc
+ *
+ */
+public class ChannelEventRunnable implements Runnable {
+ private final ChannelHandler handler;
+ private final Channel channel;
+ private final ChannelState state;
+ private final Throwable exception;
+ private final Object message;
+
+ public ChannelEventRunnable(Channel channel, ChannelHandler handler, ChannelState state) {
+ this(channel, handler, state, null);
+ }
+
+ public ChannelEventRunnable(Channel channel, ChannelHandler handler, ChannelState state, Object message) {
+ this(channel, handler, state, message, null);
+ }
+
+ public ChannelEventRunnable(Channel channel, ChannelHandler handler, ChannelState state, Throwable t) {
+ this(channel, handler, state, null , t);
+ }
+
+ public ChannelEventRunnable(Channel channel, ChannelHandler handler, ChannelState state, Object message, Throwable exception) {
+ this.channel = channel;
+ this.handler = handler;
+ this.state = state;
+ this.message = message;
+ this.exception = exception;
+ }
+
+ public void run() {
+ switch (state) {
+ case CONNECTED:
+ try{
+ handler.connected(channel);
+ }catch (Exception e) {
+ throw new RuntimeException("ChannelEventRunnable handle error,channel is "+channel,e);
+ }
+ break;
+ case DISCONNECTED:
+ try{
+ handler.disconnected(channel);
+ }catch (Exception e) {
+ throw new RuntimeException("ChannelEventRunnable handle error,channel is "+channel,e);
+ }
+ break;
+ case SENT:
+ try{
+ handler.sent(channel,message);
+ }catch (Exception e) {
+ throw new RuntimeException("ChannelEventRunnable handle error,channel is "+channel+",message is "+ message,e);
+ }
+
+ break;
+ case RECEIVED:
+ try{
+ handler.received(channel, message);
+ }catch (Exception e) {
+ throw new RuntimeException("ChannelEventRunnable handle error,channel is "+channel+",message is "+ message,e);
+ }
+ break;
+ case CAUGHT:
+ try{
+ handler.caught(channel, exception);
+ }catch (Exception e) {
+ throw new RuntimeException("ChannelEventRunnable handle error,channel is "+channel +", message is: "+message+" exception is "+exception,e);
+ }
+
+ break;
+ }
+ }
+
+ public enum ChannelState{
+ CONNECTED,DISCONNECTED,SENT,RECEIVED,CAUGHT
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/handler/ChannelHandlers.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/handler/ChannelHandlers.java
new file mode 100644
index 0000000..931a1c0
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/handler/ChannelHandlers.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting.transport.handler;
+
+
+import com.alibaba.dubbo.common.ExtensionLoader;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.remoting.ChannelHandler;
+import com.alibaba.dubbo.remoting.ChannelHandlerWrapper;
+
+/**
+ * @author chao.liuc
+ *
+ */
+public class ChannelHandlers {
+
+ public static ChannelHandler wrap(ChannelHandler handler, URL url){
+ return ExtensionLoader.getExtensionLoader(ChannelHandlerWrapper.class)
+ .getAdaptiveExtension().wrap(handler, url);
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/handler/ConnectionOrderedChannelHandler.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/handler/ConnectionOrderedChannelHandler.java
new file mode 100644
index 0000000..a33220f
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/handler/ConnectionOrderedChannelHandler.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.remoting.transport.handler;
+
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.threadpool.support.AbortPolicyWithReport;
+import com.alibaba.dubbo.common.utils.NamedThreadFactory;
+import com.alibaba.dubbo.remoting.Channel;
+import com.alibaba.dubbo.remoting.ChannelHandler;
+import com.alibaba.dubbo.remoting.ExecutionException;
+import com.alibaba.dubbo.remoting.RemotingException;
+import com.alibaba.dubbo.remoting.transport.handler.ChannelEventRunnable.ChannelState;
+
+public class ConnectionOrderedChannelHandler extends WrappedChannelHandler {
+
+ protected final ThreadPoolExecutor connectionExecutor;
+ private final int queuewarninglimit ;
+
+ public ConnectionOrderedChannelHandler(ChannelHandler handler, URL url) {
+ super(handler, url);
+ String threadName = url.getParameter(Constants.THREAD_NAME_KEY,Constants.DEFAULT_THREAD_NAME);
+ connectionExecutor = new ThreadPoolExecutor(1, 1,
+ 0L, TimeUnit.MILLISECONDS,
+ new LinkedBlockingQueue<Runnable>(url.getPositiveParameter(Constants.CONNECT_QUENE_CAPACITY, Integer.MAX_VALUE)),
+ new NamedThreadFactory(threadName, true),
+ new AbortPolicyWithReport(threadName, url)
+ ); // FIXME 没有地方释放connectionExecutor!
+ queuewarninglimit = url.getParameter(Constants.CONNECT_QUENE_WARNING_SIZE, Constants.DEFAULT_CONNECT_QUENE_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 {
+ 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 `quene size: "+connectionExecutor.getQueue().size()+" exceed the warning limit number :"+queuewarninglimit));
+ }
+ }
+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/handler/ConnectionOrderedChannelHandlerWrapper.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/handler/ConnectionOrderedChannelHandlerWrapper.java
new file mode 100644
index 0000000..48c5bef
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/handler/ConnectionOrderedChannelHandlerWrapper.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting.transport.handler;
+
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.remoting.ChannelHandler;
+import com.alibaba.dubbo.remoting.ChannelHandlerWrapper;
+
+/**
+ * connect disconnect 保证顺序.
+ *
+ * @author chao.liuc
+ */
+@Extension(ConnectionOrderedChannelHandlerWrapper.NAME)
+public class ConnectionOrderedChannelHandlerWrapper implements ChannelHandlerWrapper {
+
+ public static final String NAME = "connection";
+
+ public ChannelHandler wrap(ChannelHandler handler, URL url) {
+ return new ConnectionOrderedChannelHandler(handler, url);
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/handler/DefaultChannelHandler.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/handler/DefaultChannelHandler.java
new file mode 100644
index 0000000..3e6e3c5
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/handler/DefaultChannelHandler.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.transport.handler;
+
+import java.util.concurrent.ExecutorService;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.remoting.Channel;
+import com.alibaba.dubbo.remoting.ChannelHandler;
+import com.alibaba.dubbo.remoting.ExecutionException;
+import com.alibaba.dubbo.remoting.RemotingException;
+import com.alibaba.dubbo.remoting.transport.handler.ChannelEventRunnable.ChannelState;
+
+public class DefaultChannelHandler extends WrappedChannelHandler {
+
+ public DefaultChannelHandler(ChannelHandler handler, URL url) {
+ super(handler, url);
+ }
+
+ public void connected(Channel channel) throws RemotingException {
+ ExecutorService cexecutor = getExecutorService();
+ try{
+ cexecutor.execute(new ChannelEventRunnable(channel, handler ,ChannelState.CONNECTED));
+ }catch (Throwable t) {
+ throw new ExecutionException("connect event", channel, getClass()+" error when process connected event ." , t);
+ }
+ }
+
+ public void disconnected(Channel channel) throws RemotingException {
+ ExecutorService cexecutor = getExecutorService();
+ try{
+ cexecutor.execute(new ChannelEventRunnable(channel, handler ,ChannelState.DISCONNECTED));
+ }catch (Throwable t) {
+ throw new ExecutionException("disconnect event", channel, getClass()+" error when process disconnected event ." , t);
+ }
+ }
+
+ public void received(Channel channel, Object message) throws RemotingException {
+ ExecutorService cexecutor = getExecutorService();
+ try{
+ cexecutor.execute(new ChannelEventRunnable(channel, handler ,ChannelState.RECEIVED, message));
+ }catch (Throwable t) {
+ throw new ExecutionException(message, channel, getClass()+" error when process received event ." , t);
+ }
+ }
+
+ public void caught(Channel channel, Throwable exception) throws RemotingException {
+ ExecutorService cexecutor = getExecutorService();
+ try{
+ cexecutor.execute(new ChannelEventRunnable(channel, handler ,ChannelState.CAUGHT, exception));
+ }catch (Throwable t) {
+ throw new ExecutionException("caught event", channel, getClass()+" error when process caught event ." , t);
+ }
+ }
+
+ private ExecutorService getExecutorService() {
+ ExecutorService cexecutor = executor;
+ if (cexecutor == null || cexecutor.isShutdown()) {
+ cexecutor = SHARED_EXECUTOR;
+ }
+ return cexecutor;
+ }
+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/handler/DefaultChannelHandlerWrapper.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/handler/DefaultChannelHandlerWrapper.java
new file mode 100644
index 0000000..5d5da8c
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/handler/DefaultChannelHandlerWrapper.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting.transport.handler;
+
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.remoting.ChannelHandler;
+import com.alibaba.dubbo.remoting.ChannelHandlerWrapper;
+
+/**
+ * 默认的线程池配置
+ *
+ * @author chao.liuc
+ */
+@Extension(DefaultChannelHandlerWrapper.NAME)
+public class DefaultChannelHandlerWrapper implements ChannelHandlerWrapper {
+
+ public static final String NAME = "default";
+
+ public ChannelHandler wrap(ChannelHandler handler, URL url) {
+ return new DefaultChannelHandler(handler, url);
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/handler/DirectChannelHandlerWrapper.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/handler/DirectChannelHandlerWrapper.java
new file mode 100644
index 0000000..011307f
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/handler/DirectChannelHandlerWrapper.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting.transport.handler;
+
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.remoting.ChannelHandler;
+import com.alibaba.dubbo.remoting.ChannelHandlerWrapper;
+
+/**
+ * 不派发线程池。
+ *
+ * @author chao.liuc
+ */
+@Extension(DirectChannelHandlerWrapper.NAME)
+public class DirectChannelHandlerWrapper implements ChannelHandlerWrapper {
+
+ public static final String NAME = "direct";
+
+ public ChannelHandler wrap(ChannelHandler handler, URL url) {
+ return handler;
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/handler/ExecutionChannelHandler.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/handler/ExecutionChannelHandler.java
new file mode 100644
index 0000000..1e3d8dc
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/handler/ExecutionChannelHandler.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.handler;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.remoting.Channel;
+import com.alibaba.dubbo.remoting.ChannelHandler;
+import com.alibaba.dubbo.remoting.RemotingException;
+import com.alibaba.dubbo.remoting.transport.handler.ChannelEventRunnable.ChannelState;
+
+public class ExecutionChannelHandler extends WrappedChannelHandler {
+
+ public ExecutionChannelHandler(ChannelHandler handler, URL url) {
+ super(handler, url);
+ }
+
+ public void connected(Channel channel) throws RemotingException {
+ executor.execute(new ChannelEventRunnable(channel, handler ,ChannelState.CONNECTED));
+ }
+
+ public void disconnected(Channel channel) throws RemotingException {
+ executor.execute(new ChannelEventRunnable(channel, handler ,ChannelState.DISCONNECTED));
+ }
+
+ public void received(Channel channel, Object message) throws RemotingException {
+ executor.execute(new ChannelEventRunnable(channel, handler ,ChannelState.RECEIVED, message));
+ }
+
+ public void caught(Channel channel, Throwable exception) throws RemotingException {
+ executor.execute(new ChannelEventRunnable(channel, handler ,ChannelState.CAUGHT, exception));
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/handler/ExecutionChannelHandlerWrapper.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/handler/ExecutionChannelHandlerWrapper.java
new file mode 100644
index 0000000..7877471
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/handler/ExecutionChannelHandlerWrapper.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting.transport.handler;
+
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.remoting.ChannelHandler;
+import com.alibaba.dubbo.remoting.ChannelHandlerWrapper;
+
+/**
+ * 除发送全部使用线程池处理
+ *
+ * @author chao.liuc
+ */
+@Extension(ExecutionChannelHandlerWrapper.NAME)
+public class ExecutionChannelHandlerWrapper implements ChannelHandlerWrapper {
+
+ public static final String NAME = "execution";
+
+ public ChannelHandler wrap(ChannelHandler handler, URL url) {
+ return new ExecutionChannelHandler(handler, url);
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/handler/MessageOnlyChannelHandler.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/handler/MessageOnlyChannelHandler.java
new file mode 100644
index 0000000..9b5857e
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/handler/MessageOnlyChannelHandler.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.transport.handler;
+
+import java.util.concurrent.ExecutorService;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.remoting.Channel;
+import com.alibaba.dubbo.remoting.ChannelHandler;
+import com.alibaba.dubbo.remoting.ExecutionException;
+import com.alibaba.dubbo.remoting.RemotingException;
+import com.alibaba.dubbo.remoting.transport.handler.ChannelEventRunnable.ChannelState;
+
+public class MessageOnlyChannelHandler extends WrappedChannelHandler {
+
+ public MessageOnlyChannelHandler(ChannelHandler handler, URL url) {
+ super(handler, url);
+ }
+
+ public void received(Channel channel, Object message) throws RemotingException {
+ ExecutorService cexecutor = executor;
+ if (cexecutor == null || cexecutor.isShutdown()) {
+ cexecutor = SHARED_EXECUTOR;
+ }
+ try{
+ cexecutor.execute(new ChannelEventRunnable(channel, handler ,ChannelState.RECEIVED, message));
+ }catch (Throwable t) {
+ throw new ExecutionException(message, channel, getClass()+" error when process received event ." , t);
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/handler/MessageOnlyChannelHandlerWrapper.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/handler/MessageOnlyChannelHandlerWrapper.java
new file mode 100644
index 0000000..a6afc89
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/handler/MessageOnlyChannelHandlerWrapper.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting.transport.handler;
+
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.remoting.ChannelHandler;
+import com.alibaba.dubbo.remoting.ChannelHandlerWrapper;
+
+/**
+ * 只有message receive使用线程池.
+ *
+ * @author chao.liuc
+ */
+@Extension(MessageOnlyChannelHandlerWrapper.NAME)
+public class MessageOnlyChannelHandlerWrapper implements ChannelHandlerWrapper {
+
+ public static final String NAME = "message";
+
+ public ChannelHandler wrap(ChannelHandler handler, URL url) {
+ return new MessageOnlyChannelHandler(handler, url);
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/handler/WrappedChannelHandler.java b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/handler/WrappedChannelHandler.java
new file mode 100644
index 0000000..2b59b2c
--- /dev/null
+++ b/dubbo-remoting/src/main/java/com/alibaba/dubbo/remoting/transport/handler/WrappedChannelHandler.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.transport.handler;
+
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+import com.alibaba.dubbo.common.ExtensionLoader;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.logger.Logger;
+import com.alibaba.dubbo.common.logger.LoggerFactory;
+import com.alibaba.dubbo.common.threadpool.ThreadPool;
+import com.alibaba.dubbo.common.utils.NamedThreadFactory;
+import com.alibaba.dubbo.remoting.Channel;
+import com.alibaba.dubbo.remoting.ChannelHandler;
+import com.alibaba.dubbo.remoting.RemotingException;
+
+public class WrappedChannelHandler implements ChannelHandler {
+
+ 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);
+ }
+
+ public void received(Channel channel, Object message) throws RemotingException {
+ 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() {
+ return handler;
+ }
+
+ public URL getUrl() {
+ return url;
+ }
+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/main/resources/META-INF/services/com.alibaba.dubbo.remoting.ChannelHandlerWrapper b/dubbo-remoting/src/main/resources/META-INF/services/com.alibaba.dubbo.remoting.ChannelHandlerWrapper
new file mode 100644
index 0000000..bf00c1b
--- /dev/null
+++ b/dubbo-remoting/src/main/resources/META-INF/services/com.alibaba.dubbo.remoting.ChannelHandlerWrapper
@@ -0,0 +1,5 @@
+com.alibaba.dubbo.remoting.transport.handler.DefaultChannelHandlerWrapper
+com.alibaba.dubbo.remoting.transport.handler.DirectChannelHandlerWrapper
+com.alibaba.dubbo.remoting.transport.handler.ExecutionChannelHandlerWrapper
+com.alibaba.dubbo.remoting.transport.handler.MessageOnlyChannelHandlerWrapper
+com.alibaba.dubbo.remoting.transport.handler.ConnectionOrderedChannelHandlerWrapper
\ No newline at end of file
diff --git a/dubbo-remoting/src/main/resources/META-INF/services/com.alibaba.dubbo.remoting.Codec b/dubbo-remoting/src/main/resources/META-INF/services/com.alibaba.dubbo.remoting.Codec
new file mode 100644
index 0000000..bed639d
--- /dev/null
+++ b/dubbo-remoting/src/main/resources/META-INF/services/com.alibaba.dubbo.remoting.Codec
@@ -0,0 +1,3 @@
+com.alibaba.dubbo.remoting.transport.codec.TransportCodec
+com.alibaba.dubbo.remoting.telnet.codec.TelnetCodec
+com.alibaba.dubbo.remoting.exchange.codec.ExchangeCodec
\ No newline at end of file
diff --git a/dubbo-remoting/src/main/resources/META-INF/services/com.alibaba.dubbo.remoting.exchange.Exchanger b/dubbo-remoting/src/main/resources/META-INF/services/com.alibaba.dubbo.remoting.exchange.Exchanger
new file mode 100644
index 0000000..6084189
--- /dev/null
+++ b/dubbo-remoting/src/main/resources/META-INF/services/com.alibaba.dubbo.remoting.exchange.Exchanger
@@ -0,0 +1 @@
+com.alibaba.dubbo.remoting.exchange.support.header.HeaderExchanger
\ No newline at end of file
diff --git a/dubbo-remoting/src/main/resources/META-INF/services/com.alibaba.dubbo.remoting.p2p.Networker b/dubbo-remoting/src/main/resources/META-INF/services/com.alibaba.dubbo.remoting.p2p.Networker
new file mode 100644
index 0000000..cd1e3fc
--- /dev/null
+++ b/dubbo-remoting/src/main/resources/META-INF/services/com.alibaba.dubbo.remoting.p2p.Networker
@@ -0,0 +1,2 @@
+com.alibaba.dubbo.remoting.p2p.support.MulticastNetworker
+com.alibaba.dubbo.remoting.p2p.support.FileNetworker
\ No newline at end of file
diff --git a/dubbo-remoting/src/main/resources/META-INF/services/com.alibaba.dubbo.remoting.telnet.TelnetHandler b/dubbo-remoting/src/main/resources/META-INF/services/com.alibaba.dubbo.remoting.telnet.TelnetHandler
new file mode 100644
index 0000000..cc16268
--- /dev/null
+++ b/dubbo-remoting/src/main/resources/META-INF/services/com.alibaba.dubbo.remoting.telnet.TelnetHandler
@@ -0,0 +1,4 @@
+com.alibaba.dubbo.remoting.telnet.support.command.ClearTelnetHandler
+com.alibaba.dubbo.remoting.telnet.support.command.ExitTelnetHandler
+com.alibaba.dubbo.remoting.telnet.support.command.HelpTelnetHandler
+com.alibaba.dubbo.remoting.telnet.support.command.StatusTelnetHandler
\ No newline at end of file
diff --git a/dubbo-remoting/src/test/java/com/alibaba/dubbo/remoting/ChanelHandlerTest.java b/dubbo-remoting/src/test/java/com/alibaba/dubbo/remoting/ChanelHandlerTest.java
new file mode 100644
index 0000000..1917fd6
--- /dev/null
+++ b/dubbo-remoting/src/test/java/com/alibaba/dubbo/remoting/ChanelHandlerTest.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting;
+
+import junit.framework.TestCase;
+
+import org.junit.Test;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.logger.Logger;
+import com.alibaba.dubbo.common.logger.LoggerFactory;
+import com.alibaba.dubbo.remoting.exchange.ExchangeClient;
+import com.alibaba.dubbo.remoting.exchange.Exchangers;
+import com.alibaba.dubbo.remoting.exchange.support.ExchangeHandlerAdapter;
+
+/**
+ * ChanelHandlerTest
+ *
+ * mvn clean test -Dtest=*PerformanceClientTest -Dserver=10.20.153.187:9911
+ *
+ * @author william.liangf
+ */
+public class ChanelHandlerTest extends TestCase {
+
+ private static final Logger logger = LoggerFactory.getLogger(ChanelHandlerTest.class);
+
+ @Test
+ public void testClient() throws Throwable {
+ // 读取参数
+ if (PerformanceUtils.getProperty("server", null) == null) {
+ logger.warn("Please set -Dserver=127.0.0.1:9911");
+ return;
+ }
+ final String server = System.getProperty("server", "127.0.0.1:9911");
+ final String transporter = PerformanceUtils.getProperty(Constants.TRANSPORTER_KEY, Constants.DEFAULT_TRANSPORTER);
+ final String serialization = PerformanceUtils.getProperty(Constants.SERIALIZATION_KEY, Constants.DEFAULT_REMOTING_SERIALIZATION);
+ final int timeout = PerformanceUtils.getIntProperty(Constants.TIMEOUT_KEY, Constants.DEFAULT_TIMEOUT);
+ int sleep = PerformanceUtils.getIntProperty("sleep", 60*1000*60);
+
+ final String url = "exchange://" + server + "?transporter=" + transporter + "&serialization=" + serialization + "&timeout=" + timeout;
+ ExchangeClient exchangeClient =initClient(url);
+ Thread.sleep(sleep);
+ closeClient(exchangeClient);
+ }
+
+ public static ExchangeClient initClient(String url){
+ // 创建客户端
+ ExchangeClient exchangeClient = null;
+ PeformanceTestHandler handler = new PeformanceTestHandler(url);
+ boolean run = true;
+ while(run){
+ try{
+ exchangeClient= Exchangers.connect(url,handler);
+ } catch (Throwable t){
+
+ if(t!=null && t.getCause()!=null && t.getCause().getClass()!=null && (t.getCause().getClass()==java.net.ConnectException.class
+ || t.getCause().getClass()== java.net.ConnectException.class)){
+
+ }else {
+ t.printStackTrace();
+ }
+
+ try {
+ Thread.sleep(50);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+ if (exchangeClient != null) {
+ run = false;
+ }
+ }
+ return exchangeClient;
+ }
+
+ public static void closeClient(ExchangeClient client){
+ if(client.isConnected()){
+ client.close();
+ }
+ }
+
+ static class PeformanceTestHandler extends ExchangeHandlerAdapter{
+ String url = "";
+
+ /**
+ * @param url
+ */
+ public PeformanceTestHandler(String url) {
+ this.url = url;
+ }
+
+ public void connected(Channel channel) throws RemotingException {
+ System.out.println("connected event,chanel;"+channel);
+ }
+
+ public void disconnected(Channel channel) throws RemotingException {
+ System.out.println("disconnected event,chanel;"+channel);
+ initClient (url);
+ }
+
+ /* (non-Javadoc)
+ * @see com.alibaba.dubbo.remoting.transport.support.ChannelHandlerAdapter#caught(com.alibaba.dubbo.remoting.Channel, java.lang.Throwable)
+ */
+ @Override
+ public void caught(Channel channel, Throwable exception) throws RemotingException {
+// System.out.println("caught event:"+exception);
+ }
+
+
+ }
+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/test/java/com/alibaba/dubbo/remoting/DemoService.java b/dubbo-remoting/src/test/java/com/alibaba/dubbo/remoting/DemoService.java
new file mode 100644
index 0000000..29cadad
--- /dev/null
+++ b/dubbo-remoting/src/test/java/com/alibaba/dubbo/remoting/DemoService.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting;
+
+/**
+ * <code>TestService</code>
+ */
+
+public interface DemoService
+{
+ void sayHello(String name);
+
+ int plus(int a,int b);
+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/test/java/com/alibaba/dubbo/remoting/DemoServiceImpl.java b/dubbo-remoting/src/test/java/com/alibaba/dubbo/remoting/DemoServiceImpl.java
new file mode 100644
index 0000000..a9a90e6
--- /dev/null
+++ b/dubbo-remoting/src/test/java/com/alibaba/dubbo/remoting/DemoServiceImpl.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting;
+
+/**
+ * <code>TestServiceImpl</code>
+ */
+
+public class DemoServiceImpl implements DemoService
+{
+ public void sayHello(String name)
+ {
+ System.out.println("hello " + name);
+ }
+
+ public int plus(int a,int b)
+ {
+ return a + b;
+ }
+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/test/java/com/alibaba/dubbo/remoting/Main.java b/dubbo-remoting/src/test/java/com/alibaba/dubbo/remoting/Main.java
new file mode 100644
index 0000000..f078531
--- /dev/null
+++ b/dubbo-remoting/src/test/java/com/alibaba/dubbo/remoting/Main.java
@@ -0,0 +1,142 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting;
+
+import java.io.Serializable;
+import java.util.Random;
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.remoting.exchange.ExchangeChannel;
+import com.alibaba.dubbo.remoting.exchange.Exchangers;
+import com.alibaba.dubbo.remoting.exchange.ResponseFuture;
+import com.alibaba.dubbo.remoting.exchange.support.Replier;
+import com.alibaba.dubbo.remoting.exchange.support.ReplierDispatcher;
+
+/**
+ * Main
+ */
+
+public class Main
+{
+ public static void main(String[] args) throws Exception
+ {
+ startServer(9010);
+ mutliThreadTest(10,9010);
+ dataPackageTest(9010);
+ }
+
+ private static void startServer(int port) throws Exception
+ {
+ ReplierDispatcher dispatcher = new ReplierDispatcher();
+ dispatcher.addReplier(RpcMessage.class, new RpcMessageHandler());
+ dispatcher.addReplier(Object.class, new Replier<Object>() {
+ public Object reply(ExchangeChannel channel, Object msg)
+ {
+ for(int i=0;i<10000;i++)
+ System.currentTimeMillis();
+ System.out.println("handle:"+msg+";thread:"+Thread.currentThread().getName());
+ return new StringMessage("hello world");
+ }
+ });
+ Exchangers.bind(URL.valueOf("dubbo://localhost:" + port), dispatcher);
+ }
+
+ static void dataPackageTest(int port) throws Exception
+ {
+ ExchangeChannel client = Exchangers.connect(URL.valueOf("dubbo://localhost:" + port));
+ Random random = new Random();
+ for(int i=5;i<100;i++)
+ {
+ StringBuilder sb = new StringBuilder();
+ for(int j=0;j<i*100;j++)
+ sb.append("("+random.nextLong()+")");
+ Main.Data d = new Main.Data();
+ d.setData(sb.toString());
+ client.request(d).get();
+ }
+ System.out.println("send finished.");
+ }
+
+ static void mutliThreadTest(int tc,final int port) throws Exception
+ {
+ Executor exec = Executors.newFixedThreadPool(tc);
+ for(int i=0;i<tc;i++)
+ exec.execute(new Runnable(){
+ public void run() {
+ try{ test(port); }catch(Exception e){ e.printStackTrace(); }
+ }
+ });
+ }
+
+ private static void test(int port) throws Exception
+ {
+ ExchangeChannel client = Exchangers.connect(URL.valueOf("dubbo://localhost:" + port));
+ MockResult result = (MockResult)client.request(new RpcMessage(DemoService.class.getName(),"plus",new Class<?>[]{int.class, int.class},new Object[]{55,25})).get();
+ System.out.println("55+25="+result.getResult());
+
+ for(int i=0;i<100;i++)
+ client.request(new RpcMessage(DemoService.class.getName(),"sayHello", new Class<?>[]{String.class},new Object[]{"qianlei"+i}));
+
+ for(int i=0;i<100;i++)
+ client.request(new Main.Data());
+
+ System.out.println("=====test invoke=====");
+ for(int i=0;i<100;i++){
+ ResponseFuture future = client.request(new Main.Data());
+ System.out.println("invoke and get");
+ System.out.println("invoke result:" + future.get());
+ }
+ System.out.println("=====the end=====");
+ }
+
+ static class Data implements Serializable
+ {
+ private static final long serialVersionUID = -4666580993978548778L;
+
+ private String mData = "";
+
+ public Data(){}
+
+ public String getData()
+ {
+ return mData;
+ }
+
+ public void setData(String data)
+ {
+ mData = data;
+ }
+ }
+
+ static class StringMessage implements Serializable
+ {
+ private static final long serialVersionUID = 7193122183120113947L;
+
+ private String mText;
+
+ StringMessage(String msg)
+ {
+ mText = msg;
+ }
+
+ public String toString()
+ {
+ return mText;
+ }
+ }
+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/test/java/com/alibaba/dubbo/remoting/MockResult.java b/dubbo-remoting/src/test/java/com/alibaba/dubbo/remoting/MockResult.java
new file mode 100644
index 0000000..e8f9376
--- /dev/null
+++ b/dubbo-remoting/src/test/java/com/alibaba/dubbo/remoting/MockResult.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting;
+
+import java.io.Serializable;
+
+/**
+ * RpcResult.
+ *
+ * @author qian.lei
+ */
+
+public class MockResult implements Serializable
+{
+ private static final long serialVersionUID = -3630485157441794463L;
+
+ private final Object mResult;
+
+ public MockResult(Object result)
+ {
+ mResult = result;
+ }
+
+ public Object getResult()
+ {
+ return mResult;
+ }
+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/test/java/com/alibaba/dubbo/remoting/PeerMain.java b/dubbo-remoting/src/test/java/com/alibaba/dubbo/remoting/PeerMain.java
new file mode 100644
index 0000000..551a8d9
--- /dev/null
+++ b/dubbo-remoting/src/test/java/com/alibaba/dubbo/remoting/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;
+
+import java.util.Collection;
+
+import com.alibaba.dubbo.remoting.p2p.Networkers;
+import com.alibaba.dubbo.remoting.p2p.Peer;
+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/src/test/java/com/alibaba/dubbo/remoting/PerformanceClientCloseTest.java b/dubbo-remoting/src/test/java/com/alibaba/dubbo/remoting/PerformanceClientCloseTest.java
new file mode 100644
index 0000000..8194562
--- /dev/null
+++ b/dubbo-remoting/src/test/java/com/alibaba/dubbo/remoting/PerformanceClientCloseTest.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting;
+
+import java.util.concurrent.atomic.AtomicInteger;
+
+import junit.framework.TestCase;
+
+import org.junit.Test;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.logger.Logger;
+import com.alibaba.dubbo.common.logger.LoggerFactory;
+import com.alibaba.dubbo.remoting.exchange.ExchangeClient;
+import com.alibaba.dubbo.remoting.exchange.Exchangers;
+
+/**
+ * ProformanceClient
+ * 这个测试类会报线程池的异常,因为DefaultChannelHandler中关于线程池的判断产生并发问题(connected事件异步执行,判断已经过了,这时关闭了线程池,然后线程池执行,报错,此问题通过指定Constants.CHANNEL_HANDLER_KEY=connection即可.)
+ *
+ * @author william.liangf
+ */
+public class PerformanceClientCloseTest extends TestCase {
+
+ private static final Logger logger = LoggerFactory.getLogger(PerformanceClientCloseTest.class);
+
+ @Test
+ public void testClient() throws Throwable {
+ // 读取参数
+ if (PerformanceUtils.getProperty("server", null) == null) {
+ logger.warn("Please set -Dserver=127.0.0.1:9911");
+ return;
+ }
+ final String server = System.getProperty("server", "127.0.0.1:9911");
+ final String transporter = PerformanceUtils.getProperty(Constants.TRANSPORTER_KEY, Constants.DEFAULT_TRANSPORTER);
+ final String serialization = PerformanceUtils.getProperty(Constants.SERIALIZATION_KEY, Constants.DEFAULT_REMOTING_SERIALIZATION);
+ final int timeout = PerformanceUtils.getIntProperty(Constants.TIMEOUT_KEY, Constants.DEFAULT_TIMEOUT);
+ final int concurrent = PerformanceUtils.getIntProperty("concurrent", 1);
+ final int runs = PerformanceUtils.getIntProperty("runs", Integer.MAX_VALUE);
+ final String onerror = PerformanceUtils.getProperty("onerror", "continue");
+
+ final String url = "exchange://" + server + "?transporter=" + transporter
+ + "&serialization=" + serialization
+// + "&"+Constants.CHANNEL_HANDLER_KEY+"=connection"
+ + "&timeout=" + timeout;
+
+ final AtomicInteger count = new AtomicInteger();
+ final AtomicInteger error = new AtomicInteger();
+ for (int n = 0; n < concurrent; n++) {
+ new Thread(new Runnable() {
+ public void run() {
+ for (int i = 0; i < runs; i++) {
+ ExchangeClient client = null;
+ try {
+ client = Exchangers.connect(url);
+ int c = count.incrementAndGet();
+ if (c % 100 == 0) {
+ System.out.println("count: " + count.get() + ", error: " + error.get());
+ }
+ } catch (Exception e) {
+ error.incrementAndGet();
+ e.printStackTrace();
+ System.out.println("count: " + count.get() + ", error: " + error.get());
+ if ("exit".equals(onerror)) {
+ System.exit(-1);
+ } else if ("break".equals(onerror)) {
+ break;
+ } else if ("sleep".equals(onerror)) {
+ try {
+ Thread.sleep(30000);
+ } catch (InterruptedException e1) {
+ }
+ }
+ } finally {
+ if (client != null) {
+ client.close();
+ }
+ }
+ }
+ }
+ }).start();
+ }
+ synchronized (PerformanceServerTest.class) {
+ while (true) {
+ try {
+ PerformanceServerTest.class.wait();
+ } catch (InterruptedException e) {
+ }
+ }
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/test/java/com/alibaba/dubbo/remoting/PerformanceClientFixedTest.java b/dubbo-remoting/src/test/java/com/alibaba/dubbo/remoting/PerformanceClientFixedTest.java
new file mode 100644
index 0000000..6b93ca6
--- /dev/null
+++ b/dubbo-remoting/src/test/java/com/alibaba/dubbo/remoting/PerformanceClientFixedTest.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting;
+
+import java.util.ArrayList;
+import java.util.Random;
+
+import junit.framework.TestCase;
+
+import org.junit.Test;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.logger.Logger;
+import com.alibaba.dubbo.common.logger.LoggerFactory;
+import com.alibaba.dubbo.remoting.exchange.ExchangeClient;
+import com.alibaba.dubbo.remoting.exchange.Exchangers;
+
+public class PerformanceClientFixedTest extends TestCase {
+
+ private static final Logger logger = LoggerFactory.getLogger(PerformanceClientTest.class);
+
+ @Test
+ public void testClient() throws Exception {
+ // 读取参数
+ if (PerformanceUtils.getProperty("server", null) == null) {
+ logger.warn("Please set -Dserver=127.0.0.1:9911");
+ return;
+ }
+ final String server = System.getProperty("server", "127.0.0.1:9911");
+ final String transporter = PerformanceUtils.getProperty(Constants.TRANSPORTER_KEY, Constants.DEFAULT_TRANSPORTER);
+ final String serialization = PerformanceUtils.getProperty(Constants.SERIALIZATION_KEY, Constants.DEFAULT_REMOTING_SERIALIZATION);
+ final int timeout = PerformanceUtils.getIntProperty(Constants.TIMEOUT_KEY, Constants.DEFAULT_TIMEOUT);
+ //final int length = PerformanceUtils.getIntProperty("length", 1024);
+ final int connectionCount = PerformanceUtils.getIntProperty(Constants.CONNECTIONS_KEY, 1);
+ //final int concurrent = PerformanceUtils.getIntProperty("concurrent", 100);
+ //int r = PerformanceUtils.getIntProperty("runs", 10000);
+ //final int runs = r > 0 ? r : Integer.MAX_VALUE;
+ //final String onerror = PerformanceUtils.getProperty("onerror", "continue");
+ final String url = "exchange://" + server + "?transporter=" + transporter + "&serialization=" + serialization + "&timeout=" + timeout;
+
+ //int idx = server.indexOf(':');
+ Random rd = new Random(connectionCount);
+ ArrayList<ExchangeClient> arrays = new ArrayList<ExchangeClient>();
+ String oneKBlock = null;
+ String messageBlock = null;
+ int s = 0;
+ int f = 0;
+ System.out.println("initialize arrays " + url);
+ while (s < connectionCount) {
+ ExchangeClient client = null;
+ try {
+ System.out.println("open connection " + s + " " + url + arrays.size());
+
+ client = Exchangers.connect(url);
+
+ System.out.println("run after open");
+
+ if (client.isConnected()) {
+ arrays.add(client);
+ s++;
+ System.out.println("open client success " + s);
+ } else {
+ System.out.println("open client failed, try again.");
+ }
+ } catch (Throwable t) {
+ t.printStackTrace();
+ } finally {
+ if (client != null && client.isConnected() == false) {
+ f++;
+ System.out.println("open client failed, try again " + f);
+ client.close();
+ }
+ }
+ }
+
+ StringBuilder sb1 = new StringBuilder();
+ Random rd2 = new Random();
+ char[] numbersAndLetters = ("0123456789abcdefghijklmnopqrstuvwxyz" + "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ").toCharArray();
+ int size1 = numbersAndLetters.length;
+ for (int j = 0; j < 1024; j++) {
+ sb1.append(numbersAndLetters[rd2.nextInt(size1)]);
+ }
+ oneKBlock = sb1.toString();
+
+ for (int j = 0; j < Integer.MAX_VALUE; j++) {
+ try {
+ String size = "10";
+
+ int request_size = 10;
+ try {
+ request_size = Integer.parseInt(size);
+ } catch (Throwable t) {
+ request_size = 10;
+ }
+
+ if (messageBlock == null) {
+ StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < request_size; i++) {
+ sb.append(oneKBlock);
+ }
+ messageBlock = sb.toString();
+
+ System.out.println("set messageBlock to " + messageBlock);
+ }
+ int index = rd.nextInt(connectionCount);
+ ExchangeClient client = arrays.get(index);
+ // ExchangeClient client = arrays.get(0);
+ String output = (String) client.request(messageBlock).get();
+
+ if (output.lastIndexOf(messageBlock) < 0) {
+ System.out.println("send messageBlock;get " + output);
+ throw new Throwable("return results invalid");
+ } else {
+ if (j % 100 == 0)
+ System.out.println("OK: " + j);
+ }
+ } catch (Throwable t) {
+ t.printStackTrace();
+ }
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/test/java/com/alibaba/dubbo/remoting/PerformanceClientMain.java b/dubbo-remoting/src/test/java/com/alibaba/dubbo/remoting/PerformanceClientMain.java
new file mode 100644
index 0000000..1819601
--- /dev/null
+++ b/dubbo-remoting/src/test/java/com/alibaba/dubbo/remoting/PerformanceClientMain.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting;
+
+
+/**
+ * PerformanceClientMain
+ */
+public class PerformanceClientMain {
+
+ public static void main(String[] args) throws Throwable {
+ new PerformanceClientTest().testClient();
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/test/java/com/alibaba/dubbo/remoting/PerformanceClientTest.java b/dubbo-remoting/src/test/java/com/alibaba/dubbo/remoting/PerformanceClientTest.java
new file mode 100644
index 0000000..658de26
--- /dev/null
+++ b/dubbo-remoting/src/test/java/com/alibaba/dubbo/remoting/PerformanceClientTest.java
@@ -0,0 +1,228 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting;
+
+import java.text.DecimalFormat;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicLong;
+
+import junit.framework.TestCase;
+
+import org.junit.Test;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.logger.Logger;
+import com.alibaba.dubbo.common.logger.LoggerFactory;
+import com.alibaba.dubbo.remoting.exchange.ExchangeClient;
+import com.alibaba.dubbo.remoting.exchange.Exchangers;
+import com.alibaba.dubbo.remoting.exchange.support.ExchangeHandlerAdapter;
+
+/**
+ * PerformanceClientTest
+ *
+ * mvn clean test -Dtest=*PerformanceClientTest -Dserver=10.20.153.187:9911
+ *
+ * @author william.liangf
+ */
+public class PerformanceClientTest extends TestCase {
+
+ private static final Logger logger = LoggerFactory.getLogger(PerformanceClientTest.class);
+
+ @Test
+ @SuppressWarnings("unchecked")
+ public void testClient() throws Throwable {
+ // 读取参数
+ if (PerformanceUtils.getProperty("server", null) == null) {
+ logger.warn("Please set -Dserver=127.0.0.1:9911");
+ return;
+ }
+ final String server = System.getProperty("server", "127.0.0.1:9911");
+ final String transporter = PerformanceUtils.getProperty(Constants.TRANSPORTER_KEY, Constants.DEFAULT_TRANSPORTER);
+ final String serialization = PerformanceUtils.getProperty(Constants.SERIALIZATION_KEY, Constants.DEFAULT_REMOTING_SERIALIZATION);
+ final int timeout = PerformanceUtils.getIntProperty(Constants.TIMEOUT_KEY, Constants.DEFAULT_TIMEOUT);
+ final int length = PerformanceUtils.getIntProperty("length", 1024);
+ final int connections = PerformanceUtils.getIntProperty(Constants.CONNECTIONS_KEY, 1);
+ final int concurrent = PerformanceUtils.getIntProperty("concurrent", 100);
+ int r = PerformanceUtils.getIntProperty("runs", 10000);
+ final int runs = r > 0 ? r : Integer.MAX_VALUE;
+ final String onerror = PerformanceUtils.getProperty("onerror", "continue");
+
+ final String url = "exchange://" + server + "?transporter=" + transporter + "&serialization=" + serialization + "&timeout=" + timeout;
+ // 创建客户端
+ final ExchangeClient[] exchangeClients = new ExchangeClient[connections];
+ for (int i = 0; i < connections; i ++) {
+ //exchangeClients[i] = Exchangers.connect(url,handler);
+ exchangeClients[i] = Exchangers.connect(url);
+ }
+
+ List<String> serverEnvironment = (List<String>) exchangeClients[0].request("environment").get();
+ List<String> serverScene = (List<String>) exchangeClients[0].request("scene").get();
+
+ // 制造数据
+ StringBuilder buf = new StringBuilder(length);
+ for (int i = 0; i < length; i ++) {
+ buf.append("A");
+ }
+ final String data = buf.toString();
+
+ // 计数器
+ final AtomicLong count = new AtomicLong();
+ final AtomicLong error = new AtomicLong();
+ final AtomicLong time = new AtomicLong();
+ final AtomicLong all = new AtomicLong();
+
+ // 并发调用
+ final CountDownLatch latch = new CountDownLatch(concurrent);
+ for (int i = 0; i < concurrent; i ++) {
+ new Thread(new Runnable() {
+ public void run() {
+ try {
+ AtomicInteger index = new AtomicInteger();
+ long init = System.currentTimeMillis();
+ for (int i = 0; i < runs; i++) {
+ try {
+ count.incrementAndGet();
+ ExchangeClient client = exchangeClients[index.getAndIncrement() % connections];
+ long start = System.currentTimeMillis();
+ String result = (String) client.request(data).get();
+ long end = System.currentTimeMillis();
+ if (! data.equals(result)) {
+ throw new IllegalStateException("Invalid result " + result);
+ }
+ time.addAndGet(end - start);
+ } catch (Exception e) {
+ error.incrementAndGet();
+ e.printStackTrace();
+ if ("exit".equals(onerror)) {
+ System.exit(-1);
+ } else if ("break".equals(onerror)) {
+ break;
+ } else if ("sleep".equals(onerror)) {
+ try {
+ Thread.sleep(30000);
+ } catch (InterruptedException e1) {
+ }
+ }
+ }
+ }
+ all.addAndGet(System.currentTimeMillis() - init);
+ } finally {
+ latch.countDown();
+ }
+ }
+ }).start();
+ }
+
+ // 输出,tps不精确,但大概反映情况
+ new Thread(new Runnable() {
+ public void run() {
+ try{
+ SimpleDateFormat dateFormat = new SimpleDateFormat ("HH:mm:ss");
+ long lastCount = count.get();
+ long sleepTime = 2000;
+ long elapsd = sleepTime/1000;
+ boolean bfirst = true;
+ while (latch.getCount() > 0) {
+ long c = count.get()-lastCount ;
+ if(! bfirst)//第一次不准
+ System.out.println("["+dateFormat.format(new Date()) +"] count: " + count.get() + ", error: " + error.get() + ",tps:"+(c/elapsd));
+
+ bfirst = false;
+ lastCount = count.get();
+ Thread.sleep(sleepTime);
+ }
+ } catch(Exception e) {
+ e.printStackTrace();
+ }
+ }
+ }).start();
+
+ latch.await();
+
+ for(ExchangeClient client:exchangeClients){
+ if(client.isConnected()){
+ client.close();
+ }
+ }
+
+ long total = count.get();
+ long failed = error.get();
+ long succeeded = total - failed;
+ long elapsed = time.get();
+ long allElapsed = all.get();
+ long clientElapsed = allElapsed - elapsed;
+ long art = 0;
+ long qps = 0;
+ long throughput = 0;
+ if (elapsed > 0) {
+ art = elapsed / succeeded;
+ qps = concurrent * succeeded * 1000 / elapsed;
+ throughput = concurrent * succeeded * length * 2 * 1000 / elapsed;
+ }
+
+ PerformanceUtils.printBorder();
+ PerformanceUtils.printHeader("Dubbo Remoting Performance Test Report");
+ PerformanceUtils.printBorder();
+ PerformanceUtils.printHeader("Test Environment");
+ PerformanceUtils.printSeparator();
+ for (String item: serverEnvironment) {
+ PerformanceUtils.printBody("Server " + item);
+ }
+ PerformanceUtils.printSeparator();
+ List<String> clientEnvironment = PerformanceUtils.getEnvironment();
+ for (String item: clientEnvironment) {
+ PerformanceUtils.printBody("Client " + item);
+ }
+ PerformanceUtils.printSeparator();
+ PerformanceUtils.printHeader("Test Scene");
+ PerformanceUtils.printSeparator();
+ for (String item: serverScene) {
+ PerformanceUtils.printBody("Server " + item);
+ }
+ PerformanceUtils.printBody("Client Transporter: " + transporter);
+ PerformanceUtils.printBody("Serialization: " + serialization);
+ PerformanceUtils.printBody("Response Timeout: " + timeout + " ms");
+ PerformanceUtils.printBody("Data Length: " + length + " bytes");
+ PerformanceUtils.printBody("Client Shared Connections: " + connections);
+ PerformanceUtils.printBody("Client Concurrent Threads: " + concurrent);
+ PerformanceUtils.printBody("Run Times Per Thread: " + runs);
+ PerformanceUtils.printSeparator();
+ PerformanceUtils.printHeader("Test Result");
+ PerformanceUtils.printSeparator();
+ PerformanceUtils.printBody("Succeeded Requests: " + DecimalFormat.getIntegerInstance().format(succeeded));
+ PerformanceUtils.printBody("Failed Requests: " + failed);
+ PerformanceUtils.printBody("Client Elapsed Time: " + clientElapsed + " ms");
+ PerformanceUtils.printBody("Average Response Time: " + art + " ms");
+ PerformanceUtils.printBody("Requests Per Second: " + qps + "/s");
+ PerformanceUtils.printBody("Throughput Per Second: " + DecimalFormat.getIntegerInstance().format(throughput) + " bytes/s");
+ PerformanceUtils.printBorder();
+ }
+
+ static class PeformanceTestHandler extends ExchangeHandlerAdapter{
+
+ public void connected(Channel channel) throws RemotingException {
+ System.out.println("connected event,chanel;"+channel);
+ }
+
+ public void disconnected(Channel channel) throws RemotingException {
+ System.out.println("disconnected event,chanel;"+channel);
+ }
+ }
+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/test/java/com/alibaba/dubbo/remoting/PerformanceServerMain.java b/dubbo-remoting/src/test/java/com/alibaba/dubbo/remoting/PerformanceServerMain.java
new file mode 100644
index 0000000..55038a3
--- /dev/null
+++ b/dubbo-remoting/src/test/java/com/alibaba/dubbo/remoting/PerformanceServerMain.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting;
+
+/**
+ * PerformanceServerMain
+ *
+ * @author william.liangf
+ */
+public class PerformanceServerMain{
+
+ public static void main(String[] args) throws Exception {
+ new PerformanceServerTest().testServer();
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/test/java/com/alibaba/dubbo/remoting/PerformanceServerTest.java b/dubbo-remoting/src/test/java/com/alibaba/dubbo/remoting/PerformanceServerTest.java
new file mode 100644
index 0000000..ab32827
--- /dev/null
+++ b/dubbo-remoting/src/test/java/com/alibaba/dubbo/remoting/PerformanceServerTest.java
@@ -0,0 +1,163 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import junit.framework.TestCase;
+
+import org.junit.Test;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.logger.Logger;
+import com.alibaba.dubbo.common.logger.LoggerFactory;
+import com.alibaba.dubbo.remoting.exchange.ExchangeChannel;
+import com.alibaba.dubbo.remoting.exchange.ExchangeServer;
+import com.alibaba.dubbo.remoting.exchange.Exchangers;
+import com.alibaba.dubbo.remoting.exchange.support.ExchangeHandlerAdapter;
+import com.alibaba.dubbo.remoting.transport.handler.ExecutionChannelHandlerWrapper;
+
+/**
+ * PerformanceServer
+ *
+ * mvn clean test -Dtest=*PerformanceServerTest -Dport=9911
+ *
+ * @author william.liangf
+ */
+public class PerformanceServerTest extends TestCase {
+
+ private static final Logger logger = LoggerFactory.getLogger(PerformanceServerTest.class);
+ private static ExchangeServer server = null;
+ @Test
+ public void testServer() throws Exception {
+ // 读取参数
+ if (PerformanceUtils.getProperty("port", null) == null) {
+ logger.warn("Please set -Dport=9911");
+ return;
+ }
+ final int port = PerformanceUtils.getIntProperty("port", 9911);
+ final boolean telnet = PerformanceUtils.getBooleanProperty("telnet", true);
+ if(telnet)statTelnetServer(port+1);
+ server = statServer();
+
+ synchronized (PerformanceServerTest.class) {
+ while (true) {
+ try {
+ PerformanceServerTest.class.wait();
+ } catch (InterruptedException e) {
+ }
+ }
+ }
+ }
+ private static void restartServer(int times,int alive,int sleep) throws Exception{
+ if(server!=null && !server.isClosed()){
+ server.close();
+ Thread.sleep(100);
+ }
+
+ for(int i=0;i<times;i++){
+ logger.info("restart times:"+i);
+ server = statServer();
+ if(alive>0)Thread.sleep(alive);
+ server.close();
+ if(sleep>0)Thread.sleep(sleep);
+ }
+
+ server = statServer();
+ }
+
+ private static ExchangeServer statServer() throws Exception{
+ final int port = PerformanceUtils.getIntProperty("port", 9911);
+ final String transporter = PerformanceUtils.getProperty(Constants.TRANSPORTER_KEY, Constants.DEFAULT_TRANSPORTER);
+ final String serialization = PerformanceUtils.getProperty(Constants.SERIALIZATION_KEY, Constants.DEFAULT_REMOTING_SERIALIZATION);
+ final String threadpool = PerformanceUtils.getProperty(Constants.THREADPOOL_KEY, Constants.DEFAULT_THREADPOOL);
+ final int threads = PerformanceUtils.getIntProperty(Constants.THREADS_KEY, Constants.DEFAULT_THREADS);
+ final int iothreads = PerformanceUtils.getIntProperty(Constants.IO_THREADS_KEY, Constants.DEFAULT_IO_THREADS);
+ final int buffer = PerformanceUtils.getIntProperty(Constants.BUFFER_KEY, Constants.DEFAULT_BUFFER_SIZE);
+ final String channelHandler = PerformanceUtils.getProperty(Constants.CHANNEL_HANDLER_KEY, ExecutionChannelHandlerWrapper.NAME);
+
+
+ // 启动服务器
+ ExchangeServer server = Exchangers.bind("exchange://0.0.0.0:" + port + "?transporter="
+ + transporter + "&serialization="
+ + serialization + "&threadpool=" + threadpool
+ + "&threads=" + threads + "&iothreads=" + iothreads +"&buffer="+buffer +"&channel.handler="+channelHandler, new ExchangeHandlerAdapter() {
+ public String telnet(Channel channel, String message) throws RemotingException {
+ return "echo: " + message + "\r\ntelnet> ";
+ }
+ public Object reply(ExchangeChannel channel, Object request) throws RemotingException {
+ if ("environment".equals(request)) {
+ return PerformanceUtils.getEnvironment();
+ }
+ if ("scene".equals(request)) {
+ List<String> scene = new ArrayList<String>();
+ scene.add("Transporter: " + transporter);
+ scene.add("Service Threads: " + threads);
+ return scene;
+ }
+ return request;
+ }
+ });
+
+ return server;
+ }
+
+ private static ExchangeServer statTelnetServer(int port) throws Exception{
+ // 启动服务器
+ ExchangeServer telnetserver = Exchangers.bind("exchange://0.0.0.0:" + port , new ExchangeHandlerAdapter() {
+ public String telnet(Channel channel, String message) throws RemotingException {
+ if(message.equals("help")){
+ return "support cmd: \r\n\tstart \r\n\tstop \r\n\tshutdown \r\n\trestart times [alive] [sleep] \r\ntelnet>";
+ } else if(message.equals("stop")){
+ logger.info("server closed:"+server);
+ server.close();
+ return "stop server\r\ntelnet>";
+ } else if(message.startsWith("start")){
+ try {
+ restartServer(0,0,0);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return "start server\r\ntelnet>";
+ } else if(message.startsWith("shutdown")){
+ System.exit(0);
+ return "start server\r\ntelnet>";
+ } else if(message.startsWith("channels")){
+ return "server.getExchangeChannels():"+server.getExchangeChannels().size()+"\r\ntelnet>";
+ } else if(message.startsWith("restart ")){ //r times [sleep] r 10 or r 10 100
+ String[] args = message.split(" ");
+ int times = Integer.parseInt(args[1]);
+ int alive = args.length>2?Integer.parseInt(args[2]):0;
+ int sleep = args.length>3?Integer.parseInt(args[3]):100;
+ try {
+ restartServer(times,alive,sleep);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+
+ return "restart server,times:"+times+" stop alive time: "+alive+",sleep time: " + sleep+" usage:r times [alive] [sleep] \r\ntelnet>";
+ } else{
+ return "echo: " + message + "\r\ntelnet> ";
+ }
+
+ }
+ });
+
+ return telnetserver;
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/test/java/com/alibaba/dubbo/remoting/PerformanceUtils.java b/dubbo-remoting/src/test/java/com/alibaba/dubbo/remoting/PerformanceUtils.java
new file mode 100644
index 0000000..f74cc8c
--- /dev/null
+++ b/dubbo-remoting/src/test/java/com/alibaba/dubbo/remoting/PerformanceUtils.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting;
+
+import java.net.NetworkInterface;
+import java.net.SocketException;
+import java.text.DecimalFormat;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.List;
+
+/**
+ * PerformanceUtils
+ *
+ * @author william.liangf
+ */
+public class PerformanceUtils {
+
+ public static String getProperty(String key, String defaultValue) {
+ String value = System.getProperty(key);
+ if (value == null || value.trim().length() == 0 || value.startsWith("$")) {
+ return defaultValue;
+ }
+ return value.trim();
+ }
+
+ public static int getIntProperty(String key, int defaultValue) {
+ String value = System.getProperty(key);
+ if (value == null || value.trim().length() == 0 || value.startsWith("$")) {
+ return defaultValue;
+ }
+ return Integer.parseInt(value.trim());
+ }
+ public static boolean getBooleanProperty(String key, boolean defaultValue) {
+ String value = System.getProperty(key);
+ if (value == null || value.trim().length() == 0 || value.startsWith("$")) {
+ return defaultValue;
+ }
+ return Boolean.parseBoolean(value.trim());
+ }
+
+ public static List<String> getEnvironment() {
+ List<String> environment = new ArrayList<String>();
+ environment.add("OS: " + System.getProperty("os.name") + " " + System.getProperty("os.version") + " " + System.getProperty("os.arch", ""));
+ environment.add("CPU: " + Runtime.getRuntime().availableProcessors() + " cores");
+ environment.add("JVM: " + System.getProperty("java.vm.name") + " " + System.getProperty("java.runtime.version"));
+ environment.add("Memory: " + DecimalFormat.getIntegerInstance().format(Runtime.getRuntime().totalMemory())
+ + " bytes (Max: " + DecimalFormat.getIntegerInstance().format(Runtime.getRuntime().maxMemory()) + " bytes)");
+ NetworkInterface ni = PerformanceUtils.getNetworkInterface();
+ if (ni != null) {
+ environment.add("Network: " + ni.getDisplayName());
+ }
+ return environment;
+ }
+
+ private static final int WIDTH = 64;
+
+ public static void printSeparator() {
+ StringBuilder pad = new StringBuilder();
+ for (int i = 0; i < WIDTH; i ++) {
+ pad.append("-");
+ }
+ System.out.println("+" + pad + "+");
+ }
+
+ public static void printBorder() {
+ StringBuilder pad = new StringBuilder();
+ for (int i = 0; i < WIDTH; i ++) {
+ pad.append("=");
+ }
+ System.out.println("+" + pad + "+");
+ }
+
+ public static void printBody(String msg) {
+ StringBuilder pad = new StringBuilder();
+ int len = WIDTH - msg.length() - 1;
+ if (len > 0) {
+ for (int i = 0; i < len; i ++) {
+ pad.append(" ");
+ }
+ }
+ System.out.println("| " + msg + pad + "|");
+ }
+
+ public static void printHeader(String msg) {
+ StringBuilder pad = new StringBuilder();
+ int len = WIDTH - msg.length();
+ if (len > 0) {
+ int half = len / 2;
+ for (int i = 0; i < half; i ++) {
+ pad.append(" ");
+ }
+ }
+ System.out.println("|" + pad + msg + pad + ((len % 2 == 0) ? "" : " ") + "|");
+ }
+
+ public static NetworkInterface getNetworkInterface() {
+ try {
+ Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces();
+ if (interfaces != null) {
+ while (interfaces.hasMoreElements()) {
+ try {
+ return interfaces.nextElement();
+ } catch (Throwable e) {
+ }
+ }
+ }
+ } catch (SocketException e) {
+ }
+ return null;
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/test/java/com/alibaba/dubbo/remoting/RpcMessage.java b/dubbo-remoting/src/test/java/com/alibaba/dubbo/remoting/RpcMessage.java
new file mode 100644
index 0000000..8ab2c36
--- /dev/null
+++ b/dubbo-remoting/src/test/java/com/alibaba/dubbo/remoting/RpcMessage.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting;
+
+import java.io.Serializable;
+
+/**
+ * RpcMessage.
+ *
+ * @author qian.lei
+ */
+
+public class RpcMessage implements Serializable
+{
+ private static final long serialVersionUID = -5148079121106659095L;
+
+ private String mClassName;
+
+ private String mMethodDesc;
+
+ private Class<?>[] mParameterTypes;
+
+ private Object[] mArguments;
+
+ public RpcMessage(String cn, String desc, Class<?>[] parameterTypes,Object[] args)
+ {
+ mClassName = cn;
+ mMethodDesc = desc;
+ mParameterTypes = parameterTypes;
+ mArguments = args;
+ }
+
+ public String getClassName()
+ {
+ return mClassName;
+ }
+
+ public String getMethodDesc()
+ {
+ return mMethodDesc;
+ }
+
+ public Class<?>[] getParameterTypes() {
+ return mParameterTypes;
+ }
+
+ public Object[] getArguments()
+ {
+ return mArguments;
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/test/java/com/alibaba/dubbo/remoting/RpcMessageHandler.java b/dubbo-remoting/src/test/java/com/alibaba/dubbo/remoting/RpcMessageHandler.java
new file mode 100644
index 0000000..0a12593
--- /dev/null
+++ b/dubbo-remoting/src/test/java/com/alibaba/dubbo/remoting/RpcMessageHandler.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting;
+
+import java.lang.reflect.InvocationTargetException;
+
+import com.alibaba.dubbo.common.bytecode.NoSuchMethodException;
+import com.alibaba.dubbo.common.bytecode.Wrapper;
+import com.alibaba.dubbo.remoting.exchange.ExchangeChannel;
+import com.alibaba.dubbo.remoting.exchange.support.Replier;
+
+/**
+ * RpcMessageHandler.
+ *
+ * @author qian.lei
+ */
+
+public class RpcMessageHandler implements Replier<RpcMessage>
+{
+ public static interface ServiceProvider{ Object getImplementation(String service); }
+
+ private final static ServiceProvider DEFAULT_PROVIDER = new ServiceProvider(){
+ public Object getImplementation(String service)
+ {
+ String impl = service + "Impl";
+ try
+ {
+ Class<?> cl = Thread.currentThread().getContextClassLoader().loadClass(impl);
+ return cl.newInstance();
+ }
+ catch(Exception e)
+ {
+ e.printStackTrace();
+ }
+ return null;
+ }
+ };
+
+ private ServiceProvider mProvider;
+
+ public RpcMessageHandler()
+ {
+ this(DEFAULT_PROVIDER);
+ }
+
+ public RpcMessageHandler(ServiceProvider prov)
+ {
+ mProvider = prov;
+ }
+
+ public Class<RpcMessage> interest()
+ {
+ return RpcMessage.class;
+ }
+
+ public Object reply(ExchangeChannel channel, RpcMessage msg) throws RemotingException
+ {
+ String desc = msg.getMethodDesc();
+ Object[] args = msg.getArguments();
+ Object impl = mProvider.getImplementation(msg.getClassName());
+ Wrapper wrap = Wrapper.getWrapper(impl.getClass());
+ try
+ {
+ return new MockResult(wrap.invokeMethod(impl, desc, msg.getParameterTypes(), args));
+ }
+ catch(NoSuchMethodException e)
+ {
+ throw new RemotingException(channel, "Service method not found.");
+ }
+ catch(InvocationTargetException e)
+ {
+ return new MockResult(e.getTargetException());
+ }
+
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/test/java/com/alibaba/dubbo/remoting/TelnetServer.java b/dubbo-remoting/src/test/java/com/alibaba/dubbo/remoting/TelnetServer.java
new file mode 100644
index 0000000..637c9ce
--- /dev/null
+++ b/dubbo-remoting/src/test/java/com/alibaba/dubbo/remoting/TelnetServer.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting;
+
+import com.alibaba.dubbo.remoting.transport.ChannelHandlerAdapter;
+
+/**
+ * TelnetServer
+ *
+ * @author william.liangf
+ */
+public class TelnetServer {
+
+ public static void main(String[] args) throws Exception {
+ Transporters.bind("telnet://0.0.0.0:23", new ChannelHandlerAdapter() {
+ public void connected(Channel channel) throws RemotingException {
+ channel.send("telnet> ");
+ }
+ public void received(Channel channel, Object message) throws RemotingException {
+ channel.send("Echo: " + message + "\r\n");
+ channel.send("telnet> ");
+ }
+ });
+ // 阻止JVM退出
+ synchronized (TelnetServer.class) {
+ while (true) {
+ try {
+ TelnetServer.class.wait();
+ } catch (InterruptedException e) {
+ }
+ }
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/test/java/com/alibaba/dubbo/remoting/codec/AbstractMockChannel.java b/dubbo-remoting/src/test/java/com/alibaba/dubbo/remoting/codec/AbstractMockChannel.java
new file mode 100644
index 0000000..325a50c
--- /dev/null
+++ b/dubbo-remoting/src/test/java/com/alibaba/dubbo/remoting/codec/AbstractMockChannel.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting.codec;
+
+import java.net.InetSocketAddress;
+import java.util.HashMap;
+import java.util.Map;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.utils.NetUtils;
+import com.alibaba.dubbo.remoting.Channel;
+import com.alibaba.dubbo.remoting.ChannelHandler;
+import com.alibaba.dubbo.remoting.RemotingException;
+
+/**
+ * @author chao.liuc
+ *
+ */
+public class AbstractMockChannel implements Channel {
+ public static final String LOCAL_ADDRESS = "local";
+ public static final String REMOTE_ADDRESS = "remote";
+ public static final String ERROR_WHEN_SEND = "error_when_send";
+ private URL remoteUrl ;
+ InetSocketAddress localAddress ;
+ InetSocketAddress remoteAddress ;
+ private ChannelHandler handler;
+ private boolean isClosed ;
+ private Map<String, Object> attributes = new HashMap<String, Object>(1);
+ private volatile Object receivedMessage = null;
+
+ public AbstractMockChannel(){
+
+ }
+
+ public AbstractMockChannel(URL remoteUrl){
+ this.remoteUrl = remoteUrl;
+ this.remoteAddress = NetUtils.toAddress(remoteUrl.getParameter(REMOTE_ADDRESS));
+ this.localAddress = NetUtils.toAddress(remoteUrl.getParameter(LOCAL_ADDRESS));
+ }
+ public AbstractMockChannel(ChannelHandler handler){
+ this.handler = handler;
+ }
+
+ public URL getUrl() {
+ return remoteUrl;
+ }
+
+ public ChannelHandler getChannelHandler() {
+ return handler;
+ }
+
+ public InetSocketAddress getLocalAddress() {
+ return localAddress ;
+ }
+
+ public void send(Object message) throws RemotingException {
+ if (remoteUrl.getParameter(ERROR_WHEN_SEND, Boolean.FALSE)){
+ receivedMessage = null ;
+ throw new RemotingException(localAddress, remoteAddress, "mock error");
+ } else {
+ receivedMessage = message;
+ }
+ }
+
+ public void send(Object message, boolean sent) throws RemotingException {
+ send(message);
+ }
+
+ public void close() {
+ close(0);
+ }
+
+ public void close(int timeout) {
+ isClosed = true;
+ }
+
+ public boolean isClosed() {
+ return isClosed;
+ }
+
+ public InetSocketAddress getRemoteAddress() {
+ return remoteAddress;
+ }
+
+ public boolean isConnected() {
+ return isClosed;
+ }
+
+ public boolean hasAttribute(String key) {
+ return attributes.containsKey(key);
+ }
+
+ public Object getAttribute(String key) {
+ return attributes.get(key);
+ }
+
+ public void setAttribute(String key, Object value) {
+ attributes.put(key, value);
+ }
+
+ public void removeAttribute(String key) {
+ attributes.remove(key);
+ }
+
+ public Object getReceivedMessage() {
+ return receivedMessage;
+ }
+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/test/java/com/alibaba/dubbo/remoting/codec/ExchangeCodecTest.java b/dubbo-remoting/src/test/java/com/alibaba/dubbo/remoting/codec/ExchangeCodecTest.java
new file mode 100644
index 0000000..7d4a589
--- /dev/null
+++ b/dubbo-remoting/src/test/java/com/alibaba/dubbo/remoting/codec/ExchangeCodecTest.java
@@ -0,0 +1,357 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting.codec;
+
+
+import static org.junit.Assert.fail;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.HashMap;
+
+import junit.framework.Assert;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.ExtensionLoader;
+import com.alibaba.dubbo.common.io.Bytes;
+import com.alibaba.dubbo.common.io.UnsafeByteArrayInputStream;
+import com.alibaba.dubbo.common.io.UnsafeByteArrayOutputStream;
+import com.alibaba.dubbo.common.serialize.ObjectOutput;
+import com.alibaba.dubbo.common.serialize.Serialization;
+import com.alibaba.dubbo.remoting.Channel;
+import com.alibaba.dubbo.remoting.exchange.Request;
+import com.alibaba.dubbo.remoting.exchange.Response;
+import com.alibaba.dubbo.remoting.exchange.codec.ExchangeCodec;
+import com.alibaba.dubbo.remoting.telnet.codec.TelnetCodec;
+
+/**
+ * @author chao.liuc
+ * byte 16
+ * 0-1 magic code
+ * 2 flag
+ * 8 - 1-request/0-response
+ * 7 - two way
+ * 6 - heartbeat
+ * 1-5 serialization id
+ * 3 status
+ * 20 ok
+ * 90 error?
+ * 4-11 id (long)
+ * 12 -15 datalength
+ *
+ */
+public class ExchangeCodecTest extends TelnetCodecTest{
+ // magic header.
+ private static final short MAGIC = (short) 0xdabb;
+ private static final byte MAGIC_HIGH = (byte) Bytes.short2bytes(MAGIC)[0];
+ private static final byte MAGIC_LOW = (byte) Bytes.short2bytes(MAGIC)[1];
+ Serialization serialization = getSerialization(Constants.DEFAULT_REMOTING_SERIALIZATION);
+
+
+
+ private Object decode(byte[] request) throws IOException{
+ InputStream input = new UnsafeByteArrayInputStream(request);
+ AbstractMockChannel channel = getServerSideChannel(url);
+ //decode
+ Object obj = codec.decode(channel, input);
+ return obj;
+ }
+
+ private byte[] getRequestBytes(Object obj, byte[] header) throws IOException{
+ // encode request data.
+ UnsafeByteArrayOutputStream bos = new UnsafeByteArrayOutputStream(1024);
+ ObjectOutput out = serialization.serialize(url, bos);
+ out.writeObject(obj);
+
+ out.flushBuffer();
+ bos.flush();
+ bos.close();
+ byte[] data = bos.toByteArray();
+ byte[] len = Bytes.int2bytes(data.length);
+ System.arraycopy(len, 0, header, 12, 4);
+ byte[] request = join(header, data);
+ return request;
+ }
+
+ private byte[] assemblyDataProtocol(byte[] header){
+ Person request = new Person();
+ byte[] newbuf = join(header, objectToByte(request));
+ return newbuf;
+ }
+
+ private static Serialization getSerialization(String name) {
+ Serialization serialization = ExtensionLoader.getExtensionLoader(Serialization.class).getExtension(name);
+ return serialization;
+ }
+ //===================================================================================
+
+ @Before
+ public void setUp() throws Exception {
+ codec = new ExchangeCodec();
+ }
+ @Test
+ public void test_Decode_Error_MagicNum() throws IOException{
+ HashMap<byte[] , Object> inputBytes = new HashMap<byte[] , Object>();
+ inputBytes.put( new byte[] { 0 }, TelnetCodec.NEED_MORE_INPUT );
+ inputBytes.put( new byte[] { MAGIC_HIGH, 0 }, TelnetCodec.NEED_MORE_INPUT );
+ inputBytes.put( new byte[] { 0 , MAGIC_LOW }, TelnetCodec.NEED_MORE_INPUT );
+
+ for (byte[] input : inputBytes.keySet()){
+ testDecode_assertEquals(assemblyDataProtocol(input) ,inputBytes.get(input));
+ }
+ }
+
+ @Test
+ public void test_Decode_Error_Length() throws IOException{
+ byte[] header = new byte[] { MAGIC_HIGH, MAGIC_LOW, 0x20, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+ Person person = new Person();
+ byte[] request = getRequestBytes(person, header);
+
+ Channel channel = getServerSideChannel(url);
+ byte[] baddata = new byte[]{1,2};
+ UnsafeByteArrayInputStream input = new UnsafeByteArrayInputStream(join(request, baddata));
+ Response obj = (Response)codec.decode(channel, input);
+ Assert.assertEquals(person, obj.getResult());
+ System.out.println(input.available());
+ //only decode necessary bytes
+ Assert.assertEquals(request.length, input.position());
+ }
+ @Test
+ public void test_Decode_Error_Response_Object() throws IOException{
+ //00000010-response/oneway/hearbeat=true |20-stats=ok|id=0|length=0
+ byte[] header = new byte[] { MAGIC_HIGH, MAGIC_LOW, 0x20, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+ Person person = new Person();
+ byte[] request = getRequestBytes(person, header);
+ //bad object
+ byte[] badbytes = new byte[]{-1,-2,-3,-4,-3,-4,-3,-4,-3,-4,-3,-4};
+ System.arraycopy(badbytes, 0, request, 21, badbytes.length);
+
+ Response obj = (Response)decode(request);
+ Assert.assertEquals(90, obj.getStatus());
+ }
+ @Test
+ public void test_Decode_Check_Payload() throws IOException{
+ byte[] header = new byte[] { MAGIC_HIGH , MAGIC_LOW , 1 ,1 ,1 ,1 ,1 , 1 ,1 ,1 ,1 ,1 , 1 ,1 , 1,1 };
+ byte[] request = assemblyDataProtocol(header) ;
+ try{
+ testDecode_assertEquals(request, TelnetCodec.NEED_MORE_INPUT);
+ fail();
+ }catch (IOException expected) {
+ Assert.assertTrue(expected.getMessage().startsWith("Data length too large: "+Bytes.bytes2int(new byte[]{1,1,1,1})));
+ }
+ }
+ @Test
+ public void test_Decode_Header_Need_Readmore() throws IOException{
+ byte[] header = new byte[] { MAGIC_HIGH , MAGIC_LOW , 0 ,0 ,0 ,0 ,0 , 0 ,0 ,0 ,0 };
+ testDecode_assertEquals(header, TelnetCodec.NEED_MORE_INPUT);
+ }
+
+ @Test
+ public void test_Decode_Body_Need_Readmore() throws IOException{
+ byte[] header = new byte[] { MAGIC_HIGH , MAGIC_LOW , 0 ,0 ,0 ,0 ,0 , 0 ,0 ,0 ,0, 0, 0, 0 , 1 ,1, 'a', 'a' };
+ testDecode_assertEquals(header, TelnetCodec.NEED_MORE_INPUT);
+ }
+ @Test
+ public void test_Decode_MigicCodec_Contain_ExchangeHeader() throws IOException{
+ //
+ byte[] header = new byte[] { 0, 0, MAGIC_HIGH , MAGIC_LOW , 0 ,0 ,0 ,0 ,0 , 0 ,0 ,0 ,0 };
+
+ Channel channel = getServerSideChannel(url);
+ UnsafeByteArrayInputStream input = new UnsafeByteArrayInputStream(header);
+ Object obj = codec.decode(channel, input);
+ Assert.assertEquals(TelnetCodec.NEED_MORE_INPUT, obj);
+ //如果telnet数据与request数据在同一个数据包中,不能因为telnet没有结尾字符而影响其他数据的接收.
+ Assert.assertEquals(2, input.position());
+ }
+
+ @Test
+ public void test_Decode_Return_Response_Person() throws IOException{
+ //00000010-response/oneway/hearbeat=false/hessian |20-stats=ok|id=0|length=0
+ byte[] header = new byte[] { MAGIC_HIGH, MAGIC_LOW, 2, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+ Person person = new Person();
+ byte[] request = getRequestBytes(person, header);
+
+ Response obj = (Response)decode(request);
+ Assert.assertEquals(20, obj.getStatus());
+ Assert.assertEquals(person, obj.getResult());
+ System.out.println(obj);
+ }
+
+ @Test //status输入有问题,序列化时读取信息出错.
+ public void test_Decode_Return_Response_Error() throws IOException{
+ byte[] header = new byte[] { MAGIC_HIGH, MAGIC_LOW, 2, 90, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+ String errorString = "encode request data error ";
+ byte[] request = getRequestBytes(errorString, header);
+ Response obj = (Response)decode(request);
+ Assert.assertEquals(90, obj.getStatus());
+ Assert.assertEquals(errorString, obj.getErrorMessage());
+ }
+ @Test
+ public void test_Decode_Return_Request_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 };
+ 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.isHeartbeat());
+ Assert.assertEquals("2.0.0", obj.getVersion());
+ System.out.println(obj);
+ }
+ @Test
+ public void test_Decode_Return_Request_Object() throws IOException{
+ //|10011111|20-stats=ok|id=0|length=0
+ byte[] header = new byte[] { MAGIC_HIGH, MAGIC_LOW, (byte) 0xdf, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+ Person person = new Person();
+ byte[] request = getRequestBytes(person, header);
+
+ Request obj = (Request)decode(request);
+ Assert.assertEquals(person, obj.getData());
+ Assert.assertEquals(true, obj.isTwoWay());
+ Assert.assertEquals(false, obj.isHeartbeat());
+ Assert.assertEquals("2.0.0", obj.getVersion());
+ System.out.println(obj);
+ }
+
+ @Test
+ public void test_Decode_Error_Request_Object() throws IOException{
+ //00000010-response/oneway/hearbeat=true |20-stats=ok|id=0|length=0
+ byte[] header = new byte[] { MAGIC_HIGH, MAGIC_LOW, (byte)0xdf, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+ Person person = new Person();
+ byte[] request = getRequestBytes(person, header);
+ //bad object
+ byte[] badbytes = new byte[]{-1,-2,-3,-4,-3,-4,-3,-4,-3,-4,-3,-4};
+ System.arraycopy(badbytes, 0, request, 21, badbytes.length);
+
+ Request obj = (Request)decode(request);
+ Assert.assertEquals(true, obj.isBroken());
+ Assert.assertEquals(true, obj.getData() instanceof Throwable);
+ }
+
+ @Test
+ public void test_Header_Response_NoSerializationFlag() throws IOException{
+ //00000010-response/oneway/hearbeat=false/noset |20-stats=ok|id=0|length=0
+ byte[] header = new byte[] { MAGIC_HIGH, MAGIC_LOW, 0, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+ Person person = new Person();
+ byte[] request = getRequestBytes(person, header);
+
+ Response obj = (Response)decode(request);
+ Assert.assertEquals(20, obj.getStatus());
+ Assert.assertEquals(person, obj.getResult());
+ System.out.println(obj);
+ }
+
+ @Test
+ public void test_Header_Response_Heartbeat() throws IOException{
+ //00000010-response/oneway/hearbeat=true |20-stats=ok|id=0|length=0
+ byte[] header = new byte[] { MAGIC_HIGH, MAGIC_LOW, 0x20, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+ Person person = new Person();
+ byte[] request = getRequestBytes(person, header);
+
+ Response obj = (Response)decode(request);
+ Assert.assertEquals(20, obj.getStatus());
+ Assert.assertEquals(person, obj.getResult());
+ System.out.println(obj);
+ }
+
+ @Test
+ public void test_Encode_Request() throws IOException{
+ UnsafeByteArrayOutputStream bos = new UnsafeByteArrayOutputStream(1024);
+ Channel channel = getCliendSideChannel(url);
+ Request request = new Request();
+ Person person = new Person();
+ request.setData(person);
+
+ codec.encode(channel, bos, request);
+ byte[] data = bos.toByteArray();
+ bos.flush();
+ bos.close();
+
+ //encode resault check need decode
+ InputStream input = new UnsafeByteArrayInputStream(data);
+ Request obj = (Request)codec.decode(channel, input);
+ Assert.assertEquals(request.isBroken(), obj.isBroken());
+ Assert.assertEquals(request.isHeartbeat(), obj.isHeartbeat());
+ Assert.assertEquals(request.isTwoWay(), obj.isTwoWay());
+ Assert.assertEquals(person, obj.getData());
+ }
+
+ @Test
+ public void test_Encode_Response() throws IOException{
+ UnsafeByteArrayOutputStream bos = new UnsafeByteArrayOutputStream(1024);
+ Channel channel = getCliendSideChannel(url);
+ Response response = new Response();
+ response.setHeartbeat(true);
+ response.setId(1001l);
+ response.setStatus((byte)20 );
+ response.setVersion("11");
+ Person person = new Person();
+ response.setResult(person);
+
+ codec.encode(channel, bos, response);
+ byte[] data = bos.toByteArray();
+ bos.flush();
+ bos.close();
+
+ //encode resault check need decode
+ InputStream input = new UnsafeByteArrayInputStream(data);
+ Response obj = (Response)codec.decode(channel, input);
+
+ Assert.assertEquals(response.getId(), obj.getId());
+ Assert.assertEquals(response.getStatus(), obj.getStatus());
+ Assert.assertEquals(response.isHeartbeat(), obj.isHeartbeat());
+ Assert.assertEquals(person, obj.getResult());
+ // encode response verson ??
+// Assert.assertEquals(response.getVersion(), obj.getVersion());
+
+ }
+
+ @Test
+ public void test_Encode_Error_Response() throws IOException{
+ UnsafeByteArrayOutputStream bos = new UnsafeByteArrayOutputStream(1024);
+ Channel channel = getCliendSideChannel(url);
+ Response response = new Response();
+ response.setHeartbeat(true);
+ response.setId(1001l);
+ response.setStatus((byte)10 );
+ response.setVersion("11");
+ String badString = "bad" ;
+ response.setErrorMessage(badString);
+ Person person = new Person();
+ response.setResult(person);
+
+ codec.encode(channel, bos, response);
+ byte[] data = bos.toByteArray();
+ bos.flush();
+ bos.close();
+
+ //encode resault check need decode
+ InputStream input = new UnsafeByteArrayInputStream(data);
+ Response obj = (Response)codec.decode(channel, input);
+ Assert.assertEquals(response.getId(), obj.getId());
+ Assert.assertEquals(response.getStatus(), obj.getStatus());
+ Assert.assertEquals(response.isHeartbeat(), obj.isHeartbeat());
+ Assert.assertEquals(badString, obj.getErrorMessage());
+ Assert.assertEquals(null, obj.getResult());
+// Assert.assertEquals(response.getVersion(), obj.getVersion());
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/test/java/com/alibaba/dubbo/remoting/codec/TelnetCodecTest.java b/dubbo-remoting/src/test/java/com/alibaba/dubbo/remoting/codec/TelnetCodecTest.java
new file mode 100644
index 0000000..f34c2a9
--- /dev/null
+++ b/dubbo-remoting/src/test/java/com/alibaba/dubbo/remoting/codec/TelnetCodecTest.java
@@ -0,0 +1,382 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting.codec;
+
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
+import java.util.HashMap;
+
+import junit.framework.Assert;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.io.UnsafeByteArrayInputStream;
+import com.alibaba.dubbo.common.io.UnsafeByteArrayOutputStream;
+import com.alibaba.dubbo.remoting.Channel;
+import com.alibaba.dubbo.remoting.Codec;
+import com.alibaba.dubbo.remoting.telnet.codec.TelnetCodec;
+
+/**
+ * @author chao.liuc
+ *
+ */
+public class TelnetCodecTest {
+ protected Codec codec ;
+ byte[] UP = new byte[] {27, 91, 65};
+ byte[] DOWN = new byte[] {27, 91, 66};
+ /**
+ * @throws java.lang.Exception
+ */
+ @Before
+ public void setUp() throws Exception {
+ codec = new TelnetCodec();
+ }
+
+ protected AbstractMockChannel getServerSideChannel(URL url){
+ url = url.addParameter(AbstractMockChannel.LOCAL_ADDRESS, url.getAddress())
+ .addParameter(AbstractMockChannel.REMOTE_ADDRESS, "127.0.0.1:12345");
+ AbstractMockChannel channel = new AbstractMockChannel(url);
+ return channel;
+ }
+ protected AbstractMockChannel getCliendSideChannel(URL url){
+ url = url.addParameter(AbstractMockChannel.LOCAL_ADDRESS, "127.0.0.1:12345")
+ .addParameter(AbstractMockChannel.REMOTE_ADDRESS, url.getAddress());
+ AbstractMockChannel channel = new AbstractMockChannel(url);
+ return channel;
+ }
+
+ protected byte[] join(byte[] in1, byte[] in2){
+ byte[] ret = new byte[in1.length + in2.length];
+ System.arraycopy(in1, 0, ret, 0, in1.length);
+ System.arraycopy(in2, 0, ret, in1.length, in2.length);
+ return ret;
+ }
+
+ protected byte[] objectToByte(Object obj){
+ byte[] bytes;
+ if (obj instanceof String){
+ bytes = ((String)obj).getBytes();
+ } else if (obj instanceof byte[]){
+ bytes = (byte[]) obj;
+ } else {
+ try {
+ //object to bytearray
+ ByteArrayOutputStream bo = new ByteArrayOutputStream();
+ ObjectOutputStream oo = new ObjectOutputStream(bo);
+ oo.writeObject(obj);
+ bytes = bo.toByteArray();
+ bo.close();
+ oo.close();
+ }
+ catch(Exception e){
+ throw new RuntimeException(e);
+ }
+ }
+ return(bytes);
+ }
+
+ protected Object byteToObject(byte[] objBytes) throws Exception {
+ if (objBytes == null || objBytes.length == 0) {
+ return null;
+ }
+ ByteArrayInputStream bi = new ByteArrayInputStream(objBytes);
+ ObjectInputStream oi = new ObjectInputStream(bi);
+ return oi.readObject();
+ }
+
+ //======================================================
+ public static class Person implements Serializable{
+ private static final long serialVersionUID = 3362088148941547337L;
+ public String name ;
+ public String sex ;
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((name == null) ? 0 : name.hashCode());
+ result = prime * result + ((sex == null) ? 0 : sex.hashCode());
+ return result;
+ }
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ Person other = (Person) obj;
+ if (name == null) {
+ if (other.name != null)
+ return false;
+ } else if (!name.equals(other.name))
+ return false;
+ if (sex == null) {
+ if (other.sex != null)
+ return false;
+ } else if (!sex.equals(other.sex))
+ return false;
+ return true;
+ }
+
+ }
+
+ protected void testDecode_assertEquals(byte[] request,Object ret) throws IOException{
+ testDecode_assertEquals(request, ret, true);
+ }
+
+ protected void testDecode_assertEquals(byte[] request,Object ret, boolean isServerside) throws IOException{
+ //init channel
+ Channel channel = isServerside? getServerSideChannel(url) : getCliendSideChannel(url);
+ //init request string
+ InputStream input = new UnsafeByteArrayInputStream(request);
+
+ //decode
+ Object obj = codec.decode(channel, input);
+ Assert.assertEquals(ret, obj);
+ }
+
+
+
+ protected void testEecode_assertEquals(Object request,byte[] ret, boolean isServerside) throws IOException{
+ //init channel
+ Channel channel = isServerside? getServerSideChannel(url) : getCliendSideChannel(url);
+
+ UnsafeByteArrayOutputStream bos = new UnsafeByteArrayOutputStream(1024);
+
+ codec.encode(channel, bos, request);
+ bos.flush();
+ bos.close();
+ InputStream is = new ByteArrayInputStream(bos.toByteArray());
+ byte[] data = new byte[is.available()];
+ is.read(data);
+
+ Assert.assertEquals(ret.length, data.length);
+ for(int i=0;i<ret.length;i++){
+ if (ret[i] != data[i]){
+ Assert.fail();
+ }
+ }
+ }
+
+ protected void testDecode_assertEquals(Object request,Object ret) throws IOException{
+ testDecode_assertEquals(request, ret, null);
+ }
+
+ private void testDecode_assertEquals(Object request,Object ret, Object channelReceive) throws IOException{
+ testDecode_assertEquals(null, request, ret, channelReceive);
+ }
+
+ private void testDecode_assertEquals(AbstractMockChannel channel, Object request,Object expectret, Object channelReceive) throws IOException{
+ //init channel
+ if (channel == null){
+ channel = getServerSideChannel(url);
+ }
+
+ byte[] buf = objectToByte(request);
+ InputStream input = new UnsafeByteArrayInputStream(buf);
+
+ //decode
+ Object obj = codec.decode(channel, input);
+ Assert.assertEquals(expectret, obj);
+ Assert.assertEquals(channelReceive, channel.getReceivedMessage());
+ }
+
+ private void testDecode_PersonWithEnterByte(byte[] enterbytes ,boolean isNeedmore) throws IOException{
+ //init channel
+ Channel channel = getServerSideChannel(url);
+ //init request string
+ Person request = new Person();
+ byte[] newbuf = join(objectToByte(request),enterbytes);
+ InputStream input = new UnsafeByteArrayInputStream(newbuf);
+
+ //decode
+ Object obj = codec.decode(channel, input);
+ if (isNeedmore){
+ Assert.assertEquals(Codec.NEED_MORE_INPUT , obj);
+ }else {
+ Assert.assertTrue("return must string ", obj instanceof String);
+ }
+ }
+
+ private void testDecode_WithExitByte(byte[] exitbytes ,boolean isChannelClose) throws IOException{
+ //init channel
+ Channel channel = getServerSideChannel(url);
+ InputStream input = new UnsafeByteArrayInputStream(exitbytes);
+
+ //decode
+ codec.decode(channel, input);
+ Assert.assertEquals(isChannelClose, channel.isClosed());
+ }
+
+
+ //======================================================
+ URL url = URL.valueOf("dubbo://10.20.30.40:20880");
+
+ @Test
+ public void testDecode_String_ClientSide() throws IOException{
+ testDecode_assertEquals("aaa".getBytes(), "aaa",false);
+ }
+
+ @Test
+ public void testDecode_BlankMessage() throws IOException{
+ testDecode_assertEquals(new byte[]{}, Codec.NEED_MORE_INPUT);
+ }
+
+ @Test
+ public void testDecode_String_NoEnter() throws IOException{
+ testDecode_assertEquals("aaa", Codec.NEED_MORE_INPUT);
+ }
+
+ @Test
+ public void testDecode_String_WithEnter() throws IOException{
+ testDecode_assertEquals("aaa\n", "aaa");
+ }
+ @Test
+ public void testDecode_String_MiddleWithEnter() throws IOException{
+ testDecode_assertEquals("aaa\r\naaa", Codec.NEED_MORE_INPUT);
+ }
+
+ @Test
+ public void testDecode_Person_ObjectOnly() throws IOException{
+ testDecode_assertEquals(new Person(), Codec.NEED_MORE_INPUT);
+ }
+ @Test
+ public void testDecode_Person_WithEnter() throws IOException{
+ testDecode_PersonWithEnterByte(new byte[] { '\r', '\n' } , false);//windows end
+ testDecode_PersonWithEnterByte(new byte[] { '\n', '\r' } , true);
+ testDecode_PersonWithEnterByte(new byte[] { '\n' } , false); //linux end
+ testDecode_PersonWithEnterByte(new byte[] { '\r' } , true);
+ testDecode_PersonWithEnterByte(new byte[] { '\r', 100 } , true);
+ }
+ @Test
+ public void testDecode_WithExitByte() throws IOException{
+ HashMap<byte[] , Boolean> exitbytes = new HashMap<byte[] , Boolean>();
+ exitbytes.put( new byte[] { 3 }, true ); /* Windows Ctrl+C */
+ exitbytes.put( new byte[] { 1, 3 }, false ); //must equal the bytes
+ exitbytes.put( new byte[] { -1, -12, -1, -3, 6 }, true ); /* Linux Ctrl+C */
+ exitbytes.put( new byte[] {1, -1, -12, -1, -3, 6 }, false ); //must equal the bytes
+ exitbytes.put( new byte[] { -1, -19, -1, -3, 6 }, true ); /* Linux Pause */
+
+ for (byte[] exit : exitbytes.keySet()){
+ testDecode_WithExitByte(exit ,exitbytes.get(exit));
+ }
+ }
+ @Test
+ public void testDecode_Backspace() throws IOException{
+ //32 8 先加空格在补退格.
+ testDecode_assertEquals(new byte[]{'\b'}, Codec.NEED_MORE_INPUT, new String(new byte[] {32, 8}));
+
+ //测试中文
+ byte[] chineseBytes = "中".getBytes();
+ byte[] request = join(chineseBytes, new byte[]{'\b'});
+ testDecode_assertEquals(request, Codec.NEED_MORE_INPUT, new String(new byte[] {32, 32, 8, 8}));
+ //中文会带来此问题 (-数判断) 忽略此问题,退格键只有在真的telnet程序中才输入有意义.
+ testDecode_assertEquals(new byte[]{'a', 'x', -1, 'x', '\b'}, Codec.NEED_MORE_INPUT, new String(new byte[] {32, 32, 8, 8}));
+ }
+
+ @Test(expected = IOException.class)
+ public void testDecode_Backspace_WithError() throws IOException{
+ url = url.addParameter(AbstractMockChannel.ERROR_WHEN_SEND, Boolean.TRUE.toString());
+ testDecode_Backspace();
+ url = url.removeParameter(AbstractMockChannel.ERROR_WHEN_SEND);
+ }
+
+ @Test()
+ public void testDecode_History_UP() throws IOException{
+ //init channel
+ AbstractMockChannel channel = getServerSideChannel(url);
+
+ testDecode_assertEquals(channel, UP, Codec.NEED_MORE_INPUT, null);
+
+ String request1 = "aaa\n";
+ Object expected1 = "aaa";
+ //init history
+ testDecode_assertEquals(channel, request1, expected1, null);
+
+ testDecode_assertEquals(channel, UP, Codec.NEED_MORE_INPUT, expected1);
+ }
+
+ @Test(expected = IOException.class)
+ public void testDecode_UPorDOWN_WithError() throws IOException{
+ url = url.addParameter(AbstractMockChannel.ERROR_WHEN_SEND, Boolean.TRUE.toString());
+
+ //init channel
+ AbstractMockChannel channel = getServerSideChannel(url);
+
+ testDecode_assertEquals(channel, UP, Codec.NEED_MORE_INPUT, null);
+
+ String request1 = "aaa\n";
+ Object expected1 = "aaa";
+ //init history
+ testDecode_assertEquals(channel, request1, expected1, null);
+
+ testDecode_assertEquals(channel, UP, Codec.NEED_MORE_INPUT, expected1);
+
+ url = url.removeParameter(AbstractMockChannel.ERROR_WHEN_SEND);
+ }
+
+ @Test()
+ public void testDecode_History_UP_DOWN_MULTI() throws IOException{
+ AbstractMockChannel channel = getServerSideChannel(url);
+
+ String request1 = "aaa\n";
+ Object expected1 = request1.replace("\n", "");
+ //init history
+ testDecode_assertEquals(channel, request1, expected1, null);
+
+ String request2 = "bbb\n";
+ Object expected2 = request2.replace("\n", "");
+ //init history
+ testDecode_assertEquals(channel, request2, expected2, null);
+
+ String request3 = "ccc\n";
+ Object expected3= request3.replace("\n", "");
+ //init history
+ testDecode_assertEquals(channel, request3, expected3, null);
+
+ byte[] UP = new byte[] {27, 91, 65};
+ byte[] DOWN = new byte[] {27, 91, 66};
+ //history[aaa,bbb,ccc]
+ testDecode_assertEquals(channel, UP, Codec.NEED_MORE_INPUT, expected3);
+ testDecode_assertEquals(channel, DOWN, Codec.NEED_MORE_INPUT, expected3);
+ testDecode_assertEquals(channel, UP, Codec.NEED_MORE_INPUT, expected2);
+ testDecode_assertEquals(channel, UP, Codec.NEED_MORE_INPUT, expected1);
+ testDecode_assertEquals(channel, UP, Codec.NEED_MORE_INPUT, expected1);
+ testDecode_assertEquals(channel, UP, Codec.NEED_MORE_INPUT, expected1);
+ testDecode_assertEquals(channel, DOWN, Codec.NEED_MORE_INPUT, expected2);
+ testDecode_assertEquals(channel, DOWN, Codec.NEED_MORE_INPUT, expected3);
+ testDecode_assertEquals(channel, DOWN, Codec.NEED_MORE_INPUT, expected3);
+ testDecode_assertEquals(channel, DOWN, Codec.NEED_MORE_INPUT, expected3);
+ testDecode_assertEquals(channel, UP, Codec.NEED_MORE_INPUT, expected2);
+ }
+
+
+ //=============================================================================================================================
+ @Test
+ public void testEncode_String_ClientSide() throws IOException{
+ testEecode_assertEquals("aaa", "aaa\r\n".getBytes(), false);
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/test/java/com/alibaba/dubbo/remoting/handler/ConnectChannelHandlerTest.java b/dubbo-remoting/src/test/java/com/alibaba/dubbo/remoting/handler/ConnectChannelHandlerTest.java
new file mode 100644
index 0000000..de52f42
--- /dev/null
+++ b/dubbo-remoting/src/test/java/com/alibaba/dubbo/remoting/handler/ConnectChannelHandlerTest.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.handler;
+
+import java.util.concurrent.ThreadPoolExecutor;
+
+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.transport.handler.ConnectionOrderedChannelHandler;
+
+
+
+public class ConnectChannelHandlerTest extends WrappedChannelHandlerTest{
+
+ @Before
+ public void setUp() throws Exception {
+ handler = new ConnectionOrderedChannelHandler(new BizChannelHander(true), url);
+ }
+
+ @Test
+ public void test_Connect_Blocked() throws RemotingException{
+ handler = new ConnectionOrderedChannelHandler(new BizChannelHander(false), url);
+ ThreadPoolExecutor executor = (ThreadPoolExecutor)getField(handler, "connectionExecutor", 1);
+ Assert.assertEquals(1, executor.getMaximumPoolSize());
+
+ int runs = 20;
+ int taskCount = runs * 2;
+ for(int i=0; i<runs;i++){
+ handler.connected(new MockedChannel());
+ handler.disconnected(new MockedChannel());
+ Assert.assertTrue(executor.getActiveCount() + " must <=1" ,executor.getActiveCount() <= 1);
+ }
+ //quene.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(), "");
+ }
+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/test/java/com/alibaba/dubbo/remoting/handler/MockedChannel.java b/dubbo-remoting/src/test/java/com/alibaba/dubbo/remoting/handler/MockedChannel.java
new file mode 100644
index 0000000..eff3312
--- /dev/null
+++ b/dubbo-remoting/src/test/java/com/alibaba/dubbo/remoting/handler/MockedChannel.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.handler;
+
+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;
+
+/**
+ * @author chao.liuc
+ *
+ */
+public class MockedChannel implements Channel {
+ private boolean isClosed ;
+ private URL url;
+ private ChannelHandler handler ;
+
+ 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 false;
+ }
+
+ public Object getAttribute(String key) {
+
+ return null;
+ }
+
+ public void setAttribute(String key, Object value) {
+
+ }
+
+ public void removeAttribute(String key) {
+
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/test/java/com/alibaba/dubbo/remoting/handler/MockedChannelHandler.java b/dubbo-remoting/src/test/java/com/alibaba/dubbo/remoting/handler/MockedChannelHandler.java
new file mode 100644
index 0000000..08744da
--- /dev/null
+++ b/dubbo-remoting/src/test/java/com/alibaba/dubbo/remoting/handler/MockedChannelHandler.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting.handler;
+
+import java.util.Collections;
+import java.util.Set;
+
+import com.alibaba.dubbo.common.utils.ConcurrentHashSet;
+import com.alibaba.dubbo.remoting.Channel;
+import com.alibaba.dubbo.remoting.ChannelHandler;
+import com.alibaba.dubbo.remoting.RemotingException;
+
+/**
+ * @author chao.liuc
+ *
+ */
+public class MockedChannelHandler implements ChannelHandler {
+// ConcurrentMap<String, Channel> channels = new ConcurrentHashMap<String, Channel>();
+ ConcurrentHashSet<Channel> channels = new ConcurrentHashSet<Channel>();
+
+ public void connected(Channel channel) throws RemotingException {
+ channels.add(channel);
+ }
+
+ public void disconnected(Channel channel) throws RemotingException {
+ channels.remove(channel);
+ }
+
+ public void sent(Channel channel, Object message) throws RemotingException {
+ channel.send(message);
+ }
+
+ public void received(Channel channel, Object message) throws RemotingException {
+ //echo
+ channel.send(message);
+ }
+
+ public void caught(Channel channel, Throwable exception) throws RemotingException {
+ throw new RemotingException(channel, exception);
+
+ }
+ public Set<Channel> getChannels(){
+ return Collections.unmodifiableSet(channels);
+ }
+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/test/java/com/alibaba/dubbo/remoting/handler/WrappedChannelHandlerTest.java b/dubbo-remoting/src/test/java/com/alibaba/dubbo/remoting/handler/WrappedChannelHandlerTest.java
new file mode 100644
index 0000000..eb090e6
--- /dev/null
+++ b/dubbo-remoting/src/test/java/com/alibaba/dubbo/remoting/handler/WrappedChannelHandlerTest.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.remoting.handler;
+
+
+import static org.junit.Assert.*;
+
+import java.lang.reflect.Field;
+
+import junit.framework.Assert;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.remoting.Channel;
+import com.alibaba.dubbo.remoting.RemotingException;
+import com.alibaba.dubbo.remoting.transport.handler.WrappedChannelHandler;
+
+public class WrappedChannelHandlerTest {
+ WrappedChannelHandler handler ;
+ URL url = URL.valueOf("test://10.20.30.40:1234");
+
+ @Before
+ public void setUp() throws Exception {
+ handler = new WrappedChannelHandler(new BizChannelHander(true), url);
+ }
+
+ @Test
+ public void test_Execute_Error() throws RemotingException{
+
+ }
+
+
+ protected Object getField(Object obj, String fieldName, int parentdepth){
+ try{
+ Class<?> clazz = obj.getClass();
+ Field field = null;
+ for(int i=0;i <= parentdepth && field == null ;i++){
+ Field[] fields = clazz.getDeclaredFields();
+ for(Field f : fields){
+ if (f.getName().equals(fieldName)){
+ field = f;
+ break;
+ }
+ }
+ clazz = clazz.getSuperclass();
+ }
+ if (field != null){
+ field.setAccessible(true);
+ return field.get(obj);
+ }else {
+ throw new NoSuchFieldException();
+ }
+ }catch (Exception e) {
+ throw new IllegalStateException(e);
+ }
+ }
+ protected void sleep(int ms){
+ try {
+ Thread.sleep(ms);
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ }
+ }
+
+ class BizChannelHander extends MockedChannelHandler{
+ private boolean invokeWithBizError;
+
+ public BizChannelHander(boolean invokeWithBizError) {
+ super();
+ this.invokeWithBizError = invokeWithBizError;
+ }
+ public BizChannelHander() {
+ super();
+ }
+
+ @Override
+ public void connected(Channel channel) throws RemotingException {
+ if (invokeWithBizError){
+ throw new RemotingException(channel, "test connect biz error");
+ }
+ sleep(20);
+ }
+
+ @Override
+ public void disconnected(Channel channel) throws RemotingException {
+ if (invokeWithBizError){
+ throw new RemotingException(channel, "test disconnect biz error");
+ }
+ sleep(20);
+ }
+ @Override
+ public void received(Channel channel, Object message) throws RemotingException {
+ if (invokeWithBizError){
+ throw new RemotingException(channel, "test received biz error");
+ }
+ sleep(20);
+ }
+ };
+
+ class BizException extends RuntimeException{
+ private static final long serialVersionUID = -7541893754900723624L;
+ }
+
+ @Test(expected = RemotingException.class)
+ public void test_Connect_Biz_Error() throws RemotingException{
+ handler.connected(new MockedChannel());
+ }
+ @Test(expected = RemotingException.class)
+ public void test_Disconnect_Biz_Error() throws RemotingException{
+ handler.disconnected(new MockedChannel());
+ }
+ @Test(expected = RemotingException.class)
+ public void test_MessageReceived_Biz_Error() throws RemotingException{
+ handler.received(new MockedChannel(),"");
+ }
+ @Test
+ public void test_Caught_Biz_Error() throws RemotingException{
+ try{
+ handler.caught(new MockedChannel(), new BizException());
+ fail();
+ }catch (Exception e) {
+ Assert.assertEquals(BizException.class, e.getCause().getClass());
+ }
+ }
+}
\ No newline at end of file
diff --git a/dubbo-remoting/src/test/resources/log4j.xml b/dubbo-remoting/src/test/resources/log4j.xml
new file mode 100644
index 0000000..4a01abe
--- /dev/null
+++ b/dubbo-remoting/src/test/resources/log4j.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ - Copyright 1999-2011 Alibaba Group.
+ -
+ - Licensed under the Apache License, Version 2.0 (the "License");
+ - you may not use this file except in compliance with the License.
+ - You may obtain a copy of the License at
+ -
+ - http://www.apache.org/licenses/LICENSE-2.0
+ -
+ - Unless required by applicable law or agreed to in writing, software
+ - distributed under the License is distributed on an "AS IS" BASIS,
+ - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ - See the License for the specific language governing permissions and
+ - limitations under the License.
+-->
+<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
+<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/" debug="false">
+ <appender name="CONSOLE" class="org.apache.log4j.ConsoleAppender">
+ <layout class="org.apache.log4j.PatternLayout">
+ <param name="ConversionPattern" value="[%d{dd/MM/yy hh:mm:ss:sss z}] %t %5p %c{2}: %m%n" />
+ </layout>
+ </appender>
+ <root>
+ <level value="WARN" />
+ <appender-ref ref="CONSOLE" />
+ </root>
+</log4j:configuration>
\ No newline at end of file
diff --git a/dubbo-rpc-default/pom.xml b/dubbo-rpc-default/pom.xml
new file mode 100644
index 0000000..af7b2e4
--- /dev/null
+++ b/dubbo-rpc-default/pom.xml
@@ -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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>com.alibaba</groupId>
+ <artifactId>dubbo-parent</artifactId>
+ <version>2.0.8</version>
+ </parent>
+ <artifactId>dubbo-rpc-default</artifactId>
+ <packaging>jar</packaging>
+ <name>Dubbo Default RPC Module</name>
+ <description>The default rpc module of dubbo project</description>
+ <properties>
+ <skip_maven_deploy>false</skip_maven_deploy>
+ </properties>
+ <dependencies>
+ <dependency>
+ <groupId>com.alibaba</groupId>
+ <artifactId>dubbo-rpc</artifactId>
+ <version>${project.parent.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>com.alibaba</groupId>
+ <artifactId>dubbo-remoting</artifactId>
+ <version>${project.parent.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>com.alibaba</groupId>
+ <artifactId>dubbo-remoting-netty</artifactId>
+ <version>${project.parent.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.mina</groupId>
+ <artifactId>mina-core</artifactId>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+</project>
\ No newline at end of file
diff --git a/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/CallbackServiceCodec.java b/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/CallbackServiceCodec.java
new file mode 100644
index 0000000..c086ef5
--- /dev/null
+++ b/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/CallbackServiceCodec.java
@@ -0,0 +1,275 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.protocol.dubbo;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.ExtensionLoader;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.bytecode.Wrapper;
+import com.alibaba.dubbo.common.logger.Logger;
+import com.alibaba.dubbo.common.logger.LoggerFactory;
+import com.alibaba.dubbo.common.utils.ConcurrentHashSet;
+import com.alibaba.dubbo.common.utils.StringUtils;
+import com.alibaba.dubbo.remoting.Channel;
+import com.alibaba.dubbo.rpc.Exporter;
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.ProxyFactory;
+import com.alibaba.dubbo.rpc.RpcConstants;
+import com.alibaba.dubbo.rpc.RpcInvocation;
+
+/**
+ * callback 服务帮助类.
+ * @author chao.liuc
+ *
+ */
+public 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 inst
+ * @param clazz
+ * @param channel
+ * @param out
+ * @param export
+ * @throws IOException
+ */
+ @SuppressWarnings({ "unchecked", "rawtypes" })
+ private static String exportOrunexportCallbackService(Channel channel, Class clazz, Object inst, Boolean export) throws IOException{
+ URL url = channel.getUrl();
+ int instid = System.identityHashCode(inst);
+
+ Map<String,String> params = new HashMap<String,String>(3);
+ //不需要在重新new client
+ params.put(RpcConstants.IS_SERVER_KEY, Boolean.FALSE.toString());
+ //标识callback 变于排查问题
+ params.put(RpcConstants.IS_CALLBACK_SERVICE, Boolean.TRUE.toString());
+ String group = url.getParameter(Constants.GROUP_KEY);
+ if (group != null && group.length() > 0){
+ params.put(Constants.GROUP_KEY,group);
+ }
+ //增加方法,变于方法检查,自动降级(见dubbo protocol)
+ params.put(Constants.METHODS_KEY, StringUtils.join(Wrapper.getWrapper(clazz).getDeclaredMethodNames(), ","));
+
+ Map<String, String> tmpmap = new HashMap<String, String>(url.getParameters());
+ tmpmap.putAll(params);
+ tmpmap.remove(Constants.VERSION_KEY);//callback不需要区分version
+ URL exporturl = new URL(DubboProtocol.NAME, channel.getLocalAddress().getAddress().getHostAddress(), channel.getLocalAddress().getPort(), clazz.getName()+"."+instid, tmpmap);
+
+ //同一个jvm不需要对不同的channel产生多个exporter cache key不会碰撞
+ String cacheKey = getClientSideCallbackServiceCacheKey(instid);
+ String countkey = getClientSideCountKey(clazz.getName());
+ if(export){
+ //同一个channel 可以有多个callback instance. 不同的instance不重新export
+ if( ! channel.hasAttribute(cacheKey)){
+ if (!isInstancesOverLimit(channel, 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
+ */
+ @SuppressWarnings("unchecked")
+ private static Object referOrdestroyCallbackService(Channel channel, Class<?> clazz, Invocation inv ,int instid ,boolean isRefer){
+ Object proxy = null;
+ String invokerCacheKey = getServerSideCallbackInvokerCacheKey(channel, instid);
+ String proxyCacheKey = getServerSideCallbackServiceCacheKey(channel, instid);
+ proxy = channel.getAttribute(proxyCacheKey) ;
+ String countkey = getServerSideCountKey(channel, clazz.getName());
+ if (isRefer){
+ if( proxy == null ){
+ if (!isInstancesOverLimit(channel, clazz.getName(), instid, true)){
+ @SuppressWarnings("rawtypes")
+ Invoker<?> invoker = new ChannelWrappedInvoker(clazz, channel, String.valueOf(instid));
+ proxy = proxyFactory.getProxy(invoker);
+ channel.setAttribute(proxyCacheKey, proxy);
+ channel.setAttribute(invokerCacheKey, invoker);
+ increaseInstanceCount(channel, countkey);
+
+ //convert error fail fast .
+ //ignore concurrent problem.
+ Set<Invoker<?>> callbackInvokers = (Set<Invoker<?>>)channel.getAttribute(RpcConstants.CHANNEL_CALLBACK_KEY);
+ if (callbackInvokers == null){
+ callbackInvokers = new ConcurrentHashSet<Invoker<?>>(1);
+ callbackInvokers.add(invoker);
+ channel.setAttribute(RpcConstants.CHANNEL_CALLBACK_KEY, callbackInvokers);
+ }
+ logger.info ("method "+inv.getMethodName()+" include a callback service :"+invoker.getUrl() +", a proxy :"+invoker +" has been created.") ;
+ }
+ }
+ } else {
+ if(proxy != null){
+ Invoker<?> invoker = (Invoker<?>)channel.getAttribute(invokerCacheKey);
+ try{
+ Set<Invoker<?>> callbackInvokers = (Set<Invoker<?>>)channel.getAttribute(RpcConstants.CHANNEL_CALLBACK_KEY);
+ if (callbackInvokers != null ) {
+ callbackInvokers.remove(invoker);
+ }
+ invoker.destroy();
+ }catch (Exception e) {
+ logger.error(e);
+ }
+ //取消refer 直接在map中去除,
+ channel.removeAttribute(proxyCacheKey);
+ channel.removeAttribute(invokerCacheKey);
+ decreaseInstanceCount(channel,countkey);
+ }
+ }
+ return proxy;
+ }
+
+ private static String getClientSideCallbackServiceCacheKey(int instid){
+ return RpcConstants.CALLBACK_SERVICE_KEY+"."+instid;
+ }
+ private static String getServerSideCallbackServiceCacheKey(Channel channel, int instid){
+ return RpcConstants.CALLBACK_SERVICE_PROXY_KEY+"."+System.identityHashCode(channel)+"."+instid;
+ }
+ private static String getServerSideCallbackInvokerCacheKey(Channel channel, int instid){
+ return getServerSideCallbackServiceCacheKey(channel, instid) + "." + "invoker";
+ }
+
+ private static String getClientSideCountKey(String interfaceClass){
+ return RpcConstants.CALLBACK_SERVICE_KEY+"."+interfaceClass+".COUNT";
+ }
+ private static String getServerSideCountKey(Channel channel, String interfaceClass){
+ return RpcConstants.CALLBACK_SERVICE_PROXY_KEY+"."+System.identityHashCode(channel)+"."+interfaceClass+".COUNT";
+ }
+ private static boolean isInstancesOverLimit(Channel channel, String interfaceClass ,int instid, boolean isServer){
+ Integer count = (Integer)channel.getAttribute(isServer ? getServerSideCountKey(channel,interfaceClass) : getClientSideCountKey(interfaceClass));
+ int limit = channel.getUrl().getParameter(RpcConstants.CALLBACK_INSTANCES_LIMIT_KEY, RpcConstants.DEFAULT_CALLBACK_INSTANCES);
+ if (count != null && count >= limit){
+ //client side error
+ throw new IllegalStateException("interface " + interfaceClass +" `s callback instances num exceed providers limit :"+ limit
+ +" ,current num: "+(count+1)+". The new callback service will not work !!! you can cancle the callback service which exported before. channel :"+ channel);
+ }else {
+ return false;
+ }
+ }
+ private static void increaseInstanceCount(Channel channel, String countkey){
+ try{
+ //ignore cuncurrent problem?
+ Integer count = (Integer)channel.getAttribute(countkey);
+ if (count == null ){
+ count = 1;
+ }else {
+ count ++ ;
+ }
+ channel.setAttribute(countkey, count);
+ }catch (Exception e) {
+ logger.error(e);
+ }
+ }
+ private static void decreaseInstanceCount(Channel channel, String countkey){
+ try{
+ Integer count = (Integer)channel.getAttribute(countkey);
+ if (count == null || count <= 0){
+ return;
+ }else {
+ count -- ;
+ }
+ channel.setAttribute(countkey, count);
+ }catch (Exception e) {
+ logger.error(e);
+ }
+ }
+
+ public static Object encodeInvocationArgument(Channel channel, RpcInvocation inv, int paraIndex) throws IOException{
+ byte callbackstatus = isCallBack(channel == null ? null : channel.getUrl(), 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, pts[paraIndex], args[paraIndex], true));
+ return null;
+ case CallbackServiceCodec.CALLBACK_DESTROY:
+ inv.setAttachment(INV_ATT_CALLBACK_KEY + paraIndex, exportOrunexportCallbackService(channel, 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接口
+ byte callbackstatus = isCallBack(channel.getUrl(), inv.getMethodName(), paraIndex);
+ switch (callbackstatus) {
+ case CallbackServiceCodec.CALLBACK_NONE:
+ return inObject;
+ case CallbackServiceCodec.CALLBACK_CREATE:
+ try{
+ return referOrdestroyCallbackService(channel, pts[paraIndex], inv, Integer.parseInt(inv.getAttachment(INV_ATT_CALLBACK_KEY + paraIndex)), true);
+ }catch (Exception e) {
+ logger.error(e);
+ throw new IOException(StringUtils.toString(e));
+ }
+ case CallbackServiceCodec.CALLBACK_DESTROY:
+ try{
+ return referOrdestroyCallbackService(channel, pts[paraIndex], inv, Integer.parseInt(inv.getAttachment(INV_ATT_CALLBACK_KEY + paraIndex)), false);
+ }catch (Exception e) {
+ throw new IOException(StringUtils.toString(e));
+ }
+ default:
+ return inObject ;
+ }
+ }
+}
\ No newline at end of file
diff --git a/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/ChannelWrappedInvoker.java b/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/ChannelWrappedInvoker.java
new file mode 100644
index 0000000..9679b3b
--- /dev/null
+++ b/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/ChannelWrappedInvoker.java
@@ -0,0 +1,171 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.protocol.dubbo;
+
+import java.net.InetSocketAddress;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.remoting.Channel;
+import com.alibaba.dubbo.remoting.ChannelHandler;
+import com.alibaba.dubbo.remoting.RemotingException;
+import com.alibaba.dubbo.remoting.TimeoutException;
+import com.alibaba.dubbo.remoting.exchange.ExchangeClient;
+import com.alibaba.dubbo.remoting.exchange.support.header.HeaderExchangeClient;
+import com.alibaba.dubbo.remoting.transport.ClientDelegate;
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Result;
+import com.alibaba.dubbo.rpc.RpcConstants;
+import com.alibaba.dubbo.rpc.RpcException;
+import com.alibaba.dubbo.rpc.RpcInvocation;
+import com.alibaba.dubbo.rpc.RpcResult;
+import com.alibaba.dubbo.rpc.protocol.AbstractInvoker;
+
+/**
+ * 基于已有channel的invoker.
+ *
+ * @author chao.liuc
+ */
+public class ChannelWrappedInvoker<T> extends AbstractInvoker<T> {
+
+ private final Channel channel;
+ private final String serviceKey ;
+
+ public ChannelWrappedInvoker(Class<T> serviceType, Channel channel, String serviceKey) {
+
+ super(serviceType, channel.getUrl(), 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.getMethodName(),
+ invocation.getParameterTypes(), invocation.getArguments(),
+ invocation.getAttachments());
+ //拿不到client端export 的service path.约定为interface的名称.
+ inv.setAttachment(Constants.PATH_KEY, getInterface().getName());
+ inv.setAttachment(RpcConstants.CALLBACK_SERVICE_KEY, serviceKey);
+
+ ExchangeClient currentClient = new HeaderExchangeClient(new ChannelWrapper(this.channel));
+
+ try {
+ if (getUrl().getMethodParameter(invocation.getMethodName(), Constants.ASYNC_KEY, false)) { // 不可靠异步
+ currentClient.send(inv,getUrl().getMethodParameter(invocation.getMethodName(), Constants.SENT_KEY, false));
+ return new RpcResult();
+ }
+ int timeout = getUrl().getMethodParameter(invocation.getMethodName(),
+ Constants.TIMEOUT_KEY, Constants.DEFAULT_TIMEOUT);
+ if (timeout > 0) {
+ return (Result) currentClient.request(inv, timeout).get();
+ } else {
+ return (Result) currentClient.request(inv).get();
+ }
+ } catch (RpcException e) {
+ throw e;
+ } catch (TimeoutException e) {
+ throw new RpcException(RpcException.TIMEOUT_EXCEPTION, e.getMessage(), e);
+ } catch (RemotingException e) {
+ throw new RpcException(RpcException.NETWORK_EXCEPTION, e.getMessage(), e);
+ } catch (Throwable e) { // here is non-biz exception, wrap it.
+ throw new RpcException(e.getMessage(), e);
+ }
+ }
+
+ public static class ChannelWrapper extends ClientDelegate {
+
+ private final Channel channel;
+ private final URL url;
+
+ public ChannelWrapper(Channel channel) {
+ this.channel = channel;
+ this.url = channel.getUrl().addParameter("codec", DubboCodec.NAME);
+ }
+
+ public URL getUrl() {
+ return url;
+ }
+
+ public ChannelHandler getChannelHandler() {
+ return channel.getChannelHandler();
+ }
+
+ public InetSocketAddress getLocalAddress() {
+ return channel.getLocalAddress();
+ }
+
+ public void close() {
+ channel.close();
+ }
+
+ public boolean isClosed() {
+ return channel == null ? true : channel.isClosed();
+ }
+
+ public void reset(URL url) {
+ throw new RpcException("ChannelInvoker can not reset.");
+ }
+
+ public InetSocketAddress getRemoteAddress() {
+ return channel.getLocalAddress();
+ }
+
+ public boolean isConnected() {
+ return channel == null ? false : channel.isConnected();
+ }
+
+ public boolean hasAttribute(String key) {
+ return channel.hasAttribute(key);
+ }
+
+ public Object getAttribute(String key) {
+ return channel.getAttribute(key);
+ }
+
+ public void setAttribute(String key, Object value) {
+ channel.setAttribute(key, value);
+ }
+
+ public void removeAttribute(String key) {
+ channel.removeAttribute(key);
+ }
+
+ public void reconnect() throws RemotingException {
+
+ }
+
+ public void send(Object message) throws RemotingException {
+ channel.send(message);
+ }
+
+ public void send(Object message, boolean sent) throws RemotingException {
+ channel.send(message, sent);
+ }
+
+ }
+
+ public void destroy() {
+ //channel资源的清空由channel创建者清除.
+// super.destroy();
+// try {
+// channel.close();
+// } catch (Throwable t) {
+// logger.warn(t.getMessage(), t);
+// }
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/DubboCodec.java b/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/DubboCodec.java
new file mode 100644
index 0000000..d9aecfc
--- /dev/null
+++ b/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/DubboCodec.java
@@ -0,0 +1,180 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.util.HashMap;
+import java.util.Map;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.common.Version;
+import com.alibaba.dubbo.common.serialize.ObjectInput;
+import com.alibaba.dubbo.common.serialize.ObjectOutput;
+import com.alibaba.dubbo.common.utils.ReflectUtils;
+import com.alibaba.dubbo.common.utils.StringUtils;
+import com.alibaba.dubbo.remoting.Channel;
+import com.alibaba.dubbo.remoting.Codec;
+import com.alibaba.dubbo.remoting.exchange.codec.ExchangeCodec;
+import com.alibaba.dubbo.rpc.Result;
+import com.alibaba.dubbo.rpc.RpcInvocation;
+import com.alibaba.dubbo.rpc.RpcResult;
+
+/**
+ * Dubbo codec.
+ *
+ * @author qianlei
+ * @author chao.liuc
+ */
+@Extension(value=DubboCodec.NAME)
+public class DubboCodec extends ExchangeCodec implements Codec {
+ public static final String NAME = "dubbo";
+
+ private static final String DUBBO_VERSION = Version.getVersion(DubboCodec.class, Version.getVersion());
+
+ private static final byte RESPONSE_WITH_EXCEPTION = 0;
+
+ private static final byte RESPONSE_VALUE = 1;
+
+ private static final byte RESPONSE_NULL_VALUE = 2;
+
+ private static final Object[] EMPTY_OBJECT_ARRAY = new Object[0];
+
+ private static final Class<?>[] EMPTY_CLASS_ARRAY = new Class<?>[0];
+
+
+ @Override
+ protected void encodeRequestData(Channel channel, ObjectOutput out, Object data) throws IOException {
+ RpcInvocation inv = (RpcInvocation) data;
+
+ out.writeUTF(inv.getAttachment(Constants.DUBBO_VERSION_KEY, DUBBO_VERSION));
+ out.writeUTF(inv.getAttachment(Constants.PATH_KEY));
+ out.writeUTF(inv.getAttachment(Constants.VERSION_KEY));
+
+ out.writeUTF(inv.getMethodName());
+ out.writeUTF(ReflectUtils.getDesc(inv.getParameterTypes()));
+ Object[] args = inv.getArguments();
+ if (args != null)
+ for (int i = 0; i < args.length; i++){
+ out.writeObject(encodeInvocationArgument(channel, inv, i));
+ }
+ out.writeObject(inv.getAttachments());
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ protected Object decodeRequestData(Channel channel, ObjectInput in) throws IOException {
+ RpcInvocation inv = new RpcInvocation();
+
+ inv.setAttachment(Constants.DUBBO_VERSION_KEY, in.readUTF());
+ inv.setAttachment(Constants.PATH_KEY, in.readUTF());
+ inv.setAttachment(Constants.VERSION_KEY, in.readUTF());
+
+ inv.setMethodName(in.readUTF());
+ try {
+ Object[] args;
+ Class<?>[] pts;
+ String desc = in.readUTF();
+ if (desc.length() == 0) {
+ pts = EMPTY_CLASS_ARRAY;
+ args = EMPTY_OBJECT_ARRAY;
+ } else {
+ pts = ReflectUtils.desc2classArray(desc);
+ args = new Object[pts.length];
+ for (int i = 0; i < args.length; i++){
+ try{
+ args[i] = in.readObject(pts[i]);
+ }catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ inv.setParameterTypes(pts);
+
+ Map<String, String> map = (Map<String, String>) in.readObject(Map.class);
+ if (map != null && map.size() > 0) {
+ Map<String, String> attachment = inv.getAttachments();
+ if (attachment == null) {
+ attachment = new HashMap<String, String>();
+ }
+ attachment.putAll(map);
+ inv.setAttachments(attachment);
+ }
+ //decode argument ,may be callback
+ for (int i = 0; i < args.length; i++){
+ args[i] = decodeInvocationArgument(channel, inv, pts, i, args[i]);
+ }
+
+ inv.setArguments(args);
+
+ } catch (ClassNotFoundException e) {
+ throw new IOException(StringUtils.toString("Read invocation data failed.", e));
+ }
+ return inv;
+ }
+
+ @Override
+ protected void encodeResponseData(Channel channel, ObjectOutput out, Object data) throws IOException {
+ Result result = (Result) data;
+
+ Throwable th = result.getException();
+ if (th == null) {
+ Object ret = result.getResult();
+ if (ret == null) {
+ out.writeByte(RESPONSE_NULL_VALUE);
+ } else {
+ out.writeByte(RESPONSE_VALUE);
+ out.writeObject(ret);
+ }
+ } else {
+ out.writeByte(RESPONSE_WITH_EXCEPTION);
+ out.writeObject(th);
+ }
+ }
+
+ @Override
+ protected Object decodeResponseData(Channel channel, ObjectInput in) throws IOException {
+ RpcResult result = new RpcResult();
+
+ byte flag = in.readByte();
+ switch (flag) {
+ case RESPONSE_NULL_VALUE:
+ break;
+ case RESPONSE_VALUE:
+ try {
+ result.setResult(in.readObject());
+ } catch (ClassNotFoundException e) {
+ throw new IOException(StringUtils.toString("Read response data failed.", e));
+ }
+ break;
+ case RESPONSE_WITH_EXCEPTION:
+ try {
+ Object obj = in.readObject();
+ if (obj instanceof Throwable == false) throw new IOException("Response data error, expect Throwable, but get " + obj);
+ result.setException((Throwable) obj);
+ } catch (ClassNotFoundException e) {
+ throw new IOException(StringUtils.toString("Read response data failed.", e));
+ }
+ break;
+ default:
+ throw new IOException("Unknown result flag, expect '0' '1' '2', get " + flag);
+ }
+ return result;
+ }
+}
\ No newline at end of file
diff --git a/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/DubboExporter.java b/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/DubboExporter.java
new file mode 100644
index 0000000..e965da9
--- /dev/null
+++ b/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/DubboExporter.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.protocol.dubbo;
+
+import java.util.Map;
+
+import com.alibaba.dubbo.rpc.Exporter;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.protocol.AbstractExporter;
+
+/**
+ * DubboExporter
+ *
+ * @author william.liangf
+ */
+public class DubboExporter<T> extends AbstractExporter<T> {
+
+ private final String key;
+
+ private final Map<String, Exporter<?>> exporterMap;
+
+ public DubboExporter(Invoker<T> invoker, String key, Map<String, Exporter<?>> exporterMap){
+ super(invoker);
+ this.key = key;
+ this.exporterMap = exporterMap;
+ }
+
+ @Override
+ public void unexport() {
+ super.unexport();
+ exporterMap.remove(key);
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/DubboInvoker.java b/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/DubboInvoker.java
new file mode 100644
index 0000000..6cf91ea
--- /dev/null
+++ b/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/DubboInvoker.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.rpc.protocol.dubbo;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.utils.AtomicPositiveInteger;
+import com.alibaba.dubbo.remoting.RemotingException;
+import com.alibaba.dubbo.remoting.TimeoutException;
+import com.alibaba.dubbo.remoting.exchange.ExchangeClient;
+import com.alibaba.dubbo.remoting.exchange.ResponseFuture;
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Result;
+import com.alibaba.dubbo.rpc.RpcConstants;
+import com.alibaba.dubbo.rpc.RpcContext;
+import com.alibaba.dubbo.rpc.RpcException;
+import com.alibaba.dubbo.rpc.RpcInvocation;
+import com.alibaba.dubbo.rpc.RpcResult;
+import com.alibaba.dubbo.rpc.protocol.AbstractInvoker;
+
+/**
+ * DubboInvoker
+ *
+ * @author william.liangf
+ * @author chao.liuc
+ */
+public class DubboInvoker<T> extends AbstractInvoker<T> {
+
+ private final ExchangeClient[] clients;
+
+ private final AtomicPositiveInteger index = new AtomicPositiveInteger();
+
+ private final String version;
+
+ public DubboInvoker(Class<T> serviceType, URL url, ExchangeClient[] clients){
+ super(serviceType, url, new String[] {Constants.GROUP_KEY, Constants.TOKEN_KEY, Constants.TIMEOUT_KEY});
+ this.clients = clients;
+ // get version.
+ this.version = url.getParameter(Constants.VERSION_KEY, "0.0.0");
+ }
+
+ @Override
+ protected Result doInvoke(final Invocation invocation) throws Throwable {
+ RpcInvocation inv = null;
+ final String methodName ;
+ if(Constants.$INVOKE.equals(invocation.getMethodName()) && invocation.getArguments() !=null && invocation.getArguments().length >0 && invocation.getArguments()[0] != null){
+ inv = (RpcInvocation) invocation;
+ //the frist argument must be real method name;
+ methodName = invocation.getArguments()[0].toString();
+ }else {
+ inv = new RpcInvocation(invocation.getMethodName(), invocation.getParameterTypes(),
+ invocation.getArguments(), invocation.getAttachments());
+ methodName = invocation.getMethodName();
+ }
+ inv.setAttachment(Constants.PATH_KEY, getUrl().getPath());
+ inv.setAttachment(Constants.VERSION_KEY, version);
+
+ ExchangeClient currentClient;
+ if (clients.length == 1) {
+ currentClient = clients[0];
+ } else {
+ currentClient = clients[index.getAndIncrement() % clients.length];
+ }
+ try {
+ // 不可靠异步
+ boolean isAsync = getUrl().getMethodParameter(methodName, Constants.ASYNC_KEY, false);
+ int timeout = getUrl().getMethodParameter(methodName, Constants.TIMEOUT_KEY,Constants.DEFAULT_TIMEOUT);
+ if (isAsync) {
+ boolean isReturn = getUrl().getMethodParameter(methodName, RpcConstants.RETURN_KEY, true);
+ if (isReturn) {
+ ResponseFuture future = currentClient.request(inv, timeout) ;
+ RpcContext.getContext().setFuture(new FutureAdapter<Object>(future));
+ } else {
+ boolean isSent = getUrl().getMethodParameter(methodName, Constants.SENT_KEY, false);
+ currentClient.send(inv, isSent);
+ RpcContext.getContext().setFuture(null);
+ }
+ return new RpcResult();
+ }
+ RpcContext.getContext().setFuture(null);
+ return (Result) currentClient.request(inv, timeout).get();
+ } catch (TimeoutException e) {
+ throw new RpcException(RpcException.TIMEOUT_EXCEPTION, e.getMessage(), e);
+ } catch (RemotingException e) {
+ throw new RpcException(RpcException.NETWORK_EXCEPTION, e.getMessage(), e);
+ }
+ }
+
+ @Override
+ public boolean isAvailable() {
+ if (!super.isAvailable())
+ return false;
+ if (clients.length ==1){
+ return clients[0].isConnected();
+ } else {
+ for (ExchangeClient client : clients){
+ if (client.isConnected()){
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ public void destroy() {
+ super.destroy();
+ for (ExchangeClient client : clients) {
+ try {
+ client.close();
+ } catch (Throwable t) {
+ logger.warn(t.getMessage(), t);
+ }
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/DubboProtocol.java b/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/DubboProtocol.java
new file mode 100644
index 0000000..427226e
--- /dev/null
+++ b/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/DubboProtocol.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.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 com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.common.ExtensionLoader;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.Version;
+import com.alibaba.dubbo.common.utils.NetUtils;
+import com.alibaba.dubbo.common.utils.StringUtils;
+import com.alibaba.dubbo.remoting.Channel;
+import com.alibaba.dubbo.remoting.RemotingException;
+import com.alibaba.dubbo.remoting.Transporter;
+import com.alibaba.dubbo.remoting.exchange.ExchangeChannel;
+import com.alibaba.dubbo.remoting.exchange.ExchangeClient;
+import com.alibaba.dubbo.remoting.exchange.ExchangeHandler;
+import com.alibaba.dubbo.remoting.exchange.ExchangeServer;
+import com.alibaba.dubbo.remoting.exchange.Exchangers;
+import com.alibaba.dubbo.remoting.exchange.support.ExchangeHandlerAdapter;
+import com.alibaba.dubbo.rpc.Exporter;
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.Protocol;
+import com.alibaba.dubbo.rpc.RpcConstants;
+import com.alibaba.dubbo.rpc.RpcContext;
+import com.alibaba.dubbo.rpc.RpcException;
+import com.alibaba.dubbo.rpc.RpcInvocation;
+import com.alibaba.dubbo.rpc.protocol.AbstractProtocol;
+
+/**
+ * dubbo protocol support.
+ *
+ * @author qian.lei
+ * @author william.liangf
+ * @author chao.liuc
+ */
+@Extension(DubboProtocol.NAME)
+public class DubboProtocol extends AbstractProtocol {
+
+ public static final String NAME = "dubbo";
+
+ public static final String COMPATIBLE_CODEC_NAME = "dubbo1compatible";
+
+ public static final int DEFAULT_PORT = 20880;
+
+ private final Map<String, ExchangeServer> serverMap = new ConcurrentHashMap<String, ExchangeServer>(); // <host:port,Exchanger>
+
+ //consumer side export a stub service for dispatching event
+ //servicekey-stubmethods
+ private final ConcurrentMap<String, String> stubServiceMethodsMap = new ConcurrentHashMap<String, String>();
+
+ private ExchangeHandler requestHandler = new ExchangeHandlerAdapter() {
+ private boolean isClientSide(Channel channel) {
+ InetSocketAddress address = channel.getRemoteAddress();
+ URL url = channel.getUrl();
+ return url.getPort() == address.getPort() &&
+ NetUtils.filterLocalHost(channel.getUrl().getHost())
+ .equals(NetUtils.filterLocalHost(address.getAddress().getHostAddress()));
+ }
+
+ public Object reply(ExchangeChannel channel, Object message) throws RemotingException {
+ if (message instanceof Invocation) {
+ boolean isCallBackServiceInvoke = false;
+ boolean isStubServiceInvoke = false;
+ Invocation inv = (Invocation) message;
+ int port = channel.getLocalAddress().getPort();
+ String path = inv.getAttachments().get(Constants.PATH_KEY);
+ //如果是客户端的回调服务.
+ isStubServiceInvoke = Boolean.TRUE.toString().equals(inv.getAttachments().get(RpcConstants.STUB_EVENT_KEY));
+ if (isStubServiceInvoke){
+ port = channel.getRemoteAddress().getPort();
+ }
+ //callback
+ isCallBackServiceInvoke = isClientSide(channel) && !isStubServiceInvoke;
+ if(isCallBackServiceInvoke){
+ path = inv.getAttachments().get(Constants.PATH_KEY)+"."+inv.getAttachments().get(RpcConstants.CALLBACK_SERVICE_KEY);
+ }
+ 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:"+message);
+
+ if (isCallBackServiceInvoke){
+ String methodsStr = exporter.getInvoker().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:" + exporter.getInvoker().getUrl()) +" ,invocation is :"+inv );
+ return null;
+ }
+ }
+ RpcContext.getContext().setRemoteAddress(channel.getRemoteAddress());
+ return exporter.getInvoker().invoke(inv);
+ }
+ throw new RemotingException(channel, "Unsupported request: " + message == null ? null : (message.getClass().getName() + ": " + message) + ", channel: consumer: " + channel.getRemoteAddress() + " --> provider: " + channel.getLocalAddress());
+ }
+
+ @Override
+ public void received(Channel channel, Object message) throws RemotingException {
+ if (message instanceof Invocation) {
+ reply((ExchangeChannel) channel, message);
+ } else {
+ super.received(channel, message);
+ }
+ }
+
+ @Override
+ public void connected(Channel channel) throws RemotingException {
+ invoke(channel, RpcConstants.ON_CONNECT_KEY);
+ }
+
+ @Override
+ public void disconnected(Channel channel) throws RemotingException {
+ if(logger.isInfoEnabled()){
+ logger.info("disconected from "+ channel.getRemoteAddress() + ",url:" + channel.getUrl());
+ }
+ invoke(channel, RpcConstants.ON_DISCONNECT_KEY);
+ }
+
+ private void invoke(Channel channel, String methodKey) {
+ Invocation invocation = createInvocation(channel, channel.getUrl(), methodKey);
+ if (invocation != null) {
+ try {
+ received(channel, invocation);
+ } catch (Throwable t) {
+ logger.warn("Failed to invoke event method " + invocation.getMethodName() + "(), cause: " + t.getMessage(), t);
+ }
+ }
+ }
+
+ private Invocation createInvocation(Channel channel, URL url, String methodKey) {
+ String method = url.getParameter(methodKey);
+ if (method == null || method.length() == 0) {
+ return null;
+ }
+ RpcInvocation invocation = new RpcInvocation(method, new Class<?>[0], new Object[0]);
+ invocation.setAttachment(Constants.PATH_KEY, url.getPath());
+ invocation.setAttachment(Constants.GROUP_KEY, url.getParameter(Constants.GROUP_KEY));
+ invocation.setAttachment(Constants.INTERFACE_KEY, url.getParameter(Constants.INTERFACE_KEY));
+ invocation.setAttachment(Constants.VERSION_KEY, url.getParameter(Constants.VERSION_KEY));
+ if (url.getParameter(RpcConstants.STUB_EVENT_KEY, false)){
+ invocation.setAttachment(RpcConstants.STUB_EVENT_KEY, Boolean.TRUE.toString());
+ }
+ return invocation;
+ }
+ };
+
+ private static DubboProtocol INSTANCE;
+
+ public DubboProtocol() {
+ INSTANCE = this;
+ }
+
+ public static DubboProtocol getDubboProtocol() {
+ if (INSTANCE == null) {
+ ExtensionLoader.getExtensionLoader(Protocol.class).getExtension(DubboProtocol.NAME); // load
+ }
+ return INSTANCE;
+ }
+
+ public Collection<ExchangeServer> getServers() {
+ return Collections.unmodifiableCollection(serverMap.values());
+ }
+
+ public Collection<Exporter<?>> getExporters() {
+ return Collections.unmodifiableCollection(exporterMap.values());
+ }
+
+ public Collection<Invoker<?>> getInvokers() {
+ return Collections.unmodifiableCollection(invokers);
+ }
+
+ public int getDefaultPort() {
+ return DEFAULT_PORT;
+ }
+
+ public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
+ URL url = invoker.getUrl().addParameterIfAbsent(Constants.DOWNSTREAM_CODEC_KEY, DubboCodec.NAME);
+ // find server.
+ String key = url.getAddress();
+ //client 也可以暴露一个只有server可以调用的服务。
+ boolean isServer = url.getParameter(RpcConstants.IS_SERVER_KEY,true);
+ if (isServer && ! serverMap.containsKey(key)) {
+ serverMap.put(key, initServer(url));
+ }
+ // export service.
+ key = serviceKey(url);
+ DubboExporter<T> exporter = new DubboExporter<T>(invoker, key, exporterMap);
+ exporterMap.put(key, exporter);
+
+ //export an stub service for dispaching event
+ Boolean isStubSupportEvent = url.getParameter(RpcConstants.STUB_EVENT_KEY,RpcConstants.DEFAULT_STUB_EVENT);
+ Boolean isCallbackservice = url.getParameter(RpcConstants.IS_CALLBACK_SERVICE, false);
+ if (isStubSupportEvent && !isCallbackservice){
+ String stubServiceMethods = url.getParameter(RpcConstants.STUB_EVENT_METHODS_KEY);
+ if (stubServiceMethods == null || stubServiceMethods.length() == 0 ){
+ if (logger.isWarnEnabled()){
+ logger.warn( new IllegalStateException("consumer ["+url.getParameter(Constants.INTERFACE_KEY)+"], has set stubproxy support event ,but no stub methods founded."));
+ }
+ } else {
+ stubServiceMethodsMap.put(url.getServiceKey(), stubServiceMethods);
+ }
+ }
+ return exporter;
+ }
+
+ private ExchangeServer initServer(URL url) {
+ 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.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(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 {
+ // find client.
+ int channels = url.getPositiveParameter(Constants.CONNECTIONS_KEY, 1);
+ ExchangeClient[] clients = new ExchangeClient[channels];
+ for (int i = 0; i < clients.length; i++) {
+ clients[i] = initClient(url);
+ }
+ // create rpc invoker.
+ DubboInvoker<T> invoker = new DubboInvoker<T>(serviceType, url, clients);
+ invokers.add(invoker);
+ return invoker;
+ }
+
+ private ExchangeClient initClient(URL url) {
+ // client type setting.
+ String str = url.getParameter(Constants.CLIENT_KEY, url.getParameter(Constants.SERVER_KEY, Constants.DEFAULT_REMOTING_CLIENT));
+
+ String version = url.getParameter(Constants.DUBBO_VERSION_KEY);
+ boolean compatible = (version != null && version.startsWith("1.0."));
+ url = url.addParameter(Constants.CODEC_KEY, Version.isCompatibleVersion() && compatible ? COMPATIBLE_CODEC_NAME : DubboCodec.NAME);
+
+ // BIO存在严重性能问题,暂时不允许使用
+ if (str != null && str.length() > 0 && ! ExtensionLoader.getExtensionLoader(Transporter.class).hasExtension(str)) {
+ throw new RpcException("Unsupported client type: " + str + "," +
+ " supported client type is " + StringUtils.join(ExtensionLoader.getExtensionLoader(Transporter.class).getSupportedExtensions(), " "));
+ }
+
+ //设置连接应该是lazy的
+ if (url.getParameter(RpcConstants.LAZY_CONNECT_KEY, false)){
+ return new LazyConnectExchangeClient(url ,requestHandler);
+ }
+ try {
+ return Exchangers.connect(url ,requestHandler);
+ } catch (RemotingException e) {
+ throw new RpcException("Fail to create remoting client for service(" + url
+ + "): " + e.getMessage(), e);
+ }
+ }
+
+ 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);
+ }
+ }
+ }
+ stubServiceMethodsMap.clear();
+ }
+}
\ No newline at end of file
diff --git a/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/FutureAdapter.java b/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/FutureAdapter.java
new file mode 100644
index 0000000..c213116
--- /dev/null
+++ b/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/FutureAdapter.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.protocol.dubbo;
+
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+import com.alibaba.dubbo.common.utils.StringUtils;
+import com.alibaba.dubbo.remoting.RemotingException;
+import com.alibaba.dubbo.remoting.exchange.ResponseFuture;
+import com.alibaba.dubbo.rpc.Result;
+import com.alibaba.dubbo.rpc.RpcException;
+
+/**
+ * FutureAdapter
+ *
+ * @author william.liangf
+ */
+public class FutureAdapter<V> implements Future<V> {
+
+ private final ResponseFuture future;
+
+ public FutureAdapter(ResponseFuture future){
+ this.future = future;
+ }
+
+ public ResponseFuture getFuture() {
+ return future;
+ }
+
+ public boolean cancel(boolean mayInterruptIfRunning) {
+ return false;
+ }
+
+ public boolean isCancelled() {
+ return false;
+ }
+
+ public boolean isDone() {
+ return future.isDone();
+ }
+
+ @SuppressWarnings("unchecked")
+ public V get() throws InterruptedException, ExecutionException {
+ try {
+ return (V) (((Result) future.get()).recreate());
+ } catch (RemotingException e) {
+ throw new ExecutionException(e.getMessage(), e);
+ } catch (Throwable e) {
+ throw new RpcException(e);
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ public V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
+ int timeoutInMillis = (int) unit.convert(timeout, TimeUnit.MILLISECONDS);
+ try {
+ return (V) (((Result) future.get(timeoutInMillis)).recreate());
+ } catch (com.alibaba.dubbo.remoting.TimeoutException e) {
+ throw new TimeoutException(StringUtils.toString(e));
+ } catch (RemotingException e) {
+ throw new ExecutionException(e.getMessage(), e);
+ } catch (Throwable e) {
+ throw new RpcException(e);
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/LazyConnectExchangeClient.java b/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/LazyConnectExchangeClient.java
new file mode 100644
index 0000000..cead73c
--- /dev/null
+++ b/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/LazyConnectExchangeClient.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;
+
+import java.net.InetSocketAddress;
+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.remoting.ChannelHandler;
+import com.alibaba.dubbo.remoting.RemotingException;
+import com.alibaba.dubbo.remoting.exchange.ExchangeClient;
+import com.alibaba.dubbo.remoting.exchange.ExchangeHandler;
+import com.alibaba.dubbo.remoting.exchange.Exchangers;
+import com.alibaba.dubbo.remoting.exchange.ResponseFuture;
+import com.alibaba.dubbo.rpc.RpcConstants;
+
+/**
+ * dubbo protocol support class.
+ *
+ * @author chao.liuc
+ */
+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 ;
+
+ public LazyConnectExchangeClient(URL url, ExchangeHandler requestHandler) {
+ //lazy connect ,need set send.reconnect = true, to avoid channel bad status.
+ this.url = url.addParameter(Constants.SEND_RECONNECT_KEY, Boolean.TRUE.toString());
+ this.requestHandler = requestHandler;
+ this.initialState = url.getParameter(RpcConstants.LAZY_CONNECT_INITIAL_STATE_KEY,RpcConstants.DEFAULT_LAZY_CONNECT_INITIAL_STATE);
+ }
+
+ 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 {
+ initClient();
+ return client.request(request);
+ }
+
+ public URL getUrl() {
+ return url;
+ }
+
+ public InetSocketAddress getRemoteAddress() {
+ return client.getRemoteAddress();
+ }
+
+ public ResponseFuture request(Object request, int timeout) throws RemotingException {
+ initClient();
+ return client.request(request, timeout);
+ }
+
+ public ChannelHandler getChannelHandler() {
+ checkClient();
+ return client.getChannelHandler();
+ }
+
+ public boolean isConnected() {
+ if (client == null) {
+ return initialState;
+ } else {
+ return client.isConnected();
+ }
+ }
+
+ public InetSocketAddress getLocalAddress() {
+ checkClient();
+ return client.getLocalAddress();
+ }
+
+ public ExchangeHandler getExchangeHandler() {
+ checkClient();
+ return client.getExchangeHandler();
+ }
+
+ 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) {
+ checkClient();
+ 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) {
+ checkClient();
+ return client.hasAttribute(key);
+ }
+
+ private void checkClient() {
+ if (client == null) {
+ throw new IllegalStateException(
+ "LazyConnectExchangeClient state error. the client has not be init .url:" + url);
+ }
+ }
+}
\ No newline at end of file
diff --git a/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/filter/FutureFilter.java b/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/filter/FutureFilter.java
new file mode 100644
index 0000000..8de2140
--- /dev/null
+++ b/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/filter/FutureFilter.java
@@ -0,0 +1,203 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.protocol.dubbo.filter;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.concurrent.Future;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.common.logger.Logger;
+import com.alibaba.dubbo.common.logger.LoggerFactory;
+import com.alibaba.dubbo.remoting.exchange.ResponseCallback;
+import com.alibaba.dubbo.remoting.exchange.ResponseFuture;
+import com.alibaba.dubbo.rpc.Filter;
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.Result;
+import com.alibaba.dubbo.rpc.RpcConstants;
+import com.alibaba.dubbo.rpc.RpcContext;
+import com.alibaba.dubbo.rpc.RpcException;
+import com.alibaba.dubbo.rpc.StaticContext;
+import com.alibaba.dubbo.rpc.protocol.dubbo.FutureAdapter;
+
+/**
+ * EventFilter
+ * @author chao.liuc
+ * @author william.liangf
+ */
+@Extension("future")
+public class FutureFilter implements Filter {
+
+ protected static final Logger logger = LoggerFactory.getLogger(FutureFilter.class);
+
+ public Result invoke(final Invoker<?> invoker, final Invocation invocation) throws RpcException {
+ fireInvokeCallback(invoker, invocation);
+ //需要在调用前配置好是否有返回值,已供invoker判断是否需要返回future.
+ Result result = invoker.invoke(invocation);
+ if (invoker.getUrl().getMethodParameter(invocation.getMethodName(), Constants.ASYNC_KEY, false)) {
+ asyncCallback(invoker, invocation);
+ } else {
+ syncCallback(invoker, invocation, result);
+ }
+ return result;
+ }
+
+ private void syncCallback(final Invoker<?> invoker, final Invocation invocation, final Result result) {
+ if (result.hasException()) {
+ fireThrowCallback(invoker, invocation, result.getException());
+ } else {
+ fireReturnCallback(invoker, invocation, result.getResult());
+ }
+ }
+
+ private void asyncCallback(final Invoker<?> invoker, final Invocation invocation) {
+ Future<?> f = RpcContext.getContext().getFuture();
+ if (f instanceof FutureAdapter) {
+ ResponseFuture future = ((FutureAdapter<?>)f).getFuture();
+ future.setCallback(new ResponseCallback() {
+ public void done(Object rpcResult) {
+ if (rpcResult == null){
+ logger.error(new IllegalStateException("invalid result value : null, expected "+Result.class.getName()));
+ return;
+ }
+ ///must be rpcResult
+ if (! (rpcResult instanceof Result)){
+ logger.error(new IllegalStateException("invalid result type :" + rpcResult.getClass() + ", expected "+Result.class.getName()));
+ return;
+ }
+ Result result = (Result) rpcResult;
+ if (result.hasException()) {
+ fireThrowCallback(invoker, invocation, result.getException());
+ } else {
+ fireReturnCallback(invoker, invocation, result.getResult());
+ }
+ }
+ public void caught(Throwable exception) {
+ fireThrowCallback(invoker, invocation, exception);
+ }
+ });
+ }
+ }
+
+ private void fireInvokeCallback(final Invoker<?> invoker, final Invocation invocation) {
+ final Method onInvokeMethod = (Method)StaticContext.getSystemContext().get(StaticContext.getKey(invoker.getUrl(), invocation.getMethodName(), RpcConstants.ON_INVOKE_METHOD_KEY));
+ final Object onInvokeInst = StaticContext.getSystemContext().get(StaticContext.getKey(invoker.getUrl(), invocation.getMethodName(), RpcConstants.ON_INVOKE_INSTANCE_KEY));
+
+ if (onInvokeMethod == null && onInvokeInst == null ){
+ return ;
+ }
+ if (onInvokeMethod == null || onInvokeInst == null ){
+ throw new IllegalStateException("service:" + invoker.getUrl().getServiceKey() +" has a onreturn callback config , but no such "+(onInvokeMethod == null ? "method" : "instance")+" found. url:"+invoker.getUrl());
+ }
+ if (onInvokeMethod != null && ! onInvokeMethod.isAccessible()) {
+ onInvokeMethod.setAccessible(true);
+ }
+
+ Object[] params = invocation.getArguments();
+ try {
+ onInvokeMethod.invoke(onInvokeInst, params);
+ } catch (InvocationTargetException e) {
+ fireThrowCallback(invoker, invocation, e.getTargetException());
+ } catch (Throwable e) {
+ fireThrowCallback(invoker, invocation, e);
+ }
+ }
+
+ private void fireReturnCallback(final Invoker<?> invoker, final Invocation invocation, final Object result) {
+ final Method onReturnMethod = (Method)StaticContext.getSystemContext().get(StaticContext.getKey(invoker.getUrl(), invocation.getMethodName(), RpcConstants.ON_RETURN_METHOD_KEY));
+ final Object onReturnInst = StaticContext.getSystemContext().get(StaticContext.getKey(invoker.getUrl(), invocation.getMethodName(), RpcConstants.ON_RETURN_INSTANCE_KEY));
+
+ //not set onreturn callback
+ if (onReturnMethod == null && onReturnInst == null ){
+ return ;
+ }
+
+ if (onReturnMethod == null || onReturnInst == null ){
+ throw new IllegalStateException("service:" + invoker.getUrl().getServiceKey() +" has a onreturn callback config , but no such "+(onReturnMethod == null ? "method" : "instance")+" found. url:"+invoker.getUrl());
+ }
+ if (onReturnMethod != null && ! onReturnMethod.isAccessible()) {
+ onReturnMethod.setAccessible(true);
+ }
+
+ Object[] args = invocation.getArguments();
+ Object[] params ;
+ Class<?>[] rParaTypes = onReturnMethod.getParameterTypes() ;
+ if (rParaTypes.length >1 ) {
+ if (rParaTypes.length == 2 && rParaTypes[1].isAssignableFrom(Object[].class)){
+ params = new Object[2];
+ params[0] = result;
+ params[1] = args ;
+ }else {
+ params = new Object[args.length + 1];
+ params[0] = result;
+ System.arraycopy(args, 0, params, 1, args.length);
+ }
+ } else {
+ params = new Object[] { result };
+ }
+ try {
+ onReturnMethod.invoke(onReturnInst, params);
+ } catch (InvocationTargetException e) {
+ fireThrowCallback(invoker, invocation, e.getTargetException());
+ } catch (Throwable e) {
+ fireThrowCallback(invoker, invocation, e);
+ }
+ }
+
+ private void fireThrowCallback(final Invoker<?> invoker, final Invocation invocation, final Throwable exception) {
+ final Method onthrowMethod = (Method)StaticContext.getSystemContext().get(StaticContext.getKey(invoker.getUrl(), invocation.getMethodName(), RpcConstants.ON_THROW_METHOD_KEY));
+ final Object onthrowInst = StaticContext.getSystemContext().get(StaticContext.getKey(invoker.getUrl(), invocation.getMethodName(), RpcConstants.ON_THROW_INSTANCE_KEY));
+
+ //没有设置onthrow callback.
+ if (onthrowMethod == null && onthrowInst == null ){
+ return ;
+ }
+ if (onthrowMethod == null || onthrowInst == null ){
+ throw new IllegalStateException("service:" + invoker.getUrl().getServiceKey() +" has a onthrow callback config , but no such "+(onthrowMethod == null ? "method" : "instance")+" found. url:"+invoker.getUrl());
+ }
+ if (onthrowMethod != null && ! onthrowMethod.isAccessible()) {
+ onthrowMethod.setAccessible(true);
+ }
+ Class<?>[] rParaTypes = onthrowMethod.getParameterTypes() ;
+ if (rParaTypes[0].isAssignableFrom(exception.getClass())){
+ try {
+ Object[] args = invocation.getArguments();
+ Object[] params;
+
+ if (rParaTypes.length >1 ) {
+ if (rParaTypes.length == 2 && rParaTypes[1].isAssignableFrom(Object[].class)){
+ params = new Object[2];
+ params[0] = exception;
+ params[1] = args ;
+ }else {
+ params = new Object[args.length + 1];
+ params[0] = exception;
+ System.arraycopy(args, 0, params, 1, args.length);
+ }
+ } else {
+ params = new Object[] { exception };
+ }
+ onthrowMethod.invoke(onthrowInst,params);
+ } catch (Throwable e) {
+ logger.error(invocation.getMethodName() +".call back method invoke error . callback method :" + onthrowMethod + ", url:"+ invoker.getUrl(), e);
+ }
+ } else {
+ logger.error(invocation.getMethodName() +".call back method invoke error . callback method :" + onthrowMethod + ", url:"+ invoker.getUrl(), exception);
+ }
+ }
+}
\ No newline at end of file
diff --git a/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/filter/TraceFilter.java b/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/filter/TraceFilter.java
new file mode 100644
index 0000000..66f47be
--- /dev/null
+++ b/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/filter/TraceFilter.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.protocol.dubbo.filter;
+
+import java.util.ArrayList;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.common.json.JSON;
+import com.alibaba.dubbo.common.logger.Logger;
+import com.alibaba.dubbo.common.logger.LoggerFactory;
+import com.alibaba.dubbo.common.utils.ConcurrentHashSet;
+import com.alibaba.dubbo.remoting.Channel;
+import com.alibaba.dubbo.rpc.Filter;
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.Result;
+import com.alibaba.dubbo.rpc.RpcContext;
+import com.alibaba.dubbo.rpc.RpcException;
+
+/**
+ * TraceFilter
+ *
+ * @author william.liangf
+ */
+@Extension("trace")
+public class TraceFilter implements Filter {
+
+ private static final Logger logger = LoggerFactory.getLogger(TraceFilter.class);
+
+ private static final String TRACE_MAX = "trace.max";
+
+ private static final String TRACE_COUNT = "trace.count";
+
+ private static final ConcurrentMap<String, Set<Channel>> tracers = new ConcurrentHashMap<String, Set<Channel>>();
+
+ public static void addTracer(Class<?> type, String method, Channel channel, int max) {
+ channel.setAttribute(TRACE_MAX, max);
+ channel.setAttribute(TRACE_COUNT, new AtomicInteger());
+ String key = method != null && method.length() > 0 ? type.getName() + "." + method : type.getName();
+ Set<Channel> channels = tracers.get(key);
+ if (channels == null) {
+ tracers.putIfAbsent(key, new ConcurrentHashSet<Channel>());
+ channels = tracers.get(key);
+ }
+ channels.add(channel);
+ }
+
+ public static void removeTracer(Class<?> type, String method, Channel channel) {
+ channel.removeAttribute(TRACE_MAX);
+ channel.removeAttribute(TRACE_COUNT);
+ String key = method != null && method.length() > 0 ? type.getName() + "." + method : type.getName();
+ Set<Channel> channels = tracers.get(key);
+ if (channels != null) {
+ channels.remove(channel);
+ }
+ }
+
+ public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
+ long start = System.currentTimeMillis();
+ Result result = invoker.invoke(invocation);
+ long end = System.currentTimeMillis();
+ if (tracers.size() > 0) {
+ String key = invoker.getInterface().getName() + "." + invocation.getMethodName();
+ Set<Channel> channels = tracers.get(key);
+ if (channels == null || channels.size() == 0) {
+ key = invoker.getInterface().getName();
+ channels = tracers.get(key);
+ }
+ if (channels != null && channels.size() > 0) {
+ for (Channel channel : new ArrayList<Channel>(channels)) {
+ if (channel.isConnected()) {
+ try {
+ int max = 1;
+ Integer m = (Integer) channel.getAttribute(TRACE_MAX);
+ if (m != null) {
+ max = (int) m;
+ }
+ int count = 0;
+ AtomicInteger c = (AtomicInteger) channel.getAttribute(TRACE_COUNT);
+ if (c == null) {
+ c = new AtomicInteger();
+ channel.setAttribute(TRACE_COUNT, c);
+ }
+ count = c.getAndIncrement();
+ if (count < max) {
+ String prompt = channel.getUrl().getParameter("prompt", "telnet");
+ channel.send("\r\n" + RpcContext.getContext().getRemoteAddress() + " -> "
+ + invoker.getInterface().getName()
+ + "." + invocation.getMethodName()
+ + "(" + JSON.json(invocation.getArguments()) + ")" + " -> " + JSON.json(result.getResult())
+ + "\r\nelapsed: "+(end - start) +" ms."
+ + "\r\n\r\n" + prompt + "> ");
+ }
+ if(count >= max - 1) {
+ channels.remove(channel);
+ }
+ } catch (Throwable e) {
+ channels.remove(channel);
+ logger.warn(e.getMessage(), e);
+ }
+ } else {
+ channels.remove(channel);
+ }
+ }
+ }
+ }
+ return result;
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/status/ServerStatusChecker.java b/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/status/ServerStatusChecker.java
new file mode 100644
index 0000000..b69ad3f
--- /dev/null
+++ b/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/status/ServerStatusChecker.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.protocol.dubbo.status;
+
+import java.util.Collection;
+
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.common.status.Status;
+import com.alibaba.dubbo.common.status.StatusChecker;
+import com.alibaba.dubbo.remoting.exchange.ExchangeServer;
+import com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol;
+
+/**
+ * ServerStatusChecker
+ *
+ * @author william.liangf
+ */
+@Extension("server")
+public class ServerStatusChecker implements StatusChecker {
+
+ public Status check() {
+ Collection<ExchangeServer> servers = DubboProtocol.getDubboProtocol().getServers();
+ if (servers == null || servers.size() == 0) {
+ return new Status(Status.Level.UNKNOWN);
+ }
+ Status.Level level = Status.Level.OK;
+ StringBuilder buf = new StringBuilder();
+ for (ExchangeServer server : servers) {
+ if (! server.isBound()) {
+ level = Status.Level.ERROR;
+ buf.setLength(0);
+ buf.append(server.getLocalAddress());
+ break;
+ }
+ if (buf.length() > 0) {
+ buf.append(",");
+ }
+ buf.append(server.getLocalAddress());
+ buf.append("(clients:");
+ buf.append(server.getChannels().size());
+ buf.append(")");
+ }
+ return new Status(level, buf.toString());
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/status/ThreadPoolStatusChecker.java b/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/status/ThreadPoolStatusChecker.java
new file mode 100644
index 0000000..7c80643
--- /dev/null
+++ b/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/status/ThreadPoolStatusChecker.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.protocol.dubbo.status;
+
+import java.util.Collection;
+import java.util.concurrent.Executor;
+import java.util.concurrent.ThreadPoolExecutor;
+
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.common.status.Status;
+import com.alibaba.dubbo.common.status.StatusChecker;
+import com.alibaba.dubbo.remoting.ChannelHandler;
+import com.alibaba.dubbo.remoting.Server;
+import com.alibaba.dubbo.remoting.exchange.ExchangeServer;
+import com.alibaba.dubbo.remoting.exchange.support.header.HeaderExchangeServer;
+import com.alibaba.dubbo.remoting.transport.handler.WrappedChannelHandler;
+import com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol;
+
+/**
+ * ThreadPoolStatusChecker
+ *
+ * @author william.liangf
+ */
+@Extension("threadpool")
+public class ThreadPoolStatusChecker implements StatusChecker {
+
+ public Status check() {
+ Collection<ExchangeServer> servers = DubboProtocol.getDubboProtocol().getServers();
+ if (servers == null || servers.size() == 0) {
+ return new Status(Status.Level.UNKNOWN);
+ }
+ for (Server server : servers) {
+ if (server instanceof HeaderExchangeServer) {
+ HeaderExchangeServer exchanger = (HeaderExchangeServer) server;
+ server = exchanger.getServer();
+ }
+ ChannelHandler handler = server.getChannelHandler();
+ if (handler instanceof WrappedChannelHandler) {
+ Executor executor = ((WrappedChannelHandler) handler).getExecutor();
+ if (executor instanceof ThreadPoolExecutor) {
+ ThreadPoolExecutor tp = (ThreadPoolExecutor)executor;
+ boolean ok = tp.getActiveCount() < tp.getMaximumPoolSize() - 1;
+ return new Status(ok ? Status.Level.OK : Status.Level.WARN,
+ "max:" + tp.getMaximumPoolSize()
+ + ",core:" + tp.getCorePoolSize()
+ + ",largest:" + tp.getLargestPoolSize()
+ + ",active:" + tp.getActiveCount()
+ + ",task:" + tp.getTaskCount());
+ }
+ }
+ }
+ return new Status(Status.Level.UNKNOWN);
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/telnet/ChangeTelnetHandler.java b/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/telnet/ChangeTelnetHandler.java
new file mode 100644
index 0000000..0394f2c
--- /dev/null
+++ b/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/telnet/ChangeTelnetHandler.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.protocol.dubbo.telnet;
+
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.remoting.Channel;
+import com.alibaba.dubbo.remoting.telnet.TelnetHandler;
+import com.alibaba.dubbo.remoting.telnet.support.Help;
+import com.alibaba.dubbo.rpc.Exporter;
+import com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol;
+
+/**
+ * ChangeServiceTelnetHandler
+ *
+ * @author william.liangf
+ */
+@Help(parameter = "[service]", summary = "Change default service.", detail = "Change default service.")
+@Extension("cd")
+public class ChangeTelnetHandler implements TelnetHandler {
+
+ public static final String SERVICE_KEY = "telnet.service";
+
+ public String telnet(Channel channel, String message) {
+ if (message == null || message.length() == 0) {
+ return "Please input service name, eg: \r\ncd XxxService\r\ncd com.xxx.XxxService";
+ }
+ StringBuilder buf = new StringBuilder();
+ if (message.equals("/") || message.equals("..")) {
+ String service = (String) channel.getAttribute(SERVICE_KEY);
+ channel.removeAttribute(SERVICE_KEY);
+ buf.append("Cancelled default service " + service + ".");
+ } else {
+ boolean found = false;
+ for (Exporter<?> exporter : DubboProtocol.getDubboProtocol().getExporters()) {
+ if (message.equals(exporter.getInvoker().getInterface().getSimpleName())
+ || message.equals(exporter.getInvoker().getInterface().getName())
+ || message.equals(exporter.getInvoker().getUrl().getPath())) {
+ found = true;
+ break;
+ }
+ }
+ if (found) {
+ channel.setAttribute(SERVICE_KEY, message);
+ buf.append("Used the " + message + " as default.\r\nYou can cancel default service by command: cd /");
+ } else {
+ buf.append("No such service " + message);
+ }
+ }
+ return buf.toString();
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/telnet/CountTelnetHandler.java b/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/telnet/CountTelnetHandler.java
new file mode 100644
index 0000000..27d8a46
--- /dev/null
+++ b/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/telnet/CountTelnetHandler.java
@@ -0,0 +1,166 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.protocol.dubbo.telnet;
+
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.List;
+
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.utils.StringUtils;
+import com.alibaba.dubbo.remoting.Channel;
+import com.alibaba.dubbo.remoting.RemotingException;
+import com.alibaba.dubbo.remoting.telnet.TelnetHandler;
+import com.alibaba.dubbo.remoting.telnet.support.Help;
+import com.alibaba.dubbo.remoting.telnet.support.TelnetUtils;
+import com.alibaba.dubbo.rpc.Exporter;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.RpcStatus;
+import com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol;
+
+/**
+ * CountTelnetHandler
+ *
+ * @author william.liangf
+ */
+@Help(parameter = "[service] [method] [times]", summary = "Count the service.", detail = "Count the service.")
+@Extension("count")
+public class CountTelnetHandler implements TelnetHandler {
+
+ public String telnet(final Channel channel, String message) {
+ String service = (String) channel.getAttribute(ChangeTelnetHandler.SERVICE_KEY);
+ if ((service == null || service.length() == 0)
+ && (message == null || message.length() == 0)) {
+ return "Please input service name, eg: \r\ncount XxxService\r\ncount XxxService xxxMethod\r\ncount XxxService xxxMethod 10\r\nor \"cd XxxService\" firstly.";
+ }
+ StringBuilder buf = new StringBuilder();
+ if (service != null && service.length() > 0) {
+ buf.append("Use default service " + service + ".\r\n");
+ }
+ String[] parts = message.split("\\s+");
+ String method;
+ String times;
+ if (service == null || service.length() == 0) {
+ service = parts.length > 0 ? parts[0] : null;
+ method = parts.length > 1 ? parts[1] : null;
+ } else {
+ method = parts.length > 0 ? parts[0] : null;
+ }
+ if (StringUtils.isInteger(method)) {
+ times = method;
+ method = null;
+ } else {
+ times = parts.length > 2 ? parts[2] : "1";
+ }
+ if (! StringUtils.isInteger(times)) {
+ return "Illegal times " + times + ", must be integer.";
+ }
+ final int t = Integer.parseInt(times);
+ Invoker<?> invoker = null;
+ for (Exporter<?> exporter : DubboProtocol.getDubboProtocol().getExporters()) {
+ if (service.equals(exporter.getInvoker().getInterface().getSimpleName())
+ || service.equals(exporter.getInvoker().getInterface().getName())
+ || service.equals(exporter.getInvoker().getUrl().getPath())) {
+ invoker = exporter.getInvoker();
+ break;
+ }
+ }
+ if (invoker != null) {
+ if (t > 0) {
+ final String mtd = method;
+ final Invoker<?> inv = invoker;
+ final String prompt = channel.getUrl().getParameter("prompt", "telnet");
+ Thread thread = new Thread(new Runnable() {
+ public void run() {
+ for (int i = 0; i < t; i ++) {
+ String result = count(inv, mtd);
+ try {
+ channel.send("\r\n" + result);
+ } catch (RemotingException e1) {
+ return;
+ }
+ if (i < t - 1) {
+ try {
+ Thread.sleep(1000);
+ } catch (InterruptedException e) {
+ }
+ }
+ }
+ try {
+ channel.send("\r\n" + prompt + "> ");
+ } catch (RemotingException e1) {
+ return;
+ }
+ }
+ }, "TelnetCount");
+ thread.setDaemon(true);
+ thread.start();
+ }
+ } else {
+ buf.append("No such service " + service);
+ }
+ return buf.toString();
+ }
+
+ private String count(Invoker<?> invoker, String method) {
+ URL url = invoker.getUrl();
+ List<List<String>> table = new ArrayList<List<String>>();
+ List<String> header = new ArrayList<String>();
+ header.add("method");
+ header.add("total");
+ header.add("failed");
+ header.add("active");
+ header.add("average");
+ header.add("max");
+ if (method == null || method.length() == 0) {
+ for (Method m : invoker.getInterface().getMethods()) {
+ RpcStatus count = RpcStatus.getStatus(url, m.getName());
+ List<String> row = new ArrayList<String>();
+ row.add(m.getName());
+ row.add(String.valueOf(count.getTotal()));
+ row.add(String.valueOf(count.getFailed()));
+ row.add(String.valueOf(count.getActive()));
+ row.add(String.valueOf(count.getSucceededAverageElapsed()) + "ms");
+ row.add(String.valueOf(count.getSucceededMaxElapsed()) + "ms");
+ table.add(row);
+ }
+ } else {
+ boolean found = false;
+ for (Method m : invoker.getInterface().getMethods()) {
+ if (m.getName().equals(method)) {
+ found = true;
+ break;
+ }
+ }
+ if (found) {
+ RpcStatus count = RpcStatus.getStatus(url, method);
+ List<String> row = new ArrayList<String>();
+ row.add(method);
+ row.add(String.valueOf(count.getTotal()));
+ row.add(String.valueOf(count.getFailed()));
+ row.add(String.valueOf(count.getActive()));
+ row.add(String.valueOf(count.getSucceededAverageElapsed()) + "ms");
+ row.add(String.valueOf(count.getSucceededMaxElapsed()) + "ms");
+ table.add(row);
+ } else {
+ return "No such method " + method + " in class " + invoker.getInterface().getName();
+ }
+ }
+ return TelnetUtils.toTable(header, table);
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/telnet/CurrentTelnetHandler.java b/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/telnet/CurrentTelnetHandler.java
new file mode 100644
index 0000000..b81b82c
--- /dev/null
+++ b/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/telnet/CurrentTelnetHandler.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.protocol.dubbo.telnet;
+
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.remoting.Channel;
+import com.alibaba.dubbo.remoting.telnet.TelnetHandler;
+import com.alibaba.dubbo.remoting.telnet.support.Help;
+
+/**
+ * CurrentServiceTelnetHandler
+ *
+ * @author william.liangf
+ */
+@Help(parameter = "", summary = "Print working default service.", detail = "Print working default service.")
+@Extension("pwd")
+public class CurrentTelnetHandler implements TelnetHandler {
+
+ public String telnet(Channel channel, String message) {
+ if (message.length() > 0) {
+ return "Unsupported parameter " + message + " for pwd.";
+ }
+ String service = (String) channel.getAttribute(ChangeTelnetHandler.SERVICE_KEY);
+ StringBuilder buf = new StringBuilder();
+ if (service == null || service.length() == 0) {
+ buf.append("/");
+ } else {
+ buf.append(service);
+ }
+ return buf.toString();
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/telnet/InvokeTelnetHandler.java b/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/telnet/InvokeTelnetHandler.java
new file mode 100644
index 0000000..fddb054
--- /dev/null
+++ b/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/telnet/InvokeTelnetHandler.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.protocol.dubbo.telnet;
+
+import java.lang.reflect.Method;
+import java.util.List;
+
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.common.json.JSON;
+import com.alibaba.dubbo.common.utils.PojoUtils;
+import com.alibaba.dubbo.common.utils.ReflectUtils;
+import com.alibaba.dubbo.remoting.Channel;
+import com.alibaba.dubbo.remoting.telnet.TelnetHandler;
+import com.alibaba.dubbo.remoting.telnet.support.Help;
+import com.alibaba.dubbo.rpc.Exporter;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.RpcContext;
+import com.alibaba.dubbo.rpc.RpcInvocation;
+import com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol;
+
+/**
+ * InvokeTelnetHandler
+ *
+ * @author william.liangf
+ */
+@Help(parameter = "[service.]method(args)", summary = "Invoke the service method.", detail = "Invoke the service method.")
+@Extension("invoke")
+public class InvokeTelnetHandler implements TelnetHandler {
+
+ @SuppressWarnings("unchecked")
+ public String telnet(Channel channel, String message) {
+ if (message == null || message.length() == 0) {
+ return "Please input method name, eg: \r\ninvoke xxxMethod(1234, \"abcd\", {\"prop\" : \"value\"})\r\ninvoke XxxService.xxxMethod(1234, \"abcd\", {\"prop\" : \"value\"})\r\ninvoke com.xxx.XxxService.xxxMethod(1234, \"abcd\", {\"prop\" : \"value\"})";
+ }
+ StringBuilder buf = new StringBuilder();
+ String service = (String) channel.getAttribute(ChangeTelnetHandler.SERVICE_KEY);
+ if (service != null && service.length() > 0) {
+ buf.append("Use default service " + service + ".\r\n");
+ }
+ int i = message.indexOf("(");
+ if (i < 0 || ! message.endsWith(")")) {
+ return "Invalid parameters, format: service.method(args)";
+ }
+ String method = message.substring(0, i).trim();
+ String args = message.substring(i + 1, message.length() - 1).trim();
+ i = method.lastIndexOf(".");
+ if (i >= 0) {
+ service = method.substring(0, i).trim();
+ method = method.substring(i + 1).trim();
+ }
+ Invoker<?> invoker = null;
+ Method invokeMethod = null;
+ for (Exporter<?> exporter : DubboProtocol.getDubboProtocol().getExporters()) {
+ if (service == null || service.length() == 0) {
+ Method[] methods = exporter.getInvoker().getInterface().getMethods();
+ for (Method m : methods) {
+ if (m.getName().equals(method)
+ || ReflectUtils.getSignature(m.getName(), m.getParameterTypes()).equals(method)) {
+ invoker = exporter.getInvoker();
+ invokeMethod = m;
+ break;
+ }
+ }
+ if (invoker != null) {
+ break;
+ }
+ } else {
+ if (service.equals(exporter.getInvoker().getInterface().getSimpleName())
+ || service.equals(exporter.getInvoker().getInterface().getName())
+ || service.equals(exporter.getInvoker().getUrl().getPath())) {
+ invoker = exporter.getInvoker();
+ Method[] methods = invoker.getInterface().getMethods();
+ for (Method m : methods) {
+ if (m.getName().equals(method)
+ || ReflectUtils.getSignature(m.getName(), m.getParameterTypes()).equals(method)) {
+ invokeMethod = m;
+ }
+ }
+ break;
+ }
+ }
+ }
+ if (invoker != null) {
+ if (invokeMethod != null) {
+ List<Object> list;
+ try {
+ list = (List<Object>) JSON.parse("[" + args + "]", List.class);
+ } catch (Throwable t) {
+ return "Invalid json argument, cause: " + t.getMessage();
+ }
+ try {
+ Object[] array = PojoUtils.realize(list.toArray(), invokeMethod.getParameterTypes());
+ RpcContext.getContext().setLocalAddress(channel.getLocalAddress()).setRemoteAddress(channel.getRemoteAddress());
+ long start = System.currentTimeMillis();
+ Object result = invoker.invoke(new RpcInvocation(invokeMethod, array)).recreate();
+ long end = System.currentTimeMillis();
+ buf.append(JSON.json(result));
+ buf.append("\r\nelapsed: ");
+ buf.append(end - start);
+ buf.append(" ms.");
+ } catch (Throwable t) {
+ return "Failed to invoke method " + invokeMethod.getName() + ", cause: " + t.getMessage();
+ }
+ } else {
+ buf.append("No such method " + method + " in service " + service);
+ }
+ } else {
+ buf.append("No such service " + service);
+ }
+ return buf.toString();
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/telnet/ListTelnetHandler.java b/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/telnet/ListTelnetHandler.java
new file mode 100644
index 0000000..28c0ada
--- /dev/null
+++ b/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/telnet/ListTelnetHandler.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.protocol.dubbo.telnet;
+
+import java.lang.reflect.Method;
+
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.common.utils.ReflectUtils;
+import com.alibaba.dubbo.remoting.Channel;
+import com.alibaba.dubbo.remoting.telnet.TelnetHandler;
+import com.alibaba.dubbo.remoting.telnet.support.Help;
+import com.alibaba.dubbo.rpc.Exporter;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol;
+
+/**
+ * ListTelnetHandler
+ *
+ * @author william.liangf
+ */
+@Help(parameter = "[-l] [service]", summary = "List services and methods.", detail = "List services and methods.")
+@Extension("ls")
+public class ListTelnetHandler implements TelnetHandler {
+
+ public String telnet(Channel channel, String message) {
+ StringBuilder buf = new StringBuilder();
+ String service = null;
+ boolean detail = false;
+ if (message.length() > 0) {
+ String[] parts = message.split("\\s+");
+ for (String part : parts) {
+ if ("-l".equals(part)) {
+ detail = true;
+ } else {
+ if (service != null && service.length() > 0) {
+ return "Invaild parameter " + part;
+ }
+ service = part;
+ }
+ }
+ } else {
+ service = (String) channel.getAttribute(ChangeTelnetHandler.SERVICE_KEY);
+ if (service != null && service.length() > 0) {
+ buf.append("Use default service " + service + ".\r\n");
+ }
+ }
+ if (service == null || service.length() == 0) {
+ for (Exporter<?> exporter : DubboProtocol.getDubboProtocol().getExporters()) {
+ if (buf.length() > 0) {
+ buf.append("\r\n");
+ }
+ buf.append(exporter.getInvoker().getInterface().getName());
+ if (detail) {
+ buf.append(" -> ");
+ buf.append(exporter.getInvoker().getUrl());
+ }
+ }
+ } else {
+ Invoker<?> invoker = null;
+ for (Exporter<?> exporter : DubboProtocol.getDubboProtocol().getExporters()) {
+ if (service.equals(exporter.getInvoker().getInterface().getSimpleName())
+ || service.equals(exporter.getInvoker().getInterface().getName())
+ || service.equals(exporter.getInvoker().getUrl().getPath())) {
+ invoker = exporter.getInvoker();
+ break;
+ }
+ }
+ if (invoker != null) {
+ Method[] methods = invoker.getInterface().getMethods();
+ for (Method method : methods) {
+ if (buf.length() > 0) {
+ buf.append("\r\n");
+ }
+ if (detail) {
+ buf.append(ReflectUtils.getName(method));
+ } else {
+ buf.append(method.getName());
+ }
+ }
+ } else {
+ buf.append("No such service " + service);
+ }
+ }
+ return buf.toString();
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/telnet/LogTelnetHandler.java b/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/telnet/LogTelnetHandler.java
new file mode 100644
index 0000000..37b273d
--- /dev/null
+++ b/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/telnet/LogTelnetHandler.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.protocol.dubbo.telnet;
+
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.nio.ByteBuffer;
+import java.nio.channels.FileChannel;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.common.logger.Level;
+import com.alibaba.dubbo.common.logger.LoggerFactory;
+import com.alibaba.dubbo.common.utils.StringUtils;
+import com.alibaba.dubbo.remoting.Channel;
+import com.alibaba.dubbo.remoting.telnet.TelnetHandler;
+import com.alibaba.dubbo.remoting.telnet.support.Help;
+
+/**
+ * LogTelnetHandler
+ * @author chao.liuc
+ *
+ */
+@Help(parameter = "level", summary = "Change log level or show log ", detail = "Change log level or show log")
+@Extension("log")
+public class LogTelnetHandler implements TelnetHandler {
+
+ public static final String SERVICE_KEY = "telnet.log";
+
+ public String telnet(Channel channel, String message) {
+ long size = 0 ;
+ File file = LoggerFactory.getFile();
+ StringBuffer buf = new StringBuffer();
+ if (message == null || message.trim().length() == 0) {
+ buf.append("EXAMPLE: log error / log 100");
+ }else {
+ String str[] = message.split(" ");
+ if (! StringUtils.isInteger(str[0])){
+ LoggerFactory.setLevel(Level.valueOf(message.toUpperCase()));
+ } else {
+ int SHOW_LOG_LENGTH = Integer.parseInt(str[0]);
+
+ if (file != null && file.exists()) {
+ try{
+ FileInputStream fis = new FileInputStream(file);
+ FileChannel filechannel = fis.getChannel();
+ size = filechannel.size();
+ ByteBuffer bb;
+ if (size <= SHOW_LOG_LENGTH) {
+ bb = ByteBuffer.allocate((int) size);
+ filechannel.read(bb, 0);
+ } else {
+ int pos = (int) (size - SHOW_LOG_LENGTH);
+ bb = ByteBuffer.allocate(SHOW_LOG_LENGTH);
+ filechannel.read(bb, pos);
+ }
+ bb.flip();
+ String content = new String(bb.array()).replace("<", "<")
+ .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-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/telnet/PortTelnetHandler.java b/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/telnet/PortTelnetHandler.java
new file mode 100644
index 0000000..0743394
--- /dev/null
+++ b/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/telnet/PortTelnetHandler.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.protocol.dubbo.telnet;
+
+import java.util.Collection;
+
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.common.utils.StringUtils;
+import com.alibaba.dubbo.remoting.Channel;
+import com.alibaba.dubbo.remoting.exchange.ExchangeChannel;
+import com.alibaba.dubbo.remoting.exchange.ExchangeServer;
+import com.alibaba.dubbo.remoting.telnet.TelnetHandler;
+import com.alibaba.dubbo.remoting.telnet.support.Help;
+import com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol;
+
+/**
+ * ServerTelnetHandler
+ *
+ * @author william.liangf
+ */
+@Help(parameter = "[-l] [port]", summary = "Print server ports and connections.", detail = "Print server ports and connections.")
+@Extension("ps")
+public class PortTelnetHandler implements TelnetHandler {
+
+ public String telnet(Channel channel, String message) {
+ StringBuilder buf = new StringBuilder();
+ String port = null;
+ boolean detail = false;
+ if (message.length() > 0) {
+ String[] parts = message.split("\\s+");
+ for (String part : parts) {
+ if ("-l".equals(part)) {
+ detail = true;
+ } else {
+ if (! StringUtils.isInteger(part)) {
+ return "Illegal port " + part + ", must be integer.";
+ }
+ port = part;
+ }
+ }
+ }
+ if (port == null || port.length() == 0) {
+ for (ExchangeServer server : DubboProtocol.getDubboProtocol().getServers()) {
+ if (buf.length() > 0) {
+ buf.append("\r\n");
+ }
+ if (detail) {
+ buf.append(server.getUrl().getProtocol() + "://" + server.getUrl().getAddress());
+ } else {
+ buf.append(server.getUrl().getPort());
+ }
+ }
+ } else {
+ int p = Integer.parseInt(port);
+ ExchangeServer server = null;
+ for (ExchangeServer s : DubboProtocol.getDubboProtocol().getServers()) {
+ if (p == s.getUrl().getPort()) {
+ server = s;
+ break;
+ }
+ }
+ if (server != null) {
+ Collection<ExchangeChannel> channels = server.getExchangeChannels();
+ for (ExchangeChannel c : channels) {
+ if (buf.length() > 0) {
+ buf.append("\r\n");
+ }
+ if (detail) {
+ buf.append(c.getRemoteAddress() + " -> " + c.getLocalAddress());
+ } else {
+ buf.append(c.getRemoteAddress());
+ }
+ }
+ } else {
+ buf.append("No such port " + port);
+ }
+ }
+ return buf.toString();
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/telnet/TraceTelnetHandler.java b/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/telnet/TraceTelnetHandler.java
new file mode 100644
index 0000000..af6a291
--- /dev/null
+++ b/dubbo-rpc-default/src/main/java/com/alibaba/dubbo/rpc/protocol/dubbo/telnet/TraceTelnetHandler.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.protocol.dubbo.telnet;
+
+import java.lang.reflect.Method;
+
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.common.utils.StringUtils;
+import com.alibaba.dubbo.remoting.Channel;
+import com.alibaba.dubbo.remoting.telnet.TelnetHandler;
+import com.alibaba.dubbo.remoting.telnet.support.Help;
+import com.alibaba.dubbo.rpc.Exporter;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol;
+import com.alibaba.dubbo.rpc.protocol.dubbo.filter.TraceFilter;
+
+/**
+ * TraceTelnetHandler
+ *
+ * @author william.liangf
+ */
+@Help(parameter = "[service] [method] [times]", summary = "Trace the service.", detail = "Trace the service.")
+@Extension("trace")
+public class TraceTelnetHandler implements TelnetHandler {
+
+ public String telnet(Channel channel, String message) {
+ String service = (String) channel.getAttribute(ChangeTelnetHandler.SERVICE_KEY);
+ if ((service == null || service.length() == 0)
+ && (message == null || message.length() == 0)) {
+ return "Please input service name, eg: \r\ntrace XxxService\r\ntrace XxxService xxxMethod\r\ntrace XxxService xxxMethod 10\r\nor \"cd XxxService\" firstly.";
+ }
+ StringBuilder buf = new StringBuilder();
+ if (service != null && service.length() > 0) {
+ buf.append("Use default service " + service + ".\r\n");
+ }
+ String[] parts = message.split("\\s+");
+ String method;
+ String times;
+ if (service == null || service.length() == 0) {
+ service = parts.length > 0 ? parts[0] : null;
+ method = parts.length > 1 ? parts[1] : null;
+ } else {
+ method = parts.length > 0 ? parts[0] : null;
+ }
+ if (StringUtils.isInteger(method)) {
+ times = method;
+ method = null;
+ } else {
+ times = parts.length > 2 ? parts[2] : "1";
+ }
+ if (! StringUtils.isInteger(times)) {
+ return "Illegal times " + times + ", must be integer.";
+ }
+ Invoker<?> invoker = null;
+ for (Exporter<?> exporter : DubboProtocol.getDubboProtocol().getExporters()) {
+ if (service.equals(exporter.getInvoker().getInterface().getSimpleName())
+ || service.equals(exporter.getInvoker().getInterface().getName())
+ || service.equals(exporter.getInvoker().getUrl().getPath())) {
+ invoker = exporter.getInvoker();
+ break;
+ }
+ }
+ if (invoker != null) {
+ if (method != null && method.length() > 0) {
+ boolean found = false;
+ for (Method m : invoker.getInterface().getMethods()) {
+ if (m.getName().equals(method)) {
+ found = true;
+ break;
+ }
+ }
+ if (! found) {
+ return "No such method " + method + " in class " + invoker.getInterface().getName();
+ }
+ }
+ TraceFilter.addTracer(invoker.getInterface(), method, channel, Integer.parseInt(times));
+ } else {
+ buf.append("No such service " + service);
+ }
+ return buf.toString();
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-rpc-default/src/main/resources/META-INF/services/com.alibaba.dubbo.common.status.StatusChecker b/dubbo-rpc-default/src/main/resources/META-INF/services/com.alibaba.dubbo.common.status.StatusChecker
new file mode 100644
index 0000000..0dd9b93
--- /dev/null
+++ b/dubbo-rpc-default/src/main/resources/META-INF/services/com.alibaba.dubbo.common.status.StatusChecker
@@ -0,0 +1,2 @@
+com.alibaba.dubbo.rpc.protocol.dubbo.status.ServerStatusChecker
+com.alibaba.dubbo.rpc.protocol.dubbo.status.ThreadPoolStatusChecker
\ No newline at end of file
diff --git a/dubbo-rpc-default/src/main/resources/META-INF/services/com.alibaba.dubbo.rpc.Filter b/dubbo-rpc-default/src/main/resources/META-INF/services/com.alibaba.dubbo.rpc.Filter
new file mode 100644
index 0000000..89488e5
--- /dev/null
+++ b/dubbo-rpc-default/src/main/resources/META-INF/services/com.alibaba.dubbo.rpc.Filter
@@ -0,0 +1,2 @@
+com.alibaba.dubbo.rpc.protocol.dubbo.filter.TraceFilter
+com.alibaba.dubbo.rpc.protocol.dubbo.filter.FutureFilter
\ No newline at end of file
diff --git a/dubbo-rpc-default/src/main/resources/META-INF/services/com.alibaba.dubbo.rpc.Protocol b/dubbo-rpc-default/src/main/resources/META-INF/services/com.alibaba.dubbo.rpc.Protocol
new file mode 100644
index 0000000..b415e0f
--- /dev/null
+++ b/dubbo-rpc-default/src/main/resources/META-INF/services/com.alibaba.dubbo.rpc.Protocol
@@ -0,0 +1 @@
+com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol
\ No newline at end of file
diff --git a/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/DubboLazyConnectTest.java b/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/DubboLazyConnectTest.java
new file mode 100644
index 0000000..4179cf0
--- /dev/null
+++ b/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/DubboLazyConnectTest.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.protocol.dubbo;
+
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.utils.NetUtils;
+import com.alibaba.dubbo.rpc.RpcConstants;
+import com.alibaba.dubbo.rpc.RpcException;
+import com.alibaba.dubbo.rpc.protocol.dubbo.support.ProtocolUtils;
+
+/**
+ * dubbo protocol lazy connect test
+ * @author chao.liuc
+ *
+ */
+public class DubboLazyConnectTest {
+
+ @BeforeClass
+ public static void setUpBeforeClass() throws Exception {
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ }
+
+ @Test(expected = RpcException.class)
+ public void testSticky1(){
+ URL url = URL.valueOf("dubbo://127.0.0.1:9090/hi");
+ ProtocolUtils.refer(IDemoService.class, url);
+ }
+
+ @Test
+ public void testSticky2(){
+ URL url = URL.valueOf("dubbo://127.0.0.1:9090/hi?"+RpcConstants.LAZY_CONNECT_KEY+"=true");
+ ProtocolUtils.refer(IDemoService.class, url);
+ }
+
+ @Test(expected = RpcException.class)
+ public void testSticky3() {
+ URL url = URL.valueOf("dubbo://127.0.0.1:9090/hi?"+RpcConstants.LAZY_CONNECT_KEY+"=true");
+ IDemoService service = (IDemoService)ProtocolUtils.refer(IDemoService.class, url);
+ service.get();
+ }
+
+ @Test
+ public void testSticky4() {
+ int port = NetUtils.getAvailablePort();
+ URL url = URL.valueOf("dubbo://127.0.0.1:"+port+"/hi?"+RpcConstants.LAZY_CONNECT_KEY+"=true");
+
+ ProtocolUtils.export(new DemoServiceImpl(), IDemoService.class, url);
+
+ IDemoService service = (IDemoService)ProtocolUtils.refer(IDemoService.class, url);
+ Assert.assertEquals("ok", service.get());
+ }
+
+ public interface IDemoService{
+ public String get();
+ }
+ public class DemoServiceImpl implements IDemoService{
+ public String get(){
+ return "ok";
+ }
+ }
+}
\ No newline at end of file
diff --git a/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/ExplicitCallbackTest.java b/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/ExplicitCallbackTest.java
new file mode 100644
index 0000000..4b293a2
--- /dev/null
+++ b/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/ExplicitCallbackTest.java
@@ -0,0 +1,339 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.protocol.dubbo;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
+
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.utils.NetUtils;
+import com.alibaba.dubbo.rpc.Exporter;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.RpcConstants;
+import com.alibaba.dubbo.rpc.RpcException;
+import com.alibaba.dubbo.rpc.protocol.dubbo.support.ProtocolUtils;
+
+public class ExplicitCallbackTest {
+
+ protected Exporter<IDemoService> exporter = null;
+ protected Invoker<IDemoService> reference = null;
+
+ @After
+ public void tearDown(){
+ destroyService();
+ }
+
+ public void exportService(){
+ 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
+ +"&"+RpcConstants.CALLBACK_INSTANCES_LIMIT_KEY+"="+callbacks
+ );
+ // uncomment is unblock invoking
+// serviceURL = serviceURL.addParameter("yyy."+Constants.ASYNC_KEY,String.valueOf(true));
+// consumerUrl = consumerUrl.addParameter("yyy."+Constants.ASYNC_KEY,String.valueOf(true));
+ }
+ public void initOrResetBadUrl() throws Exception{
+ initOrResetUrl(1, 1000);
+ consumerUrl = serviceURL = serviceURL
+ .addParameter(Constants.DOWNSTREAM_CODEC_KEY, "dubbo1compatible")
+ ;
+ }
+ public void initOrResetService(){
+ destroyService();
+ exportService();
+ referService();
+ }
+ public void destroyService(){
+ demoProxy = null ;
+ try {
+ if (exporter!=null) exporter.unexport();
+ if (reference!=null) reference.destroy();
+ }catch (Exception e) {
+ }
+ }
+ // ============================华丽的分割线================================================
+ interface IDemoCallback{
+ String yyy(String msg);
+ }
+ 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 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, 1000); 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
+ public void TestCallbackMultiInstans() throws Exception {
+ initOrResetUrl(2, 1000);
+ initOrResetService() ;
+ IDemoCallback callback = new IDemoCallback(){
+ public String yyy(String msg) {
+ System.out.println("callback1:"+msg);
+ return "callback1 onChanged ,"+msg;
+ }
+ };
+
+ IDemoCallback callback2 = new IDemoCallback(){
+ public String yyy(String msg) {
+ System.out.println("callback2:"+msg);
+ return "callback2 onChanged ,"+msg;
+ }
+ };
+ {
+ demoProxy.xxx2(callback);
+ Assert.assertEquals(1, demoProxy.getCallbackCount());
+ Thread.sleep(500);
+ demoProxy.unxxx2(callback);
+ Assert.assertEquals(0, demoProxy.getCallbackCount());
+ System.out.println("");
+
+ demoProxy.xxx2(callback2);
+ Assert.assertEquals(1, demoProxy.getCallbackCount());
+ Thread.sleep(500);
+ demoProxy.unxxx2(callback2);
+ Assert.assertEquals(0, demoProxy.getCallbackCount());
+ System.out.println("");
+ demoProxy.xxx2(callback);
+ Thread.sleep(500);
+ Assert.assertEquals(1, demoProxy.getCallbackCount());
+ demoProxy.unxxx2(callback);
+ Assert.assertEquals(0, demoProxy.getCallbackCount());
+ }
+ {
+ demoProxy.xxx2(callback);
+ Assert.assertEquals(1, demoProxy.getCallbackCount());
+
+ demoProxy.xxx2(callback);
+ Assert.assertEquals(1, demoProxy.getCallbackCount());
+
+ demoProxy.xxx2(callback2);
+ Assert.assertEquals(2, demoProxy.getCallbackCount());
+ }
+ destroyService();
+ }
+
+ @Ignore
+ @Test(expected=RpcException.class)
+ public void TestCallbackDownStreamCodec() throws Exception {
+ initOrResetBadUrl(); initOrResetService() ;
+ final AtomicInteger count = new AtomicInteger(0);
+ demoProxy.xxx(new IDemoCallback() {
+ public String yyy(String msg) {
+ System.out.println("Recived callback: " + msg);
+ count.incrementAndGet();
+ return "ok";
+ }
+ },"other custom args" , 10 , 100);
+ System.out.println("Async...");
+ assertCallbackCount(10,100,count);
+ destroyService();
+ }
+
+ @Test(expected = RpcException.class)
+ public void TestCallbackConsumerLimit() throws Exception {
+ initOrResetUrl(1, 1000);
+ //api的方式 url 无法自动从服务端传递到客户端,需要手动制定
+ initOrResetService() ;
+ final AtomicInteger count = new AtomicInteger(0);
+ demoProxy.xxx(new IDemoCallback() {
+ public String yyy(String msg) {
+ System.out.println("Recived callback: " + msg);
+ count.incrementAndGet();
+ return "ok";
+ }
+ },"other custom args" , 10 , 100);
+
+ demoProxy.xxx(new IDemoCallback() {
+ public String yyy(String msg) {
+ System.out.println("Recived callback: " + msg);
+ count.incrementAndGet();
+ return "ok";
+ }
+ },"other custom args" , 10 , 100);
+ destroyService();
+ }
+
+ @Test(expected = RpcException.class)
+ public void TestCallbackProviderLimit() throws Exception {
+ initOrResetUrl(1, 1000);
+ //api的方式 url 无法自动从服务端传递到客户端,需要手动制定
+ serviceURL = serviceURL.addParameter(RpcConstants.CALLBACK_INSTANCES_LIMIT_KEY, 1+"");
+ initOrResetService() ;
+ final AtomicInteger count = new AtomicInteger(0);
+ demoProxy.xxx(new IDemoCallback() {
+ public String yyy(String msg) {
+ System.out.println("Recived callback: " + msg);
+ count.incrementAndGet();
+ return "ok";
+ }
+ },"other custom args" , 10 , 100);
+
+ demoProxy.xxx(new IDemoCallback() {
+ public String yyy(String msg) {
+ System.out.println("Recived callback: " + msg);
+ count.incrementAndGet();
+ return "ok";
+ }
+ },"other custom args" , 10 , 100);
+ destroyService();
+ }
+
+ private void assertCallbackCount(int runs, int sleep, AtomicInteger count) throws InterruptedException{
+ int last = count.get();
+ for(int i=0;i< runs ;i++){
+ if (last > runs) break;
+ Thread.sleep(sleep * 2);
+ System.out.println(count.get() + " " + last);
+ Assert.assertTrue(count.get() > last);
+ last = count.get();
+ }
+ //有一次同步调用callback
+ Assert.assertEquals(runs+1, count.get());
+ }
+
+ @Ignore //使用不同进程启动
+ @Test
+ public void startProvider() throws Exception {
+ exportService();
+ synchronized (ExplicitCallbackTest.class) {
+ ExplicitCallbackTest.class.wait();
+ }
+ }
+}
\ No newline at end of file
diff --git a/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/FutureFilterTest.java b/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/FutureFilterTest.java
new file mode 100644
index 0000000..e5e19c3
--- /dev/null
+++ b/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/FutureFilterTest.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.protocol.dubbo;
+
+import static org.junit.Assert.assertEquals;
+
+import org.easymock.EasyMock;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.rpc.Filter;
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.Result;
+import com.alibaba.dubbo.rpc.RpcConstants;
+import com.alibaba.dubbo.rpc.RpcException;
+import com.alibaba.dubbo.rpc.RpcResult;
+import com.alibaba.dubbo.rpc.protocol.dubbo.filter.FutureFilter;
+import com.alibaba.dubbo.rpc.protocol.dubbo.support.DemoService;
+
+/**
+ * EventFilterTest.java
+ *
+ * @author tony.chenl
+ * TODO 暂时依赖callback集成测试,后续补充
+ */
+public class FutureFilterTest {
+ Filter eventFilter = new FutureFilter();
+ private static Invocation invocation;
+
+ @BeforeClass
+ public static void setUp() {
+ invocation = EasyMock.createMock(Invocation.class);
+ EasyMock.expect(invocation.getMethodName()).andReturn("echo").anyTimes();
+ EasyMock.expect(invocation.getParameterTypes()).andReturn(new Class<?>[] { Enum.class }).anyTimes();
+ EasyMock.expect(invocation.getArguments()).andReturn(new Object[] { "hello" }).anyTimes();
+ EasyMock.expect(invocation.getAttachments()).andReturn(null).anyTimes();
+ EasyMock.replay(invocation);
+ }
+
+ @Test
+ public void testSyncCallback() {
+ Invoker<DemoService> invoker = EasyMock.createMock(Invoker.class);
+ EasyMock.expect(invoker.isAvailable()).andReturn(true).anyTimes();
+ EasyMock.expect(invoker.getInterface()).andReturn(DemoService.class).anyTimes();
+ RpcResult result = new RpcResult();
+ result.setResult("High");
+ EasyMock.expect(invoker.invoke(invocation)).andReturn(result).anyTimes();
+ URL url = URL.valueOf("test://test:11/test?group=dubbo&version=1.1");
+ EasyMock.expect(invoker.getUrl()).andReturn(url).anyTimes();
+ EasyMock.replay(invoker);
+ Result filterResult = eventFilter.invoke(invoker, invocation);
+ assertEquals("High", filterResult.getResult());
+ }
+
+ @Test(expected = RuntimeException.class)
+ public void testSyncCallbackHasException() throws RpcException, Throwable {
+ Invocation invocation = EasyMock.createMock(Invocation.class);
+ EasyMock.expect(invocation.getMethodName()).andReturn("echo").anyTimes();
+ EasyMock.expect(invocation.getParameterTypes()).andReturn(new Class<?>[] { Enum.class }).anyTimes();
+ EasyMock.expect(invocation.getArguments()).andReturn(new Object[] { "hello" }).anyTimes();
+ EasyMock.expect(invocation.getAttachments()).andReturn(null).anyTimes();
+ EasyMock.replay(invocation);
+ Invoker<DemoService> invoker = EasyMock.createMock(Invoker.class);
+ EasyMock.expect(invoker.isAvailable()).andReturn(true).anyTimes();
+ EasyMock.expect(invoker.getInterface()).andReturn(DemoService.class).anyTimes();
+ RpcResult result = new RpcResult();
+ result.setException(new RuntimeException());
+ EasyMock.expect(invoker.invoke(invocation)).andReturn(result).anyTimes();
+ URL url = URL.valueOf("test://test:11/test?group=dubbo&version=1.1&"+RpcConstants.ON_THROW_METHOD_KEY+"=echo");
+ EasyMock.expect(invoker.getUrl()).andReturn(url).anyTimes();
+ EasyMock.replay(invoker);
+ eventFilter.invoke(invoker, invocation).recreate();
+ }
+}
\ No newline at end of file
diff --git a/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/ImplicitCallBackTest.java b/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/ImplicitCallBackTest.java
new file mode 100644
index 0000000..12d8bdc
--- /dev/null
+++ b/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/ImplicitCallBackTest.java
@@ -0,0 +1,373 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.protocol.dubbo;
+
+
+import java.io.Serializable;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+
+import junit.framework.Assert;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.utils.NetUtils;
+import com.alibaba.dubbo.rpc.Exporter;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.RpcConstants;
+import com.alibaba.dubbo.rpc.RpcContext;
+import com.alibaba.dubbo.rpc.StaticContext;
+import com.alibaba.dubbo.rpc.protocol.dubbo.support.ProtocolUtils;
+
+public class ImplicitCallBackTest{
+
+ protected Exporter<IDemoService> exporter = null;
+ protected Invoker<IDemoService> reference = null;
+
+ protected URL serviceURL = null ;
+ protected URL consumerUrl = null ;
+ Method onReturnMethod;
+ Method onThrowMethod ;
+ Method onInvokeMethod ;
+
+ @Before
+ public void setUp() throws SecurityException, NoSuchMethodException{
+ onReturnMethod = Nofify.class.getMethod("onreturn", new Class<?>[]{Person.class, Integer.class});
+ onThrowMethod = Nofify.class.getMethod("onthrow", new Class<?>[]{Throwable.class, Integer.class});
+ onInvokeMethod = Nofify.class.getMethod("oninvoke", new Class<?>[]{Integer.class});
+ }
+
+ @After
+ public void tearDown(){
+ ProtocolUtils.closeAll();
+ }
+
+ public void initOrResetService(){
+ destroyService();
+ exportService();
+ referService();
+ }
+ public void destroyService(){
+ demoProxy = null ;
+ try {
+ if (exporter!=null) exporter.unexport();
+ if (reference!=null) reference.destroy();
+ }catch (Exception e) {
+ }
+ }
+
+ void referService() {
+ demoProxy = (IDemoService)ProtocolUtils.refer(IDemoService.class, consumerUrl);
+ }
+
+ public void exportService(){
+ exporter = ProtocolUtils.export(new NormalDemoService(), IDemoService.class, serviceURL);
+ }
+ public void exportExService(){
+ exporter = ProtocolUtils.export(new ExceptionDemoExService(), IDemoService.class, serviceURL);
+ }
+
+ public void initOrResetUrl(boolean isAsync) throws Exception {
+ int port = NetUtils.getAvailablePort() ;
+ consumerUrl = serviceURL = URL.valueOf("dubbo://127.0.0.1:"+port+"/"+IDemoService.class.getName()+"?group=test&async="+isAsync+"&timeout=100000&reference.filter=future" );
+ StaticContext.getSystemContext().clear();
+ }
+
+ public void initImplicitCallBackURL_onlyOnthrow() throws Exception {
+ StaticContext.getSystemContext().put(StaticContext.getKey(consumerUrl, "get", RpcConstants.ON_THROW_METHOD_KEY),onThrowMethod);
+ StaticContext.getSystemContext().put(StaticContext.getKey(consumerUrl, "get", RpcConstants.ON_THROW_INSTANCE_KEY),notify);
+ }
+ public void initImplicitCallBackURL_onlyOnreturn() throws Exception {
+ StaticContext.getSystemContext().put(StaticContext.getKey(consumerUrl, "get", RpcConstants.ON_RETURN_METHOD_KEY),onReturnMethod);
+ StaticContext.getSystemContext().put(StaticContext.getKey(consumerUrl, "get", RpcConstants.ON_RETURN_INSTANCE_KEY),notify);
+
+ }
+ public void initImplicitCallBackURL_onlyOninvoke() throws Exception {
+ StaticContext.getSystemContext().put(StaticContext.getKey(consumerUrl, "get", RpcConstants.ON_INVOKE_METHOD_KEY),onInvokeMethod);
+ StaticContext.getSystemContext().put(StaticContext.getKey(consumerUrl, "get", RpcConstants.ON_INVOKE_INSTANCE_KEY),notify);
+ }
+
+ //================================================================================================
+
+ NofifyImpl notify = new NofifyImpl();
+
+ interface Nofify {
+ public void onreturn(Person msg, Integer id);
+ public void onthrow(Throwable ex, Integer id);
+ public void oninvoke(Integer id);
+ }
+ class NofifyImpl implements Nofify{
+ public List<Integer> inv = new ArrayList<Integer> ();
+ public Map<Integer ,Person> ret = new HashMap<Integer ,Person> ();
+ public Map<Integer ,Throwable> errors = new HashMap<Integer ,Throwable> ();
+ public boolean exd = false;
+ public void onreturn(Person msg, Integer id) {
+ System.out.println("onNotify:"+msg);
+ ret.put(id, msg);
+ }
+ public void onthrow(Throwable ex, Integer id) {
+ errors.put(id, ex);
+// ex.printStackTrace();
+ }
+ public void oninvoke(Integer id) {
+ inv.add(id);
+ }
+ }
+
+ interface IDemoService{
+ public Person get(int id);
+ }
+
+ class NormalDemoService implements IDemoService {
+ public Person get(int id){
+ return new Person(id, "charles", 4);
+ }
+ }
+ class ExceptionDemoExService implements IDemoService {
+ public Person get(int id){
+ throw new RuntimeException("request persion id is :"+ id);
+ }
+ }
+
+ public static class Person implements Serializable{
+ private static final long serialVersionUID = 1L;
+ public Person(int id, String name, int age) {
+ this.id = id;
+ this.name = name;
+ this.age = age;
+ }
+ private int id;
+ private String name ;
+ private int age;
+ public String getName() {
+ return name;
+ }
+ public void setName(String name) {
+ this.name = name;
+ }
+ public int getAge() {
+ return age;
+ }
+ public void setAge(int age) {
+ this.age = age;
+ }
+ public int getId() {
+ return id;
+ }
+ public void setId(int id) {
+ this.id = id;
+ }
+ @Override
+ public String toString() {
+ return "Person [name=" + name + ", age=" + age + "]";
+ }
+ }
+ //================================================================================================
+ IDemoService demoProxy = null;
+
+ @Test
+ public void test_CloseCallback() throws Exception {
+ initOrResetUrl(false);
+ initOrResetService() ;
+ Person ret = demoProxy.get(1);
+ Assert.assertEquals(1, ret.getId());
+ destroyService();
+ }
+
+ @Test
+ public void test_Sync_Onreturn() throws Exception {
+ initOrResetUrl(false);
+ initImplicitCallBackURL_onlyOnreturn();
+ initOrResetService() ;
+ int requestId = 2;
+ Person ret = demoProxy.get(requestId);
+ Assert.assertEquals(requestId, ret.getId());
+ for (int i = 0; i < 10; i++) {
+ if (! notify.ret.containsKey(requestId)){
+ Thread.sleep(200);
+ }else {
+ break;
+ }
+ }
+ Assert.assertEquals(requestId, notify.ret.get(requestId).getId());
+ destroyService();
+ }
+
+ @Test
+ public void test_Ex_OnReturn() throws Exception {
+ initOrResetUrl(true);
+ initImplicitCallBackURL_onlyOnreturn();
+
+ destroyService();
+ exportExService();
+ referService();
+
+ int requestId = 2;
+ Person ret = demoProxy.get(requestId);
+ Assert.assertEquals(null, ret);
+ for (int i = 0; i < 10; i++) {
+ if (! notify.errors.containsKey(requestId)){
+ Thread.sleep(200);
+ }else {
+ break;
+ }
+ }
+ Assert.assertTrue(! notify.errors.containsKey(requestId));
+ destroyService();
+ }
+
+ @Test
+ public void test_Ex_OnInvoke() throws Exception {
+ initOrResetUrl(true);
+ initImplicitCallBackURL_onlyOninvoke();
+
+ destroyService();
+ exportExService();
+ referService();
+
+ int requestId = 2;
+ Person ret = demoProxy.get(requestId);
+ Assert.assertEquals(null, ret);
+ for (int i = 0; i < 10; i++) {
+ if (! notify.inv.contains(requestId)){
+ Thread.sleep(200);
+ }else {
+ break;
+ }
+ }
+ Assert.assertTrue(notify.inv.contains(requestId));
+ destroyService();
+ }
+
+ @Test
+ public void test_Ex_Onthrow() throws Exception {
+ initOrResetUrl(true);
+ initImplicitCallBackURL_onlyOnthrow();
+
+ destroyService();
+ exportExService();
+ referService();
+
+ int requestId = 2;
+ Person ret = demoProxy.get(requestId);
+ Assert.assertEquals(null, ret);
+ for (int i = 0; i < 10; i++) {
+ if (! notify.errors.containsKey(requestId)){
+ Thread.sleep(200);
+ }else {
+ break;
+ }
+ }
+ Assert.assertTrue(notify.errors.containsKey(requestId));
+ Assert.assertTrue(notify.errors.get(requestId) instanceof Throwable);
+ destroyService();
+ }
+
+ @Test
+ public void test_Sync_NoFuture() throws Exception {
+ initOrResetUrl(false);
+ initImplicitCallBackURL_onlyOnreturn();
+ destroyService();
+ exportService();
+ referService();
+
+ int requestId = 2;
+ Person ret = demoProxy.get(requestId);
+ Assert.assertEquals(requestId, ret.getId());
+ Future<Person> pFuture = RpcContext.getContext().getFuture();
+ Assert.assertEquals(null, pFuture);
+ destroyService();
+ }
+
+ @Test
+ public void test_Async_Future() throws Exception {
+ initOrResetUrl(true);
+ destroyService();
+ exportService();
+ referService();
+
+ int requestId = 2;
+ Person ret = demoProxy.get(requestId);
+ Assert.assertEquals(null, ret);
+ Future<Person> pFuture = RpcContext.getContext().getFuture();
+ ret = pFuture.get(1000, TimeUnit.MICROSECONDS);
+ Assert.assertEquals(requestId, ret.getId());
+ destroyService();
+ }
+
+ @Test
+ public void test_Async_Future_Multi() throws Exception {
+ initOrResetUrl(true);
+ destroyService();
+ exportService();
+ referService();
+
+ int requestId1 = 1;
+ Person ret = demoProxy.get(requestId1);
+ Assert.assertEquals(null, ret);
+ Future<Person> p1Future = RpcContext.getContext().getFuture();
+
+ int requestId2 = 1;
+ Person ret2 = demoProxy.get(requestId2);
+ Assert.assertEquals(null, ret2);
+ Future<Person> p2Future = RpcContext.getContext().getFuture();
+
+ ret = p1Future.get(1000, TimeUnit.MICROSECONDS);
+ ret2 = p2Future.get(1000, TimeUnit.MICROSECONDS);
+ Assert.assertEquals(requestId1, ret.getId());
+ Assert.assertEquals(requestId2, ret.getId());
+ destroyService();
+ }
+
+ @Test(expected = RuntimeException.class)
+ public void test_Async_Future_Ex() throws Exception {
+ try{
+ initOrResetUrl(true);
+ destroyService();
+ exportExService();
+ referService();
+
+ int requestId = 2;
+ Person ret = demoProxy.get(requestId);
+ Assert.assertEquals(null, ret);
+ Future<Person> pFuture = RpcContext.getContext().getFuture();
+ ret = pFuture.get(1000, TimeUnit.MICROSECONDS);
+ Assert.assertEquals(requestId, ret.getId());
+ }finally{
+ destroyService();
+ }
+ }
+
+ @Test(expected = RuntimeException.class)
+ public void test_Normal_Ex() throws Exception {
+ initOrResetUrl(false);
+ destroyService();
+ exportExService();
+ referService();
+
+ int requestId = 2;
+ Person ret = demoProxy.get(requestId);
+ Assert.assertEquals(requestId, ret.getId());
+ }
+}
\ No newline at end of file
diff --git a/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/MultiThreadTest.java b/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/MultiThreadTest.java
new file mode 100644
index 0000000..7fd9c0f
--- /dev/null
+++ b/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/MultiThreadTest.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.protocol.dubbo;
+
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import junit.framework.TestCase;
+
+import com.alibaba.dubbo.common.ExtensionLoader;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.rpc.Exporter;
+import com.alibaba.dubbo.rpc.Protocol;
+import com.alibaba.dubbo.rpc.ProxyFactory;
+import com.alibaba.dubbo.rpc.protocol.dubbo.support.DemoService;
+import com.alibaba.dubbo.rpc.protocol.dubbo.support.DemoServiceImpl;
+
+public class MultiThreadTest extends TestCase
+{
+
+ private Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
+ private ProxyFactory proxy = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();
+
+ public void testDubboMultiThreadInvoke() throws Exception
+ {
+ Exporter<?> rpcExporter = protocol.export(proxy.getInvoker(new DemoServiceImpl(), DemoService.class, URL.valueOf("dubbo://127.0.0.1:20880/TestService")));
+
+ final AtomicInteger counter = new AtomicInteger();
+ final DemoService service = proxy.getProxy(protocol.refer(DemoService.class, URL.valueOf("dubbo://127.0.0.1:20880/TestService")));
+ assertEquals(service.getSize(new String[]{"123", "456", "789"}), 3);
+
+ final StringBuffer sb = new StringBuffer();
+ for(int i=0;i<1024*64+32;i++)
+ sb.append('A');
+ assertEquals(sb.toString(), service.echo(sb.toString()));
+
+ ExecutorService exec = Executors.newFixedThreadPool(10);
+ for(int i=0;i<10;i++)
+ {
+ final int fi = i;
+ exec.execute(new Runnable(){
+ public void run()
+ {
+ for(int i=0;i<30;i++)
+ {
+ System.out.println(fi+":"+counter.getAndIncrement());
+ assertEquals(service.echo(sb.toString()), sb.toString());
+ }
+ }
+ });
+ }
+ exec.shutdown();
+ exec.awaitTermination(10, TimeUnit.SECONDS);
+ rpcExporter.unexport();
+ }
+}
\ No newline at end of file
diff --git a/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/ProtocolsTest.java b/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/ProtocolsTest.java
new file mode 100644
index 0000000..6d5be8e
--- /dev/null
+++ b/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/ProtocolsTest.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.dubbo;
+
+
+import static junit.framework.Assert.assertEquals;
+
+import org.junit.Test;
+
+import com.alibaba.dubbo.common.ExtensionLoader;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.rpc.Protocol;
+import com.alibaba.dubbo.rpc.ProxyFactory;
+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.Type;
+import com.alibaba.dubbo.rpc.service.EchoService;
+
+/**
+ * <code>ProxiesTest</code>
+ */
+
+public class ProtocolsTest
+{
+ 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/TestService?codec=exchange")));
+ service = proxy.getProxy(protocol.refer(DemoService.class, URL.valueOf("dubbo://127.0.0.1:9020/TestService?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/TestService?service.filter=echo")));
+ service = proxy.getProxy(protocol.refer(DemoService.class, URL.valueOf("dubbo://127.0.0.1:9010/TestService")));
+ assertEquals(service.enumlength(new Type[]{}), Type.Lower);
+ assertEquals(service.getSize(null), -1);
+ assertEquals(service.getSize(new String[]{"", "", ""}), 3);
+ service.invoke("dubbo://127.0.0.1:9010/TestService", "invoke");
+
+ service = proxy.getProxy(protocol.refer(DemoService.class, URL.valueOf("dubbo://127.0.0.1:9010/TestService?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/TestService?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 testPerm() throws Exception
+ {
+ DemoService service = new DemoServiceImpl();
+ protocol.export(proxy.getInvoker(service, DemoService.class, URL.valueOf("dubbo://127.0.0.1:9050/TestService?codec=exchange")));
+ service = proxy.getProxy(protocol.refer(DemoService.class, URL.valueOf("dubbo://127.0.0.1:9050/TestService?codec=exchange")));
+ long start = System.currentTimeMillis();
+ for(int i=0;i<1000;i++)
+ service.getSize(new String[]{"", "", ""});
+ System.out.println("take:"+(System.currentTimeMillis()-start));
+ }
+}
\ No newline at end of file
diff --git a/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/RpcFilterTest.java b/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/RpcFilterTest.java
new file mode 100644
index 0000000..b89564b
--- /dev/null
+++ b/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/RpcFilterTest.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.protocol.dubbo;
+
+import junit.framework.TestCase;
+
+import com.alibaba.dubbo.common.ExtensionLoader;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.rpc.Protocol;
+import com.alibaba.dubbo.rpc.ProxyFactory;
+import com.alibaba.dubbo.rpc.protocol.dubbo.support.DemoService;
+import com.alibaba.dubbo.rpc.protocol.dubbo.support.DemoServiceImpl;
+import com.alibaba.dubbo.rpc.service.EchoService;
+
+public class RpcFilterTest extends TestCase
+{
+ private Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
+ private ProxyFactory proxy = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();
+
+ public void testRpcFilter() throws Exception
+ {
+ DemoService service = new DemoServiceImpl();
+ URL url = URL.valueOf("dubbo://127.0.0.1:9010/com.alibaba.dubbo.rpc.DemoService?service.filter=echo");
+ protocol.export(proxy.getInvoker(service, DemoService.class, url));
+ service = proxy.getProxy(protocol.refer(DemoService.class, url));
+ assertEquals("123",service.echo("123"));
+ // cast to EchoService
+ EchoService echo = proxy.getProxy(protocol.refer(EchoService.class, url));
+ assertEquals(echo.$echo("test"), "test");
+ assertEquals(echo.$echo("abcdefg"), "abcdefg");
+ assertEquals(echo.$echo(1234), 1234);
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/support/CustomArgument.java b/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/support/CustomArgument.java
new file mode 100644
index 0000000..0aead9e
--- /dev/null
+++ b/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/support/CustomArgument.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.protocol.dubbo.support;
+
+import java.io.Serializable;
+
+
+/**
+ * @author chao.liuc
+ *
+ */
+@SuppressWarnings("serial")
+public class CustomArgument implements Serializable{
+
+ public CustomArgument(){}
+
+ public CustomArgument(Type type, String name) {
+ super();
+ this.type = type;
+ this.name = name;
+ }
+ Type type;
+ String name;
+ public Type getType() {
+ return type;
+ }
+ public void setType(Type type) {
+ this.type = type;
+ }
+ public String getName() {
+ return name;
+ }
+ public void setName(String name) {
+ this.name = name;
+ }
+}
\ No newline at end of file
diff --git a/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/support/DemoRequest.java b/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/support/DemoRequest.java
new file mode 100644
index 0000000..66c6892
--- /dev/null
+++ b/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/support/DemoRequest.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.protocol.dubbo.support;
+
+import java.io.Serializable;
+
+/**
+ * TestRequest.
+ *
+ * @author qian.lei
+ */
+
+class DemoRequest implements Serializable
+{
+ private static final long serialVersionUID = -2579095288792344869L;
+
+ private String mServiceName;
+
+ private String mMethodName;
+
+ private Class<?>[] mParameterTypes;
+
+ private Object[] mArguments;
+
+ public DemoRequest(String serviceName,String methodName, Class<?>[] parameterTypes,Object[] args)
+ {
+ mServiceName = serviceName;
+ mMethodName = methodName;
+ mParameterTypes = parameterTypes;
+ mArguments = args;
+ }
+
+ public String getServiceName()
+ {
+ return mServiceName;
+ }
+
+ public String getMethodName()
+ {
+ return mMethodName;
+ }
+
+ public Class<?>[] getParameterTypes() {
+ return mParameterTypes;
+ }
+
+ public Object[] getArguments()
+ {
+ return mArguments;
+ }
+}
\ No newline at end of file
diff --git a/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/support/DemoService.java b/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/support/DemoService.java
new file mode 100644
index 0000000..d00bdbb
--- /dev/null
+++ b/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/support/DemoService.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.protocol.dubbo.support;
+
+
+
+/**
+ * <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-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/support/DemoServiceImpl.java b/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/support/DemoServiceImpl.java
new file mode 100644
index 0000000..a272d77
--- /dev/null
+++ b/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/support/DemoServiceImpl.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.support;
+
+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-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/support/EnumBak.java b/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/support/EnumBak.java
new file mode 100644
index 0000000..9d5d348
--- /dev/null
+++ b/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/support/EnumBak.java
@@ -0,0 +1,183 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.protocol.dubbo.support;
+
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.junit.Assert;
+import org.junit.Ignore;
+import org.junit.Test;
+
+import com.alibaba.dubbo.common.ExtensionLoader;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.utils.NetUtils;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.Protocol;
+import com.alibaba.dubbo.rpc.ProxyFactory;
+import com.alibaba.dubbo.rpc.service.GenericService;
+
+public class EnumBak {
+
+ private Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
+ private ProxyFactory proxy = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();
+
+ @Test
+ public void testNormal(){
+ int port = NetUtils.getAvailablePort();
+ URL serviceurl = URL.valueOf("dubbo://127.0.0.1:"+port+"/test?proxy=jdk"
+ + "&interface=com.alibaba.dubbo.rpc.DemoService"
+ + "&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());
+ System.out.println("byte:"+demoProxy.getbyte((byte)-128));
+
+// invoker.destroy();
+ reference.destroy();
+ }
+ @Ignore
+ @Test
+ public void testExportService() throws InterruptedException{
+ int port = NetUtils.getAvailablePort();
+ URL serviceurl = URL.valueOf("dubbo://127.0.0.1:"+port+"/test?proxy=jdk&timeout="+Integer.MAX_VALUE
+ );
+ DemoService demo = new DemoServiceImpl();
+ Invoker<DemoService> invoker = proxy.getInvoker(demo, DemoService.class, serviceurl);
+ protocol.export(invoker);
+ synchronized (EnumBak.class) {
+ EnumBak.class.wait();
+ }
+
+// URL consumerurl = serviceurl;
+// Invoker<DemoService> reference = protocol.refer(DemoService.class, consumerurl);
+// DemoService demoProxy = (DemoService)proxyFactory.createProxy(reference);
+//// System.out.println(demoProxy.getThreadName());
+// System.out.println("byte:"+demoProxy.getbyte((byte)-128));
+//
+// invoker.destroy();
+// reference.destroy();
+ }
+
+ @Test
+ public void testNormalEnum(){
+ int port = NetUtils.getAvailablePort();
+ URL serviceurl = URL.valueOf("dubbo://127.0.0.1:"+port+"/test?timeout="+Integer.MAX_VALUE
+ );
+ DemoService demo = new DemoServiceImpl();
+ Invoker<DemoService> invoker = proxy.getInvoker(demo, DemoService.class, serviceurl);
+ protocol.export(invoker);
+
+ URL consumerurl = serviceurl;
+ Invoker<DemoService> reference = protocol.refer(DemoService.class, consumerurl);
+ DemoService demoProxy = (DemoService)proxy.getProxy(reference);
+ Type type = demoProxy.enumlength(Type.High);
+ System.out.println(type);
+ Assert.assertEquals(Type.High, type);
+
+ invoker.destroy();
+ reference.destroy();
+ }
+// @Test
+ //测试2.0.5调用2.0.3的兼容
+ public void testEnumCompat(){
+ int port = 20880;
+ URL consumerurl = URL.valueOf("dubbo://127.0.0.1:"+port+"/test?timeout="+Integer.MAX_VALUE
+ );
+ Invoker<DemoService> reference = protocol.refer(DemoService.class, consumerurl);
+ DemoService demoProxy = (DemoService)proxy.getProxy(reference);
+ Type type = demoProxy.enumlength(Type.High);
+ System.out.println(type);
+ Assert.assertEquals(Type.High, type);
+ reference.destroy();
+ }
+// @Test
+ //测试2.0.5调用2.0.3的兼容
+ public void testGenricEnumCompat(){
+ int port = 20880;
+ URL consumerurl = URL.valueOf("dubbo://127.0.0.1:"+port+"/test?timeout="+Integer.MAX_VALUE
+ );
+ Invoker<GenericService> reference = protocol.refer(GenericService.class, consumerurl);
+
+ GenericService demoProxy = (GenericService)proxy.getProxy(reference);
+ Object obj = demoProxy.$invoke("enumlength", new String[]{Type[].class.getName()}, new Object[]{new Type[]{Type.High,Type.High}});
+ System.out.println("obj---------->"+obj);
+ reference.destroy();
+ }
+
+// @Test
+ //测试2.0.5调用2.0.3的兼容 自定义类型参数中包含enum类型
+ public void testGenricCustomArg(){
+
+ int port = 20880;
+ URL consumerurl = URL.valueOf("dubbo://127.0.0.1:"+port+"/test?timeout=2000000"
+ );
+ Invoker<GenericService> reference = protocol.refer(GenericService.class, consumerurl);
+
+ GenericService demoProxy = (GenericService)proxy.getProxy(reference);
+ Map<String, Object> arg = new HashMap<String, Object>();
+ arg.put("type", "High");
+ arg.put("name", "hi");
+
+ Object obj = demoProxy.$invoke("get", new String[]{"com.alibaba.dubbo.rpc.CustomArgument"}, new Object[]{arg});
+ System.out.println("obj---------->"+obj);
+ reference.destroy();
+ }
+
+// @Ignore
+// @Test
+ public void testGenericExport() throws InterruptedException{
+ int port = NetUtils.getAvailablePort();
+ port = 20880;
+ URL serviceurl = URL.valueOf("dubbo://127.0.0.1:"+port+"/test?timeout="+Integer.MAX_VALUE
+ );
+ DemoService demo = new DemoServiceImpl();
+ Invoker<DemoService> invoker = proxy.getInvoker(demo, DemoService.class, serviceurl);
+ protocol.export(invoker);
+
+
+ //SERVER
+ Thread.sleep(Integer.MAX_VALUE);
+ }
+
+ @Test
+ public void testGenericEnum() throws InterruptedException{
+ int port = NetUtils.getAvailablePort();
+ URL serviceurl = URL.valueOf("dubbo://127.0.0.1:"+port+"/test?timeout="+Integer.MAX_VALUE
+ );
+ DemoService demo = new DemoServiceImpl();
+ Invoker<DemoService> invoker = proxy.getInvoker(demo, DemoService.class, serviceurl);
+ protocol.export(invoker);
+
+ URL consumerurl = serviceurl;
+
+ Invoker<GenericService> reference = protocol.refer(GenericService.class, consumerurl);
+
+ GenericService demoProxy = (GenericService)proxy.getProxy(reference);
+ Object obj = demoProxy.$invoke("enumlength", new String[]{Type[].class.getName()}, new Object[]{new Type[]{Type.High,Type.High}});
+ System.out.println("obj---------->"+obj);
+
+ invoker.destroy();
+ reference.destroy();
+ }
+}
\ No newline at end of file
diff --git a/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/support/Person.java b/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/support/Person.java
new file mode 100644
index 0000000..392330a
--- /dev/null
+++ b/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/support/Person.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.protocol.dubbo.support;
+
+import java.io.Serializable;
+
+/**
+ * Person.java
+ *
+ * @author tony.chenl
+ */
+public class Person implements Serializable {
+
+ private static final long serialVersionUID = 1L;
+ private String name;
+ private int age;
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public int getAge() {
+ return age;
+ }
+
+ public void setAge(int age) {
+ this.age = age;
+ }
+}
\ No newline at end of file
diff --git a/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/support/ProtocolUtils.java b/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/support/ProtocolUtils.java
new file mode 100644
index 0000000..02981c8
--- /dev/null
+++ b/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/support/ProtocolUtils.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.protocol.dubbo.support;
+
+import java.util.Collection;
+
+import com.alibaba.dubbo.common.ExtensionLoader;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.remoting.exchange.ExchangeServer;
+import com.alibaba.dubbo.rpc.Exporter;
+import com.alibaba.dubbo.rpc.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();
+ 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));
+ }
+
+ public static void closeAll() {
+ DubboProtocol.getDubboProtocol().destroy();
+ Collection<ExchangeServer> servers = DubboProtocol.getDubboProtocol().getServers();
+ for (ExchangeServer server : servers) {
+ server.close();
+ }
+ }
+}
\ No newline at end of file
diff --git a/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/support/RemoteService.java b/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/support/RemoteService.java
new file mode 100644
index 0000000..1bf8400
--- /dev/null
+++ b/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/support/RemoteService.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.protocol.dubbo.support;
+
+import java.rmi.Remote;
+import java.rmi.RemoteException;
+
+public interface RemoteService extends Remote
+{
+ String sayHello(String name) throws RemoteException;
+
+ String getThreadName() throws RemoteException;
+}
\ No newline at end of file
diff --git a/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/support/RemoteServiceImpl.java b/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/support/RemoteServiceImpl.java
new file mode 100644
index 0000000..53aa847
--- /dev/null
+++ b/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/support/RemoteServiceImpl.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.protocol.dubbo.support;
+
+import java.rmi.RemoteException;
+
+import com.alibaba.dubbo.rpc.RpcContext;
+
+public class RemoteServiceImpl implements RemoteService
+{
+ public String getThreadName() throws RemoteException
+ {
+ System.out.println("RpcContext.getContext().getRemoteHost()="+RpcContext.getContext().getRemoteHost());
+ return Thread.currentThread().getName();
+ }
+
+ public String sayHello(String name) throws RemoteException
+ {
+ return "hello " + name + "@" + RemoteServiceImpl.class.getName();
+ }
+}
\ No newline at end of file
diff --git a/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/support/Type.java b/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/support/Type.java
new file mode 100644
index 0000000..ebbadb0
--- /dev/null
+++ b/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/support/Type.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.protocol.dubbo.support;
+
+public enum Type
+{
+ High, Normal, Lower
+}
\ No newline at end of file
diff --git a/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/telnet/ChangeTelnetHandlerTest.java b/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/telnet/ChangeTelnetHandlerTest.java
new file mode 100644
index 0000000..1e0a0ab
--- /dev/null
+++ b/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/telnet/ChangeTelnetHandlerTest.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.protocol.dubbo.telnet;
+
+import static org.junit.Assert.assertEquals;
+
+import org.easymock.EasyMock;
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.Test;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.remoting.Channel;
+import com.alibaba.dubbo.remoting.RemotingException;
+import com.alibaba.dubbo.remoting.telnet.TelnetHandler;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol;
+import com.alibaba.dubbo.rpc.protocol.dubbo.support.DemoService;
+import com.alibaba.dubbo.rpc.protocol.dubbo.support.ProtocolUtils;
+
+/**
+ * ChangeTelnetHandlerTest.java
+ *
+ * @author tony.chenl
+ */
+public class ChangeTelnetHandlerTest {
+
+ private static TelnetHandler change = new ChangeTelnetHandler();
+ private Channel mockChannel;
+ private Invoker<DemoService> mockInvoker;
+
+ @SuppressWarnings("unchecked")
+ @Before
+ public void setUp() {
+ mockChannel = EasyMock.createMock(Channel.class);
+ mockInvoker = EasyMock.createMock(Invoker.class);
+ EasyMock.expect(mockChannel.getAttribute("telnet.service")).andReturn("com.alibaba.dubbo.rpc.protocol.dubbo.support.DemoService").anyTimes();
+ mockChannel.setAttribute("telnet.service", "DemoService");
+ EasyMock.expectLastCall().anyTimes();
+ mockChannel.setAttribute("telnet.service", "com.alibaba.dubbo.rpc.protocol.dubbo.support.DemoService");
+ EasyMock.expectLastCall().anyTimes();
+ mockChannel.setAttribute("telnet.service", "demo");
+ EasyMock.expectLastCall().anyTimes();
+ mockChannel.removeAttribute("telnet.service");
+ EasyMock.expectLastCall().anyTimes();
+ EasyMock.expect(mockInvoker.getInterface()).andReturn(DemoService.class).anyTimes();
+ EasyMock.expect(mockInvoker.getUrl()).andReturn(URL.valueOf("dubbo://127.0.0.1:20883/demo")).anyTimes();
+ EasyMock.replay(mockChannel, mockInvoker);
+ }
+
+ @AfterClass
+ public static void tearDown() {
+
+ }
+
+ @After
+ public void after() {
+ ProtocolUtils.closeAll();
+ EasyMock.reset(mockChannel, mockInvoker);
+ }
+
+ @Test
+ public void testChangeSimpleName() throws RemotingException {
+ DubboProtocol.getDubboProtocol().export(mockInvoker);
+ String result = change.telnet(mockChannel, "DemoService");
+ assertEquals("Used the DemoService as default.\r\nYou can cancel default service by command: cd /", result);
+ }
+
+ @Test
+ public void testChangeName() throws RemotingException {
+ DubboProtocol.getDubboProtocol().export(mockInvoker);
+ String result = change.telnet(mockChannel, "com.alibaba.dubbo.rpc.protocol.dubbo.support.DemoService");
+ assertEquals("Used the com.alibaba.dubbo.rpc.protocol.dubbo.support.DemoService as default.\r\nYou can cancel default service by command: cd /",
+ result);
+ }
+
+ @Test
+ public void testChangePath() throws RemotingException {
+ DubboProtocol.getDubboProtocol().export(mockInvoker);
+ String result = change.telnet(mockChannel, "demo");
+ assertEquals("Used the demo as default.\r\nYou can cancel default service by command: cd /", result);
+ }
+
+ @Test
+ public void testChangeMessageNull() throws RemotingException {
+ String result = change.telnet(mockChannel, null);
+ assertEquals("Please input service name, eg: \r\ncd XxxService\r\ncd com.xxx.XxxService", result);
+ }
+
+ @Test
+ public void testChangeServiceNotExport() throws RemotingException {
+ String result = change.telnet(mockChannel, "demo");
+ assertEquals("No such service demo", result);
+ }
+
+ @Test
+ public void testChangeCancel() throws RemotingException {
+ String result = change.telnet(mockChannel, "..");
+ assertEquals("Cancelled default service com.alibaba.dubbo.rpc.protocol.dubbo.support.DemoService.", result);
+ }
+
+ @Test
+ public void testChangeCancel2() throws RemotingException {
+ String result = change.telnet(mockChannel, "/");
+ assertEquals("Cancelled default service com.alibaba.dubbo.rpc.protocol.dubbo.support.DemoService.", result);
+ }
+}
\ No newline at end of file
diff --git a/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/telnet/CurrentTelnetHandlerTest.java b/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/telnet/CurrentTelnetHandlerTest.java
new file mode 100644
index 0000000..f730e09
--- /dev/null
+++ b/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/telnet/CurrentTelnetHandlerTest.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.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;
+
+/**
+ * CountTelnetHandlerTest.java
+ *
+ * @author tony.chenl
+ */
+public class CurrentTelnetHandlerTest {
+
+ private static TelnetHandler count = new CurrentTelnetHandler();
+ private Channel mockChannel;
+
+ @After
+ public void after() {
+ EasyMock.reset(mockChannel);
+ }
+
+ @Test
+ public void testService() throws RemotingException {
+ mockChannel = EasyMock.createMock(Channel.class);
+ EasyMock.expect(mockChannel.getAttribute("telnet.service")).andReturn("com.alibaba.dubbo.rpc.protocol.dubbo.support.DemoService").anyTimes();
+ EasyMock.replay(mockChannel);
+ String result = count.telnet(mockChannel, "");
+ assertEquals("com.alibaba.dubbo.rpc.protocol.dubbo.support.DemoService", result);
+ }
+
+ @Test
+ public void testSlash() throws RemotingException {
+ mockChannel = EasyMock.createMock(Channel.class);
+ EasyMock.expect(mockChannel.getAttribute("telnet.service")).andReturn(null).anyTimes();
+ EasyMock.replay(mockChannel);
+ String result = count.telnet(mockChannel, "");
+ assertEquals("/", result);
+ }
+
+ @Test
+ public void testMessageError() throws RemotingException {
+ mockChannel = EasyMock.createMock(Channel.class);
+ EasyMock.expect(mockChannel.getAttribute("telnet.service")).andReturn(null).anyTimes();
+ EasyMock.replay(mockChannel);
+ String result = count.telnet(mockChannel, "test");
+ assertEquals("Unsupported parameter test for pwd.", result);
+ }
+}
\ No newline at end of file
diff --git a/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/telnet/InvokerTelnetHandlerTest.java b/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/telnet/InvokerTelnetHandlerTest.java
new file mode 100644
index 0000000..fab78b2
--- /dev/null
+++ b/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/telnet/InvokerTelnetHandlerTest.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.dubbo.telnet;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.util.Collection;
+
+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.exchange.ExchangeServer;
+import com.alibaba.dubbo.remoting.telnet.TelnetHandler;
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.RpcResult;
+import com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol;
+import com.alibaba.dubbo.rpc.protocol.dubbo.support.DemoService;
+import com.alibaba.dubbo.rpc.protocol.dubbo.support.ProtocolUtils;
+
+/**
+ * CountTelnetHandlerTest.java
+ *
+ * @author tony.chenl
+ */
+public class InvokerTelnetHandlerTest {
+
+ private static TelnetHandler invoke = new InvokeTelnetHandler();
+ private Channel mockChannel;
+ private Invoker<DemoService> mockInvoker;
+
+ @After
+ public void after() {
+ ProtocolUtils.closeAll();
+ }
+
+ @SuppressWarnings("unchecked")
+ @Test
+ public void testInvokeDefaultSService() throws RemotingException {
+ mockInvoker = EasyMock.createMock(Invoker.class);
+ EasyMock.expect(mockInvoker.getInterface()).andReturn(DemoService.class).anyTimes();
+ EasyMock.expect(mockInvoker.getUrl()).andReturn(URL.valueOf("dubbo://127.0.0.1:20883/demo")).anyTimes();
+ EasyMock.expect(mockInvoker.invoke((Invocation) EasyMock.anyObject())).andReturn(new RpcResult("ok")).anyTimes();
+ mockChannel = EasyMock.createMock(Channel.class);
+ EasyMock.expect(mockChannel.getAttribute("telnet.service")).andReturn("com.alibaba.dubbo.rpc.protocol.dubbo.support.DemoService").anyTimes();
+ EasyMock.expect(mockChannel.getLocalAddress()).andReturn(NetUtils.toAddress("127.0.0.1:5555")).anyTimes();
+ EasyMock.expect(mockChannel.getRemoteAddress()).andReturn(NetUtils.toAddress("127.0.0.1:20883")).anyTimes();
+ EasyMock.replay(mockChannel, mockInvoker);
+ DubboProtocol.getDubboProtocol().export(mockInvoker);
+ String result = invoke.telnet(mockChannel, "DemoService.echo(\"ok\")");
+ assertTrue(result.contains("Use default service com.alibaba.dubbo.rpc.protocol.dubbo.support.DemoService.\r\n\"ok\"\r\n"));
+ EasyMock.reset(mockChannel, mockInvoker);
+ }
+
+ @SuppressWarnings("unchecked")
+ @Test
+ public void testInvokeAutoFindMethod() throws RemotingException {
+ mockInvoker = EasyMock.createMock(Invoker.class);
+ EasyMock.expect(mockInvoker.getInterface()).andReturn(DemoService.class).anyTimes();
+ EasyMock.expect(mockInvoker.getUrl()).andReturn(URL.valueOf("dubbo://127.0.0.1:20883/demo")).anyTimes();
+ EasyMock.expect(mockInvoker.invoke((Invocation) EasyMock.anyObject())).andReturn(new RpcResult("ok")).anyTimes();
+ mockChannel = EasyMock.createMock(Channel.class);
+ EasyMock.expect(mockChannel.getAttribute("telnet.service")).andReturn(null).anyTimes();
+ EasyMock.expect(mockChannel.getLocalAddress()).andReturn(NetUtils.toAddress("127.0.0.1:5555")).anyTimes();
+ EasyMock.expect(mockChannel.getRemoteAddress()).andReturn(NetUtils.toAddress("127.0.0.1:20883")).anyTimes();
+ EasyMock.replay(mockChannel, mockInvoker);
+ DubboProtocol.getDubboProtocol().export(mockInvoker);
+ String result = invoke.telnet(mockChannel, "echo(\"ok\")");
+ assertTrue(result.contains("ok"));
+ EasyMock.reset(mockChannel, mockInvoker);
+ }
+
+ @Test
+ public void testMessageNull() throws RemotingException {
+ mockChannel = EasyMock.createMock(Channel.class);
+ EasyMock.expect(mockChannel.getAttribute("telnet.service")).andReturn(null).anyTimes();
+ EasyMock.replay(mockChannel);
+ String result = invoke.telnet(mockChannel, null);
+ assertEquals("Please input method name, eg: \r\ninvoke xxxMethod(1234, \"abcd\", {\"prop\" : \"value\"})\r\ninvoke XxxService.xxxMethod(1234, \"abcd\", {\"prop\" : \"value\"})\r\ninvoke com.xxx.XxxService.xxxMethod(1234, \"abcd\", {\"prop\" : \"value\"})",
+ result);
+ EasyMock.reset(mockChannel);
+ }
+
+ @Test
+ public void testInvaildMessage() throws RemotingException {
+ mockChannel = EasyMock.createMock(Channel.class);
+ EasyMock.expect(mockChannel.getAttribute("telnet.service")).andReturn(null).anyTimes();
+ EasyMock.replay(mockChannel);
+ String result = invoke.telnet(mockChannel, "(");
+ assertEquals("Invalid parameters, format: service.method(args)", result);
+ EasyMock.reset(mockChannel);
+ }
+}
\ No newline at end of file
diff --git a/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/telnet/ListTelnetHandlerTest.java b/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/telnet/ListTelnetHandlerTest.java
new file mode 100644
index 0000000..e481381
--- /dev/null
+++ b/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/telnet/ListTelnetHandlerTest.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.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();
+ }
+
+ @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 {
+ 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, "-l");
+ assertEquals("com.alibaba.dubbo.rpc.protocol.dubbo.support.DemoService -> dubbo://" + NetUtils.getLocalHost()
+ + ":20885/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-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/telnet/LogTelnetHandlerTest.java b/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/telnet/LogTelnetHandlerTest.java
new file mode 100644
index 0000000..5822c72
--- /dev/null
+++ b/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/telnet/LogTelnetHandlerTest.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.protocol.dubbo.telnet;
+
+import static org.junit.Assert.assertTrue;
+
+import org.easymock.EasyMock;
+import org.junit.Test;
+
+import com.alibaba.dubbo.remoting.Channel;
+import com.alibaba.dubbo.remoting.RemotingException;
+import com.alibaba.dubbo.remoting.telnet.TelnetHandler;
+
+/**
+ * LogTelnetHandlerTest.java
+ *
+ * @author tony.chenl
+ */
+public class LogTelnetHandlerTest {
+
+ private static TelnetHandler log = new LogTelnetHandler();
+ private Channel mockChannel;
+
+ @Test
+ public void testChangeLogLevel() throws RemotingException {
+ mockChannel = EasyMock.createMock(Channel.class);
+ EasyMock.replay(mockChannel);
+ String result = log.telnet(mockChannel, "error");
+ assertTrue(result.contains("\r\nCURRENT LOG LEVEL:ERROR"));
+ String result2 = log.telnet(mockChannel, "warn");
+ assertTrue(result2.contains("\r\nCURRENT LOG LEVEL:WARN"));
+ EasyMock.reset(mockChannel);
+ }
+
+ @Test
+ public void testPrintLog() throws RemotingException {
+ mockChannel = EasyMock.createMock(Channel.class);
+ EasyMock.replay(mockChannel);
+ String result = log.telnet(mockChannel, "100");
+ assertTrue(result.contains("CURRENT LOG APPENDER"));
+ EasyMock.reset(mockChannel);
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/telnet/PortTelnetHandlerTest.java b/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/telnet/PortTelnetHandlerTest.java
new file mode 100644
index 0000000..2b2bf4f
--- /dev/null
+++ b/dubbo-rpc-default/src/test/java/com/alibaba/dubbo/rpc/protocol/dubbo/telnet/PortTelnetHandlerTest.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.protocol.dubbo.telnet;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import org.easymock.EasyMock;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.utils.NetUtils;
+import com.alibaba.dubbo.remoting.RemotingException;
+import com.alibaba.dubbo.remoting.exchange.ExchangeClient;
+import com.alibaba.dubbo.remoting.exchange.Exchangers;
+import com.alibaba.dubbo.remoting.telnet.TelnetHandler;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol;
+import com.alibaba.dubbo.rpc.protocol.dubbo.support.DemoService;
+import com.alibaba.dubbo.rpc.protocol.dubbo.support.ProtocolUtils;
+
+/**
+ * PortTelnetHandlerTest.java
+ *
+ * @author tony.chenl
+ */
+public class PortTelnetHandlerTest {
+
+ private static TelnetHandler port = new PortTelnetHandler();
+ private Invoker<DemoService> mockInvoker;
+
+ @SuppressWarnings("unchecked")
+ @Before
+ public void before() {
+ mockInvoker = EasyMock.createMock(Invoker.class);
+ EasyMock.expect(mockInvoker.getInterface()).andReturn(DemoService.class).anyTimes();
+ EasyMock.expect(mockInvoker.getUrl()).andReturn(URL.valueOf("dubbo://127.0.0.1:20887/demo")).anyTimes();
+ EasyMock.replay(mockInvoker);
+ DubboProtocol.getDubboProtocol().export(mockInvoker);
+ }
+
+ @After
+ public void after() {
+ EasyMock.reset(mockInvoker);
+ ProtocolUtils.closeAll();
+ }
+
+ @Test
+ public void testListClient() throws RemotingException {
+ ExchangeClient client1 = Exchangers.connect("dubbo://127.0.0.1:20887/demo");
+ ExchangeClient client2 = Exchangers.connect("dubbo://127.0.0.1:20887/demo");
+ String result = port.telnet(null, "-l 20887");
+ assertTrue(result.contains(client1.getLocalAddress().toString()));
+ assertTrue(result.contains(client2.getLocalAddress().toString()));
+
+ }
+
+ @Test
+ public void testListDetail() throws RemotingException {
+ String result = port.telnet(null, "-l");
+ assertEquals("dubbo://" + NetUtils.getLocalHost() + ":20887", result);
+ }
+
+ @Test
+ public void testListAllPort() throws RemotingException {
+ String result = port.telnet(null, "");
+ assertEquals("20887", result);
+ }
+
+ @Test
+ public void testErrorMessage() throws RemotingException {
+ String result = port.telnet(null, "a");
+ assertEquals("Illegal port a, must be integer.", result);
+ }
+
+ @Test
+ public void testNoPort() throws RemotingException {
+ String result = port.telnet(null, "-l 20880");
+ assertEquals("No such port 20880", result);
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-rpc-default/src/test/resources/log4j.xml b/dubbo-rpc-default/src/test/resources/log4j.xml
new file mode 100644
index 0000000..788ed66
--- /dev/null
+++ b/dubbo-rpc-default/src/test/resources/log4j.xml
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ - Copyright 1999-2011 Alibaba Group.
+ -
+ - Licensed under the Apache License, Version 2.0 (the "License");
+ - you may not use this file except in compliance with the License.
+ - You may obtain a copy of the License at
+ -
+ - http://www.apache.org/licenses/LICENSE-2.0
+ -
+ - Unless required by applicable law or agreed to in writing, software
+ - distributed under the License is distributed on an "AS IS" BASIS,
+ - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ - See the License for the specific language governing permissions and
+ - limitations under the License.
+-->
+<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
+<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
+ <!-- ===================================================================== -->
+ <!-- 以下是appender的定义 -->
+ <!-- ===================================================================== -->
+ <appender name="dubbo" class="com.alibaba.dubbo.common.utils.DubboAppender">
+ <param name="encoding" value="GBK" />
+ <layout class="org.apache.log4j.PatternLayout">
+ <param name="ConversionPattern" value="%d %p [%c:%M] - %m%n" />
+ </layout>
+ <!-- <filter class="org.apache.log4j.varia.LevelRangeFilter">
+ <param name="LevelMin" value="DEBUG" />
+ <param name="LevelMax" value="DEBUG" />
+ </filter> -->
+ </appender>
+ <appender name="FILE" class="org.apache.log4j.FileAppender">
+ <param name="File" value="dubbo.log" />
+ <layout class="org.apache.log4j.PatternLayout">
+ <!-- <param name="ConversionPattern" value="[%t %d{dd/MM/yy hh:mm:ss:sss
+ z}] %5p %c{2}: %L %m%n" /> -->
+ <param name="ConversionPattern" value="[%t %l %d{dd/MM/yy hh:mm:ss:sss z}] %5p %m %n" />
+ </layout>
+ </appender>
+ <root>
+ <level value="INFO" />
+ <appender-ref ref="dubbo" />
+ <appender-ref ref="FILE" />
+ </root>
+</log4j:configuration>
\ No newline at end of file
diff --git a/dubbo-rpc-hessian/pom.xml b/dubbo-rpc-hessian/pom.xml
new file mode 100644
index 0000000..8d56852
--- /dev/null
+++ b/dubbo-rpc-hessian/pom.xml
@@ -0,0 +1,53 @@
+<!--
+ - Copyright 1999-2011 Alibaba Group.
+ -
+ - Licensed under the Apache License, Version 2.0 (the "License");
+ - you may not use this file except in compliance with the License.
+ - You may obtain a copy of the License at
+ -
+ - http://www.apache.org/licenses/LICENSE-2.0
+ -
+ - Unless required by applicable law or agreed to in writing, software
+ - distributed under the License is distributed on an "AS IS" BASIS,
+ - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ - See the License for the specific language governing permissions and
+ - limitations under the License.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>com.alibaba</groupId>
+ <artifactId>dubbo-parent</artifactId>
+ <version>2.0.8</version>
+ </parent>
+ <artifactId>dubbo-rpc-hessian</artifactId>
+ <packaging>jar</packaging>
+ <name>Dubbo Hessian RPC Module</name>
+ <description>The hessian rpc module of dubbo project</description>
+ <properties>
+ <skip_maven_deploy>false</skip_maven_deploy>
+ </properties>
+ <dependencies>
+ <dependency>
+ <groupId>com.alibaba</groupId>
+ <artifactId>dubbo-rpc</artifactId>
+ <version>${project.parent.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>com.alibaba</groupId>
+ <artifactId>dubbo-remoting-http</artifactId>
+ <version>${project.parent.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>com.caucho</groupId>
+ <artifactId>hessian</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.httpcomponents</groupId>
+ <artifactId>httpclient</artifactId>
+ <scope>provided</scope>
+ <optional>true</optional>
+ </dependency>
+ </dependencies>
+</project>
\ No newline at end of file
diff --git a/dubbo-rpc-hessian/src/main/java/com/alibaba/dubbo/rpc/protocol/hessian/HessianProtocol.java b/dubbo-rpc-hessian/src/main/java/com/alibaba/dubbo/rpc/protocol/hessian/HessianProtocol.java
new file mode 100644
index 0000000..ff6e317
--- /dev/null
+++ b/dubbo-rpc-hessian/src/main/java/com/alibaba/dubbo/rpc/protocol/hessian/HessianProtocol.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.protocol.hessian;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.remoting.http.HttpBinder;
+import com.alibaba.dubbo.remoting.http.HttpHandler;
+import com.alibaba.dubbo.remoting.http.HttpServer;
+import com.alibaba.dubbo.rpc.Exporter;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.ProxyFactory;
+import com.alibaba.dubbo.rpc.RpcException;
+import com.alibaba.dubbo.rpc.protocol.AbstractProtocol;
+
+/**
+ * http rpc support.
+ *
+ * @author qianlei
+ */
+@Extension("hessian")
+public class HessianProtocol extends AbstractProtocol {
+
+ private final Map<String, HttpServer> serverMap = new ConcurrentHashMap<String, HttpServer>();
+
+ private HttpBinder httpTransporter;
+
+ private ProxyFactory proxyFactory;
+
+ public void setHttpTransporter(HttpBinder httpTransporter) {
+ this.httpTransporter = httpTransporter;
+ }
+
+ public void setProxyFactory(ProxyFactory proxyFactory) {
+ this.proxyFactory = proxyFactory;
+ }
+
+ public int getDefaultPort() {
+ return 80;
+ }
+
+ private class HessianHandler implements HttpHandler {
+
+ public void handle(HttpServletRequest request, HttpServletResponse response)
+ throws IOException, ServletException {
+ String uri = request.getRequestURI();
+ HessianRpcExporter<?> exporter = (HessianRpcExporter<?>) exporterMap.get(uri);
+ exporter.handle(request, response);
+ }
+
+ }
+
+ public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
+ final URL url = invoker.getUrl();
+ final String uri = url.getAbsolutePath(); // service uri also exporter cache key.
+
+ String addr = url.getHost() + ":" + url.getPort();
+ HttpServer server = serverMap.get(addr);
+ if (server == null) {
+ server = httpTransporter.bind(url, new HessianHandler());
+ serverMap.put(addr, server);
+ }
+
+ HessianRpcExporter<T> exporter = new HessianRpcExporter<T>(invoker, proxyFactory) {
+ public void unexport() {
+ super.unexport();
+ exporterMap.remove(uri);
+ }
+ };
+ exporterMap.put(uri, exporter);
+ return exporter;
+ }
+
+ public <T> Invoker<T> refer(Class<T> serviceType, URL url) throws RpcException {
+ Invoker<T> invoker = new HessianRpcInvoker<T>(serviceType, url, proxyFactory);
+ invokers.add(invoker);
+ return invoker;
+ }
+
+ public void destroy() {
+ super.destroy();
+ for (String key : new ArrayList<String>(serverMap.keySet())) {
+ HttpServer server = serverMap.remove(key);
+ if (server != null) {
+ try {
+ if (logger.isInfoEnabled()) {
+ logger.info("Close hessian server " + server.getUrl());
+ }
+ server.close();
+ } catch (Throwable t) {
+ logger.warn(t.getMessage(), t);
+ }
+ }
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-rpc-hessian/src/main/java/com/alibaba/dubbo/rpc/protocol/hessian/HessianRpcExporter.java b/dubbo-rpc-hessian/src/main/java/com/alibaba/dubbo/rpc/protocol/hessian/HessianRpcExporter.java
new file mode 100644
index 0000000..6994d2e
--- /dev/null
+++ b/dubbo-rpc-hessian/src/main/java/com/alibaba/dubbo/rpc/protocol/hessian/HessianRpcExporter.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.protocol.hessian;
+
+import java.io.IOException;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import com.alibaba.dubbo.remoting.http.HttpHandler;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.ProxyFactory;
+import com.alibaba.dubbo.rpc.RpcContext;
+import com.alibaba.dubbo.rpc.protocol.AbstractExporter;
+import com.caucho.hessian.server.HessianSkeleton;
+
+/**
+ * hessian rpc exporter.
+ *
+ * @author qian.lei
+ */
+public class HessianRpcExporter<T> extends AbstractExporter<T> implements HttpHandler {
+
+ private HessianSkeleton skeleton;
+
+ public HessianRpcExporter(Invoker<T> invoker, ProxyFactory proxyFactory) {
+ super(invoker);
+ skeleton = new HessianSkeleton(proxyFactory.getProxy(invoker), invoker.getInterface());
+ }
+
+ public void handle(HttpServletRequest request, HttpServletResponse response)
+ throws IOException, ServletException {
+ if (! request.getMethod().equalsIgnoreCase("POST")) {
+ response.setStatus(500);
+ } else {
+ RpcContext.getContext().setRemoteAddress(request.getRemoteAddr(),
+ request.getRemotePort());
+ try {
+ skeleton.invoke(request.getInputStream(), response.getOutputStream());
+ } catch (Throwable e) {
+ throw new ServletException(e);
+ }
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-rpc-hessian/src/main/java/com/alibaba/dubbo/rpc/protocol/hessian/HessianRpcInvoker.java b/dubbo-rpc-hessian/src/main/java/com/alibaba/dubbo/rpc/protocol/hessian/HessianRpcInvoker.java
new file mode 100644
index 0000000..fc70d42
--- /dev/null
+++ b/dubbo-rpc-hessian/src/main/java/com/alibaba/dubbo/rpc/protocol/hessian/HessianRpcInvoker.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.protocol.hessian;
+
+import java.net.SocketTimeoutException;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.ProxyFactory;
+import com.alibaba.dubbo.rpc.Result;
+import com.alibaba.dubbo.rpc.RpcException;
+import com.alibaba.dubbo.rpc.RpcResult;
+import com.alibaba.dubbo.rpc.protocol.AbstractInvoker;
+import com.caucho.hessian.HessianException;
+import com.caucho.hessian.client.HessianConnectionException;
+import com.caucho.hessian.client.HessianConnectionFactory;
+import com.caucho.hessian.client.HessianProxyFactory;
+import com.caucho.hessian.io.HessianMethodSerializationException;
+
+/**
+ * hessian rpc invoker.
+ *
+ * @author qianlei
+ */
+public class HessianRpcInvoker<T> extends AbstractInvoker<T> {
+
+ protected static final String HESSIAN_EXCEPTION_PREFIX = HessianException.class.getPackage().getName() + "."; //fix by tony.chenl
+
+ protected Invoker<T> invoker;
+
+ protected HessianConnectionFactory hessianConnectionFactory = new HttpClientConnectionFactory();
+
+ @SuppressWarnings("unchecked")
+ public HessianRpcInvoker(Class<T> serviceType, URL url, ProxyFactory proxyFactory){
+ super(serviceType, url);
+ HessianProxyFactory hessianProxyFactory = new HessianProxyFactory();
+ String client = url.getParameter(Constants.CLIENT_KEY, Constants.DEFAULT_HTTP_CLIENT);
+ if ("httpclient".equals(client)) {
+ hessianProxyFactory.setConnectionFactory(hessianConnectionFactory);
+ } else if (client != null && client.length() > 0 && ! Constants.DEFAULT_HTTP_CLIENT.equals(client)) {
+ throw new IllegalStateException("Unsupported http protocol client=\"" + client + "\"!");
+ }
+ int timeout = url.getParameter(Constants.TIMEOUT_KEY, Constants.DEFAULT_TIMEOUT);
+ hessianProxyFactory.setConnectTimeout(timeout);
+ hessianProxyFactory.setReadTimeout(timeout);
+ invoker = proxyFactory.getInvoker((T)hessianProxyFactory.create(serviceType, url.setProtocol("http").toJavaURL(), Thread.currentThread().getContextClassLoader()), serviceType, url);
+ }
+
+ @Override
+ protected Result doInvoke(Invocation invocation) throws Throwable {
+ try {
+ Result result = invoker.invoke(invocation);
+ Throwable e = result.getException();
+ if (e != null) {
+ String name = e.getClass().getName();
+ if (name.startsWith(HESSIAN_EXCEPTION_PREFIX)) {
+ RpcException re = new RpcException("Failed to invoke remote service: " + getInterface() + ", method: "
+ + invocation.getMethodName() + ", cause: " + e.getMessage(), e);
+ throw setRpcExceptionCode(e, re);
+ }
+ }
+ return result;
+ } catch (RpcException e) {
+ throw setRpcExceptionCode(e.getCause(), e);
+ } catch (HessianException e) {
+ throw setRpcExceptionCode(e, new RpcException("Failed to invoke remote service: " + getInterface() + ", method: "
+ + invocation.getMethodName() + ", cause: " + e.getMessage(), e));
+ } catch (Throwable e) {
+ return new RpcResult(e);
+ }
+ }
+
+ private RpcException setRpcExceptionCode(Throwable e, RpcException re) {
+ if (e != null) {
+ if (e instanceof HessianConnectionException) {
+ re.setCode(RpcException.NETWORK_EXCEPTION);
+ if (e.getCause() != null) {
+ Class<?> cls = e.getCause().getClass();
+ if (SocketTimeoutException.class.equals(cls)) {
+ re.setCode(RpcException.TIMEOUT_EXCEPTION);
+ }
+ }
+ } else if (e instanceof HessianMethodSerializationException) {
+ re.setCode(RpcException.SERIALIZATION_EXCEPTION);
+ }
+ }
+ return re;
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-rpc-hessian/src/main/java/com/alibaba/dubbo/rpc/protocol/hessian/HttpClientConnection.java b/dubbo-rpc-hessian/src/main/java/com/alibaba/dubbo/rpc/protocol/hessian/HttpClientConnection.java
new file mode 100644
index 0000000..e8cf9f1
--- /dev/null
+++ b/dubbo-rpc-hessian/src/main/java/com/alibaba/dubbo/rpc/protocol/hessian/HttpClientConnection.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.protocol.hessian;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.URL;
+
+import org.apache.http.HttpResponse;
+import org.apache.http.client.HttpClient;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.entity.ByteArrayEntity;
+import org.apache.http.message.BasicHeader;
+
+import com.caucho.hessian.client.HessianConnection;
+
+/**
+ * HttpClientConnection
+ *
+ * @author william.liangf
+ */
+public class HttpClientConnection implements HessianConnection {
+
+ private final HttpClient httpClient;
+
+ private final ByteArrayOutputStream output;
+
+ private final HttpPost request;
+
+ private volatile HttpResponse response;
+
+ public HttpClientConnection(HttpClient httpClient, URL url) {
+ this.httpClient = httpClient;
+ this.output = new ByteArrayOutputStream();
+ this.request = new HttpPost(url.toString());
+ }
+
+ public void addHeader(String key, String value) {
+ request.addHeader(new BasicHeader(key, value));
+ }
+
+ public OutputStream getOutputStream() throws IOException {
+ return output;
+ }
+
+ public void sendRequest() throws IOException {
+ request.setEntity(new ByteArrayEntity(output.toByteArray()));
+ this.response = httpClient.execute(request);
+ }
+
+ public int getStatusCode() {
+ return response == null || response.getStatusLine() == null ? 0 : response.getStatusLine().getStatusCode();
+ }
+
+ public String getStatusMessage() {
+ return response == null || response.getStatusLine() == null ? null : response.getStatusLine().getReasonPhrase();
+ }
+
+ public InputStream getInputStream() throws IOException {
+ return response == null || response.getEntity() == null ? null : response.getEntity().getContent();
+ }
+
+ public void close() throws IOException {
+ HttpPost request = this.request;
+ if (request != null) {
+ request.abort();
+ }
+ }
+
+ public void destroy() throws IOException {
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-rpc-hessian/src/main/java/com/alibaba/dubbo/rpc/protocol/hessian/HttpClientConnectionFactory.java b/dubbo-rpc-hessian/src/main/java/com/alibaba/dubbo/rpc/protocol/hessian/HttpClientConnectionFactory.java
new file mode 100644
index 0000000..8829b0c
--- /dev/null
+++ b/dubbo-rpc-hessian/src/main/java/com/alibaba/dubbo/rpc/protocol/hessian/HttpClientConnectionFactory.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.protocol.hessian;
+
+import java.io.IOException;
+import java.net.URL;
+
+import org.apache.http.client.HttpClient;
+import org.apache.http.impl.client.DefaultHttpClient;
+import org.apache.http.params.HttpConnectionParams;
+
+import com.caucho.hessian.client.HessianConnection;
+import com.caucho.hessian.client.HessianConnectionFactory;
+import com.caucho.hessian.client.HessianProxyFactory;
+
+/**
+ * HttpClientConnectionFactory
+ *
+ * @author william.liangf
+ */
+public class HttpClientConnectionFactory implements HessianConnectionFactory {
+
+ private final HttpClient httpClient = new DefaultHttpClient();
+
+ public void setHessianProxyFactory(HessianProxyFactory factory) {
+ HttpConnectionParams.setConnectionTimeout(httpClient.getParams(), (int) factory.getConnectTimeout());
+ HttpConnectionParams.setSoTimeout(httpClient.getParams(), (int) factory.getReadTimeout());
+ }
+
+ public HessianConnection open(URL url) throws IOException {
+ return new HttpClientConnection(httpClient, url);
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-rpc-hessian/src/main/resources/META-INF/services/com.alibaba.dubbo.rpc.Protocol b/dubbo-rpc-hessian/src/main/resources/META-INF/services/com.alibaba.dubbo.rpc.Protocol
new file mode 100644
index 0000000..9b80f39
--- /dev/null
+++ b/dubbo-rpc-hessian/src/main/resources/META-INF/services/com.alibaba.dubbo.rpc.Protocol
@@ -0,0 +1 @@
+com.alibaba.dubbo.rpc.protocol.hessian.HessianProtocol
\ No newline at end of file
diff --git a/dubbo-rpc-hessian/src/test/java/com/alibaba/dubbo/rpc/protocol/hessian/HessianProtocolTest.java b/dubbo-rpc-hessian/src/test/java/com/alibaba/dubbo/rpc/protocol/hessian/HessianProtocolTest.java
new file mode 100644
index 0000000..d3b449e
--- /dev/null
+++ b/dubbo-rpc-hessian/src/test/java/com/alibaba/dubbo/rpc/protocol/hessian/HessianProtocolTest.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.protocol.hessian;
+
+import static org.junit.Assert.fail;
+import junit.framework.Assert;
+
+import org.junit.Test;
+
+import com.alibaba.dubbo.common.ExtensionLoader;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.rpc.Exporter;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.Protocol;
+import com.alibaba.dubbo.rpc.ProxyFactory;
+import com.alibaba.dubbo.rpc.RpcException;
+import com.alibaba.dubbo.rpc.protocol.hessian.HessianServiceImpl.MyException;
+
+/**
+ * HessianProtocolTest
+ *
+ * @author william.liangf
+ */
+public class HessianProtocolTest {
+
+ @Test
+ public void testHessianProtocol() {
+ HessianServiceImpl server = new HessianServiceImpl();
+ Assert.assertFalse(server.isCalled());
+ ProxyFactory proxyFactory = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();
+ Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
+ URL url = URL.valueOf("hessian://127.0.0.1:5342/" + HessianService.class.getName() + "?version=1.0.0");
+ Exporter<HessianService> exporter = protocol.export(proxyFactory.getInvoker(server, HessianService.class, url));
+ Invoker<HessianService> invoker = protocol.refer(HessianService.class, url);
+ HessianService client = proxyFactory.getProxy(invoker);
+ String result = client.sayHello("haha");
+ Assert.assertTrue(server.isCalled());
+ Assert.assertEquals("Hello, haha", result);
+ invoker.destroy();
+ exporter.unexport();
+ }
+
+ @Test
+ public void testHttpClient() {
+ HessianServiceImpl server = new HessianServiceImpl();
+ Assert.assertFalse(server.isCalled());
+ ProxyFactory proxyFactory = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();
+ Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
+ URL url = URL.valueOf("hessian://127.0.0.1:5342/" + HessianService.class.getName() + "?version=1.0.0&client=httpclient");
+ Exporter<HessianService> exporter = protocol.export(proxyFactory.getInvoker(server, HessianService.class, url));
+ Invoker<HessianService> invoker = protocol.refer(HessianService.class, url);
+ HessianService client = proxyFactory.getProxy(invoker);
+ String result = client.sayHello("haha");
+ Assert.assertTrue(server.isCalled());
+ Assert.assertEquals("Hello, haha", result);
+ invoker.destroy();
+ exporter.unexport();
+ }
+
+ @Test
+ public void testTimeOut() {
+ HessianServiceImpl server = new HessianServiceImpl();
+ ProxyFactory proxyFactory = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();
+ Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
+ URL url = URL.valueOf("hessian://127.0.0.1:5342/" + HessianService.class.getName() + "?version=1.0.0&timeout=10");
+ Exporter<HessianService> exporter = protocol.export(proxyFactory.getInvoker(server, HessianService.class, url));
+ Invoker<HessianService> invoker = protocol.refer(HessianService.class, url);
+ HessianService client = proxyFactory.getProxy(invoker);
+ try {
+ client.timeOut(6000);
+ fail();
+ } catch (RpcException expected) {
+ Assert.assertEquals(true, expected.isTimeout());
+ }finally{
+ invoker.destroy();
+ exporter.unexport();
+ }
+
+ }
+
+ @Test
+ public void testCustomException() {
+ HessianServiceImpl server = new HessianServiceImpl();
+ ProxyFactory proxyFactory = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();
+ Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
+ URL url = URL.valueOf("hessian://127.0.0.1:5342/" + HessianService.class.getName() + "?version=1.0.0");
+ Exporter<HessianService> exporter = protocol.export(proxyFactory.getInvoker(server, HessianService.class, url));
+ Invoker<HessianService> invoker = protocol.refer(HessianService.class, url);
+ HessianService client = proxyFactory.getProxy(invoker);
+ try {
+ client.customException();
+ fail();
+ } catch (MyException expected) {
+ }
+ invoker.destroy();
+ exporter.unexport();
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-rpc-hessian/src/test/java/com/alibaba/dubbo/rpc/protocol/hessian/HessianService.java b/dubbo-rpc-hessian/src/test/java/com/alibaba/dubbo/rpc/protocol/hessian/HessianService.java
new file mode 100644
index 0000000..133be00
--- /dev/null
+++ b/dubbo-rpc-hessian/src/test/java/com/alibaba/dubbo/rpc/protocol/hessian/HessianService.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.protocol.hessian;
+
+
+/**
+ * HessianService
+ *
+ * @author william.liangf
+ */
+public interface HessianService {
+
+ String sayHello(String name);
+
+ void timeOut(int millis);
+
+ String customException();
+
+}
\ No newline at end of file
diff --git a/dubbo-rpc-hessian/src/test/java/com/alibaba/dubbo/rpc/protocol/hessian/HessianServiceImpl.java b/dubbo-rpc-hessian/src/test/java/com/alibaba/dubbo/rpc/protocol/hessian/HessianServiceImpl.java
new file mode 100644
index 0000000..c9d7095
--- /dev/null
+++ b/dubbo-rpc-hessian/src/test/java/com/alibaba/dubbo/rpc/protocol/hessian/HessianServiceImpl.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.protocol.hessian;
+
+/**
+ * HessianServiceImpl
+ *
+ * @author william.liangf
+ */
+public class HessianServiceImpl implements HessianService {
+
+ private boolean called;
+
+ public String sayHello(String name) {
+ called = true;
+ return "Hello, " + name;
+ }
+
+ public boolean isCalled() {
+ return called;
+ }
+
+ public void timeOut(int millis) {
+ try {
+ Thread.sleep(millis);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+
+ public String customException() {
+ throw new MyException("custom exception");
+ }
+
+ static class MyException extends RuntimeException{
+
+ private static final long serialVersionUID = -3051041116483629056L;
+
+ public MyException(String message) {
+ super(message);
+ }
+ }
+}
\ No newline at end of file
diff --git a/dubbo-rpc-injvm/pom.xml b/dubbo-rpc-injvm/pom.xml
new file mode 100644
index 0000000..4def6e1
--- /dev/null
+++ b/dubbo-rpc-injvm/pom.xml
@@ -0,0 +1,38 @@
+<!--
+ - Copyright 1999-2011 Alibaba Group.
+ -
+ - Licensed under the Apache License, Version 2.0 (the "License");
+ - you may not use this file except in compliance with the License.
+ - You may obtain a copy of the License at
+ -
+ - http://www.apache.org/licenses/LICENSE-2.0
+ -
+ - Unless required by applicable law or agreed to in writing, software
+ - distributed under the License is distributed on an "AS IS" BASIS,
+ - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ - See the License for the specific language governing permissions and
+ - limitations under the License.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>com.alibaba</groupId>
+ <artifactId>dubbo-parent</artifactId>
+ <version>2.0.8</version>
+ </parent>
+ <artifactId>dubbo-rpc-injvm</artifactId>
+ <packaging>jar</packaging>
+ <name>Dubbo InJVM RPC Module</name>
+ <description>The injvm rpc module of dubbo project</description>
+ <properties>
+ <skip_maven_deploy>false</skip_maven_deploy>
+ </properties>
+ <dependencies>
+ <dependency>
+ <groupId>com.alibaba</groupId>
+ <artifactId>dubbo-rpc</artifactId>
+ <version>${project.parent.version}</version>
+ </dependency>
+ </dependencies>
+</project>
\ No newline at end of file
diff --git a/dubbo-rpc-injvm/src/main/java/com/alibaba/dubbo/rpc/protocol/injvm/InjvmExporter.java b/dubbo-rpc-injvm/src/main/java/com/alibaba/dubbo/rpc/protocol/injvm/InjvmExporter.java
new file mode 100644
index 0000000..bc234e7
--- /dev/null
+++ b/dubbo-rpc-injvm/src/main/java/com/alibaba/dubbo/rpc/protocol/injvm/InjvmExporter.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.protocol.injvm;
+
+import java.util.Map;
+
+import com.alibaba.dubbo.rpc.Exporter;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.protocol.AbstractExporter;
+
+/**
+ * InjvmExporter
+ *
+ * @author william.liangf
+ */
+class InjvmExporter<T> extends AbstractExporter<T> {
+
+ private final String key;
+
+ private final Map<String, Exporter<?>> exporterMap;
+
+ InjvmExporter(Invoker<T> invoker, String key, Map<String, Exporter<?>> exporterMap){
+ super(invoker);
+ this.key = key;
+ this.exporterMap = exporterMap;
+ exporterMap.put(key, this);
+ }
+
+ public void unexport() {
+ super.unexport();
+ exporterMap.remove(key);
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-rpc-injvm/src/main/java/com/alibaba/dubbo/rpc/protocol/injvm/InjvmInvoker.java b/dubbo-rpc-injvm/src/main/java/com/alibaba/dubbo/rpc/protocol/injvm/InjvmInvoker.java
new file mode 100644
index 0000000..eeb5f6a
--- /dev/null
+++ b/dubbo-rpc-injvm/src/main/java/com/alibaba/dubbo/rpc/protocol/injvm/InjvmInvoker.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.protocol.injvm;
+
+import java.util.Map;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.utils.NetUtils;
+import com.alibaba.dubbo.rpc.Exporter;
+import com.alibaba.dubbo.rpc.RpcContext;
+import com.alibaba.dubbo.rpc.RpcException;
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Result;
+import com.alibaba.dubbo.rpc.protocol.AbstractInvoker;
+
+/**
+ * InjvmInvoker
+ *
+ * @author william.liangf
+ */
+class InjvmInvoker<T> extends AbstractInvoker<T> {
+
+ private final String key;
+
+ private final Map<String, Exporter<?>> exporterMap;
+
+ InjvmInvoker(Class<T> type, URL url, String key, Map<String, Exporter<?>> exporterMap){
+ super(type, url);
+ this.key = key;
+ this.exporterMap = exporterMap;
+ }
+
+ public Result doInvoke(Invocation invocation) throws Throwable {
+ InjvmExporter<?> exporter = (InjvmExporter<?>) exporterMap.get(key);
+ if (exporter == null) {
+ throw new RpcException("Service [" + key + "] not found.");
+ }
+ RpcContext.getContext().setRemoteAddress(NetUtils.LOCALHOST, 0);
+ return exporter.getInvoker().invoke(invocation);
+ }
+}
\ No newline at end of file
diff --git a/dubbo-rpc-injvm/src/main/java/com/alibaba/dubbo/rpc/protocol/injvm/InjvmProtocol.java b/dubbo-rpc-injvm/src/main/java/com/alibaba/dubbo/rpc/protocol/injvm/InjvmProtocol.java
new file mode 100644
index 0000000..a3e8139
--- /dev/null
+++ b/dubbo-rpc-injvm/src/main/java/com/alibaba/dubbo/rpc/protocol/injvm/InjvmProtocol.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.protocol.injvm;
+
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.rpc.Exporter;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.Protocol;
+import com.alibaba.dubbo.rpc.RpcException;
+import com.alibaba.dubbo.rpc.protocol.AbstractProtocol;
+
+/**
+ * InjvmProtocol
+ *
+ * @author qian.lei
+ * @author william.liangf
+ */
+@Extension(InjvmProtocol.NAME)
+public class InjvmProtocol extends AbstractProtocol implements Protocol {
+
+ public static final String NAME = "injvm";
+
+ public static final int DEFAULT_PORT = 0;
+
+ public int getDefaultPort() {
+ return DEFAULT_PORT;
+ }
+
+ public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
+ return new InjvmExporter<T>(invoker, serviceKey(invoker.getUrl().setPort(DEFAULT_PORT)), exporterMap);
+ }
+
+ public <T> Invoker<T> refer(Class<T> serviceType, URL url) throws RpcException {
+ return new InjvmInvoker<T>(serviceType, url, serviceKey(url), exporterMap);
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-rpc-injvm/src/main/resources/META-INF/services/com.alibaba.dubbo.rpc.Protocol b/dubbo-rpc-injvm/src/main/resources/META-INF/services/com.alibaba.dubbo.rpc.Protocol
new file mode 100644
index 0000000..2b9dc61
--- /dev/null
+++ b/dubbo-rpc-injvm/src/main/resources/META-INF/services/com.alibaba.dubbo.rpc.Protocol
@@ -0,0 +1 @@
+com.alibaba.dubbo.rpc.protocol.injvm.InjvmProtocol
\ No newline at end of file
diff --git a/dubbo-rpc-injvm/src/test/java/com/alibaba/dubbo/rpc/protococol/injvm/DemoRequest.java b/dubbo-rpc-injvm/src/test/java/com/alibaba/dubbo/rpc/protococol/injvm/DemoRequest.java
new file mode 100644
index 0000000..961c287
--- /dev/null
+++ b/dubbo-rpc-injvm/src/test/java/com/alibaba/dubbo/rpc/protococol/injvm/DemoRequest.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.protococol.injvm;
+
+import java.io.Serializable;
+
+/**
+ * TestRequest.
+ *
+ * @author qian.lei
+ */
+
+class DemoRequest implements Serializable
+{
+ private static final long serialVersionUID = -2579095288792344869L;
+
+ private String mServiceName;
+
+ private String mMethodName;
+
+ private Class<?>[] mParameterTypes;
+
+ private Object[] mArguments;
+
+ public DemoRequest(String serviceName,String methodName, Class<?>[] parameterTypes,Object[] args)
+ {
+ mServiceName = serviceName;
+ mMethodName = methodName;
+ mParameterTypes = parameterTypes;
+ mArguments = args;
+ }
+
+ public String getServiceName()
+ {
+ return mServiceName;
+ }
+
+ public String getMethodName()
+ {
+ return mMethodName;
+ }
+
+ public Class<?>[] getParameterTypes() {
+ return mParameterTypes;
+ }
+
+ public Object[] getArguments()
+ {
+ return mArguments;
+ }
+}
\ No newline at end of file
diff --git a/dubbo-rpc-injvm/src/test/java/com/alibaba/dubbo/rpc/protococol/injvm/DemoService.java b/dubbo-rpc-injvm/src/test/java/com/alibaba/dubbo/rpc/protococol/injvm/DemoService.java
new file mode 100644
index 0000000..9b1a680
--- /dev/null
+++ b/dubbo-rpc-injvm/src/test/java/com/alibaba/dubbo/rpc/protococol/injvm/DemoService.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.protococol.injvm;
+
+/**
+ * <code>TestService</code>
+ */
+
+public interface DemoService
+{
+ void sayHello(String name);
+
+ String echo(String text);
+
+ long timestamp();
+
+ String getThreadName();
+
+ int getSize(String[] strs);
+
+ int getSize(Object[] os);
+
+ Object invoke(String service, String method) throws Exception;
+
+ int stringLength(String str);
+
+ Type enumlength(Type... types);
+}
\ No newline at end of file
diff --git a/dubbo-rpc-injvm/src/test/java/com/alibaba/dubbo/rpc/protococol/injvm/DemoServiceImpl.java b/dubbo-rpc-injvm/src/test/java/com/alibaba/dubbo/rpc/protococol/injvm/DemoServiceImpl.java
new file mode 100644
index 0000000..bdb1959
--- /dev/null
+++ b/dubbo-rpc-injvm/src/test/java/com/alibaba/dubbo/rpc/protococol/injvm/DemoServiceImpl.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.protococol.injvm;
+
+import com.alibaba.dubbo.rpc.RpcContext;
+
+/**
+ * DemoServiceImpl
+ */
+
+public class DemoServiceImpl implements DemoService
+{
+ public DemoServiceImpl()
+ {
+ super();
+ }
+
+ public void sayHello(String name) {
+ System.out.println("hello "+name);
+ }
+
+ public String echo(String text)
+ {
+ return text;
+ }
+
+ public long timestamp() {
+ return System.currentTimeMillis();
+ }
+
+ public String getThreadName()
+ {
+ return Thread.currentThread().getName();
+ }
+
+ public int getSize(String[] strs)
+ {
+ if( strs == null )
+ return -1;
+ return strs.length;
+ }
+
+ public int getSize(Object[] os)
+ {
+ if( os == null )
+ return -1;
+ return os.length;
+ }
+
+ public Object invoke(String service, String method) throws Exception
+ {
+ System.out.println("RpcContext.getContext().getRemoteHost()="+RpcContext.getContext().getRemoteHost());
+ return service + ":" + method;
+ }
+
+ public Type enumlength(Type... types)
+ {
+ if( types.length == 0 )
+ return Type.Lower;
+ return types[0];
+ }
+
+ public int stringLength(String str)
+ {
+ return str.length();
+ }
+}
\ No newline at end of file
diff --git a/dubbo-rpc-injvm/src/test/java/com/alibaba/dubbo/rpc/protococol/injvm/IEcho.java b/dubbo-rpc-injvm/src/test/java/com/alibaba/dubbo/rpc/protococol/injvm/IEcho.java
new file mode 100644
index 0000000..ec98751
--- /dev/null
+++ b/dubbo-rpc-injvm/src/test/java/com/alibaba/dubbo/rpc/protococol/injvm/IEcho.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.protococol.injvm;
+
+public interface IEcho {
+ String echo(String e);
+}
\ No newline at end of file
diff --git a/dubbo-rpc-injvm/src/test/java/com/alibaba/dubbo/rpc/protococol/injvm/InjvmProtocolTest.java b/dubbo-rpc-injvm/src/test/java/com/alibaba/dubbo/rpc/protococol/injvm/InjvmProtocolTest.java
new file mode 100644
index 0000000..2905069
--- /dev/null
+++ b/dubbo-rpc-injvm/src/test/java/com/alibaba/dubbo/rpc/protococol/injvm/InjvmProtocolTest.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.protococol.injvm;
+
+
+import static junit.framework.Assert.assertEquals;
+
+import org.junit.Test;
+
+import com.alibaba.dubbo.common.ExtensionLoader;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.rpc.Protocol;
+import com.alibaba.dubbo.rpc.ProxyFactory;
+
+/**
+ * <code>ProxiesTest</code>
+ */
+
+public class InjvmProtocolTest
+{
+ private Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
+ private ProxyFactory proxy = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();
+
+ @Test
+ public void testLocalProtocol() throws Exception
+ {
+ DemoService service = new DemoServiceImpl();
+ protocol.export(proxy.getInvoker(service, DemoService.class, URL.valueOf("injvm://127.0.0.1/TestService")));
+ service = proxy.getProxy(protocol.refer(DemoService.class, URL.valueOf("injvm://127.0.0.1/TestService")));
+ assertEquals(service.getSize(new String[]{"", "", ""}), 3);
+ service.invoke("injvm://127.0.0.1/TestService", "invoke");
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-rpc-injvm/src/test/java/com/alibaba/dubbo/rpc/protococol/injvm/ProtocolTest.java b/dubbo-rpc-injvm/src/test/java/com/alibaba/dubbo/rpc/protococol/injvm/ProtocolTest.java
new file mode 100644
index 0000000..2adaced
--- /dev/null
+++ b/dubbo-rpc-injvm/src/test/java/com/alibaba/dubbo/rpc/protococol/injvm/ProtocolTest.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.protococol.injvm;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+import static org.junit.matchers.JUnitMatchers.containsString;
+
+import org.junit.Test;
+
+import com.alibaba.dubbo.common.ExtensionLoader;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.Protocol;
+import com.alibaba.dubbo.rpc.ProxyFactory;
+
+/**
+ * @author ding.lid
+ */
+public class ProtocolTest {
+
+ IEcho echo = new IEcho() {
+ public String echo(String e) {
+ return e;
+ }
+ };
+
+ ProxyFactory proxyFactory = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getExtension("javassist");
+
+ URL url = URL.valueOf("injvm://localhost:0/com.alibaba.dubbo.rpc.support.IEcho");
+
+ Invoker<IEcho> invoker = proxyFactory.getInvoker(echo, IEcho.class, url);
+
+ @Test
+ public void test_destroyWontCloseAllProtocol() throws Exception {
+ Protocol autowireProtocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
+
+ Protocol InjvmProtocol = ExtensionLoader.getExtensionLoader(Protocol.class).getExtension("injvm");
+
+ InjvmProtocol.export(invoker);
+
+ Invoker<IEcho> refer = InjvmProtocol.refer(IEcho.class, url);
+ IEcho echoProxy = proxyFactory.getProxy(refer);
+
+ assertEquals("ok", echoProxy.echo("ok"));
+
+ try {
+ autowireProtocol.destroy();
+ } catch (UnsupportedOperationException expected) {
+ assertThat(expected.getMessage(), containsString("of interface com.alibaba.dubbo.rpc.Protocol is not adaptive method!"));
+ }
+
+ assertEquals("ok2", echoProxy.echo("ok2"));
+ }
+}
\ No newline at end of file
diff --git a/dubbo-rpc-injvm/src/test/java/com/alibaba/dubbo/rpc/protococol/injvm/Type.java b/dubbo-rpc-injvm/src/test/java/com/alibaba/dubbo/rpc/protococol/injvm/Type.java
new file mode 100644
index 0000000..f667ff6
--- /dev/null
+++ b/dubbo-rpc-injvm/src/test/java/com/alibaba/dubbo/rpc/protococol/injvm/Type.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.protococol.injvm;
+
+public enum Type
+{
+ High, Normal, Lower
+}
\ No newline at end of file
diff --git a/dubbo-rpc-rmi/pom.xml b/dubbo-rpc-rmi/pom.xml
new file mode 100644
index 0000000..5accf84
--- /dev/null
+++ b/dubbo-rpc-rmi/pom.xml
@@ -0,0 +1,44 @@
+<!--
+ - Copyright 1999-2011 Alibaba Group.
+ -
+ - Licensed under the Apache License, Version 2.0 (the "License");
+ - you may not use this file except in compliance with the License.
+ - You may obtain a copy of the License at
+ -
+ - http://www.apache.org/licenses/LICENSE-2.0
+ -
+ - Unless required by applicable law or agreed to in writing, software
+ - distributed under the License is distributed on an "AS IS" BASIS,
+ - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ - See the License for the specific language governing permissions and
+ - limitations under the License.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>com.alibaba</groupId>
+ <artifactId>dubbo-parent</artifactId>
+ <version>2.0.8</version>
+ </parent>
+ <artifactId>dubbo-rpc-rmi</artifactId>
+ <packaging>jar</packaging>
+ <name>Dubbo RMI RPC Module</name>
+ <description>The rmi rpc module of dubbo project</description>
+ <properties>
+ <skip_maven_deploy>false</skip_maven_deploy>
+ </properties>
+ <dependencies>
+ <dependency>
+ <groupId>com.alibaba</groupId>
+ <artifactId>dubbo-rpc</artifactId>
+ <version>${project.parent.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring</artifactId>
+ <scope>provided</scope>
+ <optional>true</optional>
+ </dependency>
+ </dependencies>
+</project>
\ No newline at end of file
diff --git a/dubbo-rpc-rmi/src/main/java/com/alibaba/dubbo/rpc/protocol/rmi/RmiExporter.java b/dubbo-rpc-rmi/src/main/java/com/alibaba/dubbo/rpc/protocol/rmi/RmiExporter.java
new file mode 100644
index 0000000..14ad1f2
--- /dev/null
+++ b/dubbo-rpc-rmi/src/main/java/com/alibaba/dubbo/rpc/protocol/rmi/RmiExporter.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.protocol.rmi;
+
+import java.rmi.Remote;
+import java.rmi.registry.Registry;
+import java.rmi.server.UnicastRemoteObject;
+
+import com.alibaba.dubbo.common.logger.Logger;
+import com.alibaba.dubbo.common.logger.LoggerFactory;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.protocol.AbstractExporter;
+
+/**
+ * Rmi exporter.
+ *
+ * @author qian.lei
+ */
+public class RmiExporter<T> extends AbstractExporter<T> {
+
+ private static final Logger Log = LoggerFactory.getLogger(RmiExporter.class);
+
+ private Remote remote;
+
+ private Registry registry;
+
+ public RmiExporter(Invoker<T> invoker, Remote remote, Registry registry) {
+ super(invoker);
+ this.remote = remote;
+ this.registry = registry;
+ }
+
+ public void unexport() {
+ super.unexport();
+ // unexport.
+ if (remote != null) {
+ try {
+ UnicastRemoteObject.unexportObject(remote, true);
+ } catch (Exception e) {
+ Log.warn("Unexport rmi object error.", e); //ignore it.
+ }
+ remote = null;
+ }
+ if (registry != null) {
+ try {
+ // unbind.
+ registry.unbind(getInvoker().getUrl().getPath());
+ } catch (Exception e) {
+ Log.warn("Unexport rmi object error.", e); //ignore it.
+ }
+ registry = null;
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-rpc-rmi/src/main/java/com/alibaba/dubbo/rpc/protocol/rmi/RmiInvoker.java b/dubbo-rpc-rmi/src/main/java/com/alibaba/dubbo/rpc/protocol/rmi/RmiInvoker.java
new file mode 100644
index 0000000..bcae7db
--- /dev/null
+++ b/dubbo-rpc-rmi/src/main/java/com/alibaba/dubbo/rpc/protocol/rmi/RmiInvoker.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.protocol.rmi;
+
+import java.io.IOException;
+import java.net.SocketTimeoutException;
+import java.rmi.ConnectException;
+import java.rmi.ConnectIOException;
+import java.rmi.NoSuchObjectException;
+import java.rmi.RemoteException;
+import java.rmi.StubNotFoundException;
+import java.rmi.UnknownHostException;
+import java.rmi.registry.Registry;
+
+import org.omg.CORBA.COMM_FAILURE;
+import org.omg.CORBA.CompletionStatus;
+import org.omg.CORBA.NO_RESPONSE;
+import org.omg.CORBA.SystemException;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.Result;
+import com.alibaba.dubbo.rpc.RpcException;
+import com.alibaba.dubbo.rpc.protocol.AbstractInvoker;
+
+/**
+ * RmiInvoker.
+ *
+ * @author qian.lei
+ */
+public class RmiInvoker<T> extends AbstractInvoker<T> {
+
+ private Registry registry;
+
+ private RmiProxyFactory rmiProxyFactory;
+
+ private Invoker<T> invoker;
+
+ private boolean reconnect;
+
+ public RmiInvoker(Registry registry, RmiProxyFactory rmiProxyFactory, Invoker<T> invoker) {
+ super(invoker.getInterface(), invoker.getUrl());
+ this.registry = registry;
+ this.rmiProxyFactory = rmiProxyFactory;
+ this.invoker = invoker;
+ this.reconnect = invoker.getUrl().getParameter(Constants.RECONNECT_KEY, true);
+ }
+
+ @Override
+ protected Result doInvoke(Invocation invocation) throws RpcException {
+ try {
+ Result result = invoker.invoke(invocation);
+ Throwable e = result.getException();
+ if (e != null && isConnectFailure(e) && reconnect) {
+ invoker = rmiProxyFactory.getInvoker(registry.lookup(invoker.getUrl().getPath()), invoker.getInterface(), invoker.getUrl());
+ result = invoker.invoke(invocation);
+ e = result.getException();
+ }
+ if (e != null && e instanceof RemoteException) {
+ throw setRpcExceptionCode(e, new RpcException("Failed to invoke remote service: " + getInterface() + ", method: "
+ + invocation.getMethodName() + ", url: " + invoker.getUrl() + ", cause: " + e.getMessage(), e));
+ }
+ return result;
+ } catch (RpcException e) {
+ throw setRpcExceptionCode(e.getCause(), e);
+ } catch (Throwable e) {
+ throw setRpcExceptionCode(e, new RpcException(e.getMessage(), e));
+ }
+ }
+
+ private RpcException setRpcExceptionCode(Throwable e, RpcException re) {
+ if (e != null && e.getCause() != null) {
+ Class<?> cls = e.getCause().getClass();
+ if (SocketTimeoutException.class.equals(cls)) {
+ re.setCode(RpcException.TIMEOUT_EXCEPTION);
+ } else if (IOException.class.isAssignableFrom(cls)) {
+ re.setCode(RpcException.NETWORK_EXCEPTION);
+ } else if (ClassNotFoundException.class.isAssignableFrom(cls)) {
+ re.setCode(RpcException.SERIALIZATION_EXCEPTION);
+ }
+ }
+ return re;
+ }
+
+ private static final String ORACLE_CONNECTION_EXCEPTION = "com.evermind.server.rmi.RMIConnectionException";
+
+ private static boolean isConnectFailure(Throwable ex) {
+ return (ex instanceof ConnectException || ex instanceof ConnectIOException ||
+ ex instanceof UnknownHostException || ex instanceof NoSuchObjectException ||
+ ex instanceof StubNotFoundException || isCorbaConnectFailure(ex.getCause()) ||
+ ORACLE_CONNECTION_EXCEPTION.equals(ex.getClass().getName()));
+ }
+
+ private static boolean isCorbaConnectFailure(Throwable ex) {
+ return ((ex instanceof COMM_FAILURE || ex instanceof NO_RESPONSE) &&
+ ((SystemException) ex).completed == CompletionStatus.COMPLETED_NO);
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-rpc-rmi/src/main/java/com/alibaba/dubbo/rpc/protocol/rmi/RmiProtocol.java b/dubbo-rpc-rmi/src/main/java/com/alibaba/dubbo/rpc/protocol/rmi/RmiProtocol.java
new file mode 100644
index 0000000..368a5a7
--- /dev/null
+++ b/dubbo-rpc-rmi/src/main/java/com/alibaba/dubbo/rpc/protocol/rmi/RmiProtocol.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.rpc.protocol.rmi;
+
+import java.lang.reflect.Method;
+import java.rmi.AlreadyBoundException;
+import java.rmi.NotBoundException;
+import java.rmi.Remote;
+import java.rmi.RemoteException;
+import java.rmi.registry.LocateRegistry;
+import java.rmi.registry.Registry;
+import java.rmi.server.UnicastRemoteObject;
+import java.util.ArrayList;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import javassist.CannotCompileException;
+import javassist.ClassPool;
+import javassist.CtClass;
+import javassist.CtNewMethod;
+import javassist.NotFoundException;
+import javassist.bytecode.Descriptor;
+
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.bytecode.ClassGenerator;
+import com.alibaba.dubbo.rpc.Exporter;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.RpcException;
+import com.alibaba.dubbo.rpc.protocol.AbstractProtocol;
+
+/**
+ * RmiProtocol.
+ *
+ * @author qian.lei
+ */
+@Extension("rmi")
+public class RmiProtocol extends AbstractProtocol {
+
+ public static final int DEFAULT_PORT = 1099;
+
+ private final Map<Integer, Registry> registryMap = new ConcurrentHashMap<Integer, Registry>();
+
+ private RmiProxyFactory rmiProxyFactory;
+
+ public void setRmiProxyFactory(RmiProxyFactory rmiProxyFactory) {
+ this.rmiProxyFactory = rmiProxyFactory;
+ }
+
+ public int getDefaultPort() {
+ return DEFAULT_PORT;
+ }
+
+ @SuppressWarnings("unchecked")
+ public static <T> Class<T> getRemoteClass(Class<T> type) {
+ if (Remote.class.isAssignableFrom(type)) {
+ return type;
+ }
+ try {
+ String remoteType = type.getName() + "$Remote";
+ try {
+ return (Class<T>) Class.forName(remoteType, true, type.getClassLoader());
+ } catch (ClassNotFoundException e) {
+ ClassPool pool = ClassGenerator.getClassPool(type.getClassLoader());
+ CtClass ctClass = pool.makeInterface(remoteType);
+ ctClass.addInterface(getCtClass(pool, Remote.class.getName()));
+ Method[] methods = type.getMethods();
+ for (Method method : methods) {
+ CtClass[] parameters = new CtClass[method.getParameterTypes().length];
+ int i = 0;
+ for (Class<?> pt : method.getParameterTypes()) {
+ parameters[i++] = getCtClass(pool, pt.getCanonicalName());
+ }
+ CtClass[] exceptions = new CtClass[method.getExceptionTypes().length + 1];
+ exceptions[0] = getCtClass(pool, RemoteException.class.getName());
+ i = 1;
+ for (Class<?> et : method.getExceptionTypes()) {
+ exceptions[i++] = getCtClass(pool, et.getCanonicalName());
+ }
+ ctClass.addMethod(CtNewMethod.abstractMethod(
+ getCtClass(pool, method.getReturnType().getCanonicalName()),
+ method.getName(), parameters, exceptions, ctClass));
+ }
+ return ctClass.toClass();
+ }
+ } catch (CannotCompileException e) {
+ throw new IllegalStateException(e.getMessage(), e);
+ } catch (NotFoundException e) {
+ throw new IllegalStateException(e.getMessage(), e);
+ }
+ }
+ //fix javassist version problem (getCtClass is since 3.8.5 ,jboss )
+ private static CtClass getCtClass(ClassPool pool, String classname) throws NotFoundException{
+ if (classname.charAt(0) == '[')
+ return Descriptor.toCtClass(classname, pool);
+ else
+ return pool.get(classname);
+ }
+
+ public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
+ Remote remote = rmiProxyFactory.getProxy(invoker);
+ // export.
+ try {
+ UnicastRemoteObject.exportObject(remote, 0);
+ } catch (RemoteException e) {
+ if ("object already exported".equalsIgnoreCase(e.getMessage())) {
+ logger.warn("Ignore 'object already exported' exception.", e);
+ } else {
+ throw new RpcException("Export rmi service error.", e);
+ }
+ }
+ // register.
+ Registry registry = getOrCreateRegistry(invoker.getUrl().getPort());
+ try {
+ // bind service.
+ registry.bind(invoker.getUrl().getPath(), remote);
+ } catch (RemoteException e) {
+ throw new RpcException("Bind rmi service [" + invoker.getUrl().getPath() + "] error.",
+ e);
+ } catch (AlreadyBoundException e) {
+ throw new RpcException("Bind rmi service error. Service name ["
+ + invoker.getUrl().getPath() + "] already bound.", e);
+ }
+ RmiExporter<T> exporter = new RmiExporter<T>(invoker, remote, registry);
+ exporterMap.put(serviceKey(invoker.getUrl()), exporter);
+ return exporter;
+ }
+
+ public <T> Invoker<T> refer(Class<T> serviceType, URL url) throws RpcException {
+ Invoker<T> invoker;
+ try {
+ try {
+ if ("dubbo".equals(url.getParameter("codec"))) {
+ RmiProtocol.getRemoteClass(serviceType);
+ }
+ } catch (Throwable t) {
+ logger.warn(t.getMessage(), t);
+ }
+ Registry registry = LocateRegistry.getRegistry(url.getHost(), url.getPort());
+ String path = url.getPath();
+ if (path == null || path.length() == 0) {
+ path = serviceType.getName();
+ }
+ invoker = new RmiInvoker<T>(registry, rmiProxyFactory, rmiProxyFactory.getInvoker(registry.lookup(path), serviceType, url));
+ } catch (RemoteException e) {
+ Throwable cause = e.getCause();
+ boolean isExportedBySpringButNoSpringClass = ClassNotFoundException.class
+ .isInstance(cause)
+ && cause.getMessage().contains(
+ "org.springframework.remoting.rmi.RmiInvocationHandler");
+
+ String msg = String
+ .format("Can not create remote object%s. url = %s",
+ isExportedBySpringButNoSpringClass ? "(Rmi object is exported by spring rmi but NO spring class org.springframework.remoting.rmi.RmiInvocationHandler at consumer side)"
+ : "", url);
+ throw new RpcException(msg, e);
+ } catch (NotBoundException e) {
+ throw new RpcException("Rmi service not found. url = " + url, e);
+ }
+ invokers.add(invoker);
+ return invoker;
+ }
+
+ protected Registry getOrCreateRegistry(int port) {
+ Registry registry = registryMap.get(port);
+ if (registry == null) {
+ try {
+ registry = LocateRegistry.createRegistry(port);
+ } catch (RemoteException e) {
+ throw new IllegalStateException("Failed to create rmi registry on port " + port
+ + ", cause: " + e.getMessage(), e);
+ }
+ registryMap.put(port, registry);
+ }
+ return registry;
+ }
+
+ public void destroy() {
+ super.destroy();
+ for (Integer key : new ArrayList<Integer>(registryMap.keySet())) {
+ Registry registry = registryMap.remove(key);
+ if (registry != null) {
+ try {
+ String[] services = registry.list();
+ if (services != null && services.length > 0) {
+ for (String service : services) {
+ if (logger.isInfoEnabled()) {
+ logger.info("Unbind rmi service: " + service);
+ }
+ registry.unbind(service);
+ }
+ }
+ } catch (Throwable t) {
+ logger.warn(t.getMessage(), t);
+ }
+ }
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-rpc-rmi/src/main/java/com/alibaba/dubbo/rpc/protocol/rmi/RmiProxyFactory.java b/dubbo-rpc-rmi/src/main/java/com/alibaba/dubbo/rpc/protocol/rmi/RmiProxyFactory.java
new file mode 100644
index 0000000..740bf80
--- /dev/null
+++ b/dubbo-rpc-rmi/src/main/java/com/alibaba/dubbo/rpc/protocol/rmi/RmiProxyFactory.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.protocol.rmi;
+
+import java.rmi.Remote;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.rpc.Invoker;
+
+/**
+ * RmiProxyFactory
+ *
+ * @author william.liangf
+ */
+public interface RmiProxyFactory {
+
+ <T> Remote getProxy(Invoker<T> invoker);
+
+ <T> Invoker<T> getInvoker(Remote remote, Class<T> serviceType, URL url);
+
+ boolean isSupported(Remote remote, Class<?> serviceType, URL url);
+
+}
\ No newline at end of file
diff --git a/dubbo-rpc-rmi/src/main/java/com/alibaba/dubbo/rpc/protocol/rmi/proxy/DubboRmiProxyFactory.java b/dubbo-rpc-rmi/src/main/java/com/alibaba/dubbo/rpc/protocol/rmi/proxy/DubboRmiProxyFactory.java
new file mode 100644
index 0000000..f373a01
--- /dev/null
+++ b/dubbo-rpc-rmi/src/main/java/com/alibaba/dubbo/rpc/protocol/rmi/proxy/DubboRmiProxyFactory.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.protocol.rmi.proxy;
+
+import java.lang.reflect.Method;
+import java.rmi.Remote;
+import java.rmi.RemoteException;
+import java.rmi.server.RemoteServer;
+import java.rmi.server.ServerNotActiveException;
+
+import javassist.CannotCompileException;
+import javassist.ClassPool;
+import javassist.CtClass;
+import javassist.CtNewMethod;
+import javassist.NotFoundException;
+
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.bytecode.ClassGenerator;
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.ProxyFactory;
+import com.alibaba.dubbo.rpc.Result;
+import com.alibaba.dubbo.rpc.RpcContext;
+import com.alibaba.dubbo.rpc.RpcException;
+import com.alibaba.dubbo.rpc.protocol.rmi.RmiProxyFactory;
+
+/**
+ * DubboRmiProxyFactory
+ *
+ * @author william.liangf
+ */
+@Extension("dubbo")
+public class DubboRmiProxyFactory implements RmiProxyFactory {
+
+ private ProxyFactory proxyFactory;
+
+ public void setProxyFactory(ProxyFactory proxyFactory) {
+ this.proxyFactory = proxyFactory;
+ }
+
+ public boolean isSupported(Remote remote, Class<?> serviceType, URL url) {
+ for (Class<?> i : remote.getClass().getInterfaces()) {
+ if (i.getName().endsWith("$Remote")) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public <T> Remote getProxy(final Invoker<T> invoker) {
+ final Class<T> remoteClass = getRemoteClass(invoker.getInterface());
+ return (Remote) proxyFactory.getProxy(new Invoker<T>() {
+ public Class<T> getInterface() {
+ return remoteClass;
+ }
+
+ public URL getUrl() {
+ return invoker.getUrl();
+ }
+
+ public boolean isAvailable() {
+ return true;
+ }
+
+ public Result invoke(Invocation invocation) throws RpcException {
+ String client = null;
+ try {
+ client = RemoteServer.getClientHost();
+ } catch (ServerNotActiveException e) {
+ // Ignore it.
+ }
+ RpcContext.getContext().setRemoteAddress(client, 0);
+ return invoker.invoke(invocation);
+ }
+
+ public void destroy() {
+ }
+ });
+ }
+
+ @SuppressWarnings("unchecked")
+ public <T> Invoker<T> getInvoker(Remote remote, Class<T> serviceType, URL url) {
+ return proxyFactory.getInvoker((T) remote, serviceType, url);
+ }
+
+ @SuppressWarnings("unchecked")
+ public static <T> Class<T> getRemoteClass(Class<T> type) {
+ if (Remote.class.isAssignableFrom(type)) {
+ return type;
+ }
+ try {
+ String remoteType = type.getName() + "$Remote";
+ try {
+ return (Class<T>) Class.forName(remoteType, true, type.getClassLoader());
+ } catch (ClassNotFoundException e) {
+ ClassPool pool = ClassGenerator.getClassPool(type.getClassLoader());
+ CtClass ctClass = pool.makeInterface(remoteType);
+ // ctClass.addInterface(pool.getCtClass(type.getName()));
+ ctClass.addInterface(pool.getCtClass(Remote.class.getName()));
+ Method[] methods = type.getMethods();
+ for (Method method : methods) {
+ CtClass[] parameters = new CtClass[method.getParameterTypes().length];
+ int i = 0;
+ for (Class<?> pt : method.getParameterTypes()) {
+ parameters[i++] = pool.getCtClass(pt.getCanonicalName());
+ }
+ CtClass[] exceptions = new CtClass[method.getExceptionTypes().length + 1];
+ exceptions[0] = pool.getCtClass(RemoteException.class.getName());
+ i = 1;
+ for (Class<?> et : method.getExceptionTypes()) {
+ exceptions[i++] = pool.getCtClass(et.getCanonicalName());
+ }
+ ctClass.addMethod(CtNewMethod.abstractMethod(
+ pool.getCtClass(method.getReturnType().getCanonicalName()),
+ method.getName(), parameters, exceptions, ctClass));
+ }
+ return ctClass.toClass();
+ }
+ } catch (CannotCompileException e) {
+ throw new IllegalStateException(e.getMessage(), e);
+ } catch (NotFoundException e) {
+ throw new IllegalStateException(e.getMessage(), e);
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-rpc-rmi/src/main/java/com/alibaba/dubbo/rpc/protocol/rmi/proxy/NativeProxyFactory.java b/dubbo-rpc-rmi/src/main/java/com/alibaba/dubbo/rpc/protocol/rmi/proxy/NativeProxyFactory.java
new file mode 100644
index 0000000..9150298
--- /dev/null
+++ b/dubbo-rpc-rmi/src/main/java/com/alibaba/dubbo/rpc/protocol/rmi/proxy/NativeProxyFactory.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.protocol.rmi.proxy;
+
+import java.rmi.Remote;
+import java.rmi.server.RemoteServer;
+import java.rmi.server.ServerNotActiveException;
+
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.ProxyFactory;
+import com.alibaba.dubbo.rpc.Result;
+import com.alibaba.dubbo.rpc.RpcContext;
+import com.alibaba.dubbo.rpc.RpcException;
+import com.alibaba.dubbo.rpc.protocol.rmi.RmiProxyFactory;
+
+/**
+ * DefaultProxyFactoryAdaptive
+ *
+ * @author william.liangf
+ */
+@Extension("native")
+public class NativeProxyFactory implements RmiProxyFactory {
+
+ private ProxyFactory proxyFactory;
+
+ public void setProxyFactory(ProxyFactory proxyFactory) {
+ this.proxyFactory = proxyFactory;
+ }
+
+ public <T> Remote getProxy(final Invoker<T> invoker) {
+ return (Remote) proxyFactory.getProxy(new Invoker<T>() {
+ public Class<T> getInterface() {
+ return invoker.getInterface();
+ }
+
+ public URL getUrl() {
+ return invoker.getUrl();
+ }
+
+ public boolean isAvailable() {
+ return true;
+ }
+
+ public Result invoke(Invocation invocation) throws RpcException {
+ String client = null;
+ try {
+ client = RemoteServer.getClientHost();
+ } catch (ServerNotActiveException e) {
+ // Ignore it.
+ }
+ RpcContext.getContext().setRemoteAddress(client, 0);
+ return invoker.invoke(invocation);
+ }
+
+ public void destroy() {
+ }
+ });
+ }
+
+ public boolean isSupported(Remote remote, Class<?> serviceType, URL url) {
+ return Remote.class.isAssignableFrom(serviceType) && serviceType.isInstance(remote);
+ }
+
+ @SuppressWarnings("unchecked")
+ public <T> Invoker<T> getInvoker(Remote remote, Class<T> serviceType, URL url) {
+ return proxyFactory.getInvoker((T) remote, serviceType, url);
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-rpc-rmi/src/main/java/com/alibaba/dubbo/rpc/protocol/rmi/proxy/RmiProxyFactoryAdaptive.java b/dubbo-rpc-rmi/src/main/java/com/alibaba/dubbo/rpc/protocol/rmi/proxy/RmiProxyFactoryAdaptive.java
new file mode 100644
index 0000000..37ce73b
--- /dev/null
+++ b/dubbo-rpc-rmi/src/main/java/com/alibaba/dubbo/rpc/protocol/rmi/proxy/RmiProxyFactoryAdaptive.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.protocol.rmi.proxy;
+
+import java.rmi.Remote;
+
+import com.alibaba.dubbo.common.Adaptive;
+import com.alibaba.dubbo.common.ExtensionLoader;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.protocol.rmi.RmiProxyFactory;
+
+/**
+ * RmiProxyFactoryAdaptive
+ *
+ * @author william.liangf
+ */
+@Adaptive
+public class RmiProxyFactoryAdaptive implements RmiProxyFactory {
+
+ public <T> Remote getProxy(Invoker<T> invoker) {
+ ExtensionLoader<RmiProxyFactory> extensionLoader = ExtensionLoader.getExtensionLoader(RmiProxyFactory.class);
+ String name = invoker.getUrl().getParameter("codec", "spring");
+ if (name != null && name.length() > 0 && ! extensionLoader.hasExtension(name)) {
+ throw new IllegalArgumentException("Unsupported protocol codec " + name
+ + " for protocol RMI, Only support: " + extensionLoader.getSupportedExtensions());
+ }
+ if (Remote.class.isAssignableFrom(invoker.getInterface())) {
+ name = "native";
+ }
+ return extensionLoader.getExtension(name).getProxy(invoker);
+ }
+
+ public <T> Invoker<T> getInvoker(Remote remote, Class<T> serviceType, URL url) {
+ ExtensionLoader<RmiProxyFactory> extensionLoader = ExtensionLoader.getExtensionLoader(RmiProxyFactory.class);
+ for (String name : extensionLoader.getSupportedExtensions()) {
+ RmiProxyFactory rmiProxyFactory = extensionLoader.getExtension(name);
+ if (rmiProxyFactory.isSupported(remote, serviceType, url)) {
+ return rmiProxyFactory.getInvoker(remote, serviceType, url);
+ }
+ }
+ throw new UnsupportedOperationException("Unsupported remote stub " + remote + " by type " + extensionLoader.getSupportedExtensions() + ", service: " + serviceType.getName() + ", url" + url);
+ }
+
+ public boolean isSupported(Remote remote, Class<?> serviceType, URL url) {
+ return true;
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-rpc-rmi/src/main/java/com/alibaba/dubbo/rpc/protocol/rmi/proxy/SpringRmiProxyFactory.java b/dubbo-rpc-rmi/src/main/java/com/alibaba/dubbo/rpc/protocol/rmi/proxy/SpringRmiProxyFactory.java
new file mode 100644
index 0000000..0f6556b
--- /dev/null
+++ b/dubbo-rpc-rmi/src/main/java/com/alibaba/dubbo/rpc/protocol/rmi/proxy/SpringRmiProxyFactory.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.protocol.rmi.proxy;
+
+import java.lang.reflect.InvocationTargetException;
+import java.rmi.Remote;
+import java.rmi.RemoteException;
+import java.rmi.server.RemoteServer;
+import java.rmi.server.ServerNotActiveException;
+
+import org.springframework.remoting.rmi.RmiInvocationHandler;
+import org.springframework.remoting.support.RemoteInvocation;
+
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.utils.ReflectUtils;
+import com.alibaba.dubbo.common.utils.StringUtils;
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.Result;
+import com.alibaba.dubbo.rpc.RpcContext;
+import com.alibaba.dubbo.rpc.RpcException;
+import com.alibaba.dubbo.rpc.RpcInvocation;
+import com.alibaba.dubbo.rpc.RpcResult;
+import com.alibaba.dubbo.rpc.protocol.rmi.RmiProxyFactory;
+
+/**
+ * SpringRmiProxyFactory
+ *
+ * @author william.liangf
+ */
+@Extension("spring")
+public class SpringRmiProxyFactory implements RmiProxyFactory {
+
+ public boolean isSupported(Remote remote, Class<?> serviceType, URL url) {
+ return ReflectUtils.isInstance(remote, "org.springframework.remoting.rmi.RmiInvocationHandler");
+ }
+
+ private static void assertRmiInvocationHandler() {
+ try {
+ Class.forName("org.springframework.remoting.rmi.RmiInvocationHandler");
+ } catch (ClassNotFoundException e1) {
+ throw new RpcException(
+ "set codec spring for protocol rmi,"
+ + " but NO spring class org.springframework.remoting.rmi.RmiInvocationHandler at provider side!");
+ }
+ }
+
+ public <T> Remote getProxy(final Invoker<T> invoker) {
+ assertRmiInvocationHandler();
+ return new org.springframework.remoting.rmi.RmiInvocationHandler() {
+ public Object invoke(RemoteInvocation invocation) throws RemoteException,
+ NoSuchMethodException, IllegalAccessException, InvocationTargetException {
+ String client = null;
+ try {
+ client = RemoteServer.getClientHost();
+ } catch (ServerNotActiveException e) {
+ // Ignore it.
+ }
+ Invocation inv = new RpcInvocation(invocation.getMethodName(),
+ invocation.getParameterTypes(), invocation.getArguments());
+ try {
+ RpcContext.getContext().setRemoteAddress(client, 0);
+ return invoker.invoke(inv).recreate();
+ } catch (RpcException e) {
+ throw new RemoteException(StringUtils.toString(e));
+ } catch (Throwable t) {
+ throw new InvocationTargetException(t);
+ }
+ }
+ public String getTargetInterfaceName() throws RemoteException {
+ return invoker.getInterface().getName();
+ }
+ };
+ }
+
+ public <T> Invoker<T> getInvoker(final Remote remote, final Class<T> serviceType, final URL url) {
+ assertRmiInvocationHandler();
+ return new Invoker<T>() {
+ public Class<T> getInterface() {
+ return serviceType;
+ }
+
+ public URL getUrl() {
+ return url;
+ }
+
+ public boolean isAvailable() {
+ return true;
+ }
+
+ public Result invoke(Invocation invocation) throws RpcException {
+ RpcResult result = new RpcResult();
+ try {
+ RemoteInvocation i = new RemoteInvocation();
+ i.setMethodName(invocation.getMethodName());
+ i.setParameterTypes(invocation.getParameterTypes());
+ i.setArguments(invocation.getArguments());
+ result.setResult(((RmiInvocationHandler) remote).invoke(i));
+ } catch (InvocationTargetException e) {
+ result.setException(e.getTargetException());
+ } catch (Exception e) {
+ throw new RpcException(StringUtils.toString(e), e);
+ }
+ return result;
+ }
+
+ public void destroy() {
+ }
+ };
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-rpc-rmi/src/main/resources/META-INF/services/com.alibaba.dubbo.rpc.Protocol b/dubbo-rpc-rmi/src/main/resources/META-INF/services/com.alibaba.dubbo.rpc.Protocol
new file mode 100644
index 0000000..62809e8
--- /dev/null
+++ b/dubbo-rpc-rmi/src/main/resources/META-INF/services/com.alibaba.dubbo.rpc.Protocol
@@ -0,0 +1 @@
+com.alibaba.dubbo.rpc.protocol.rmi.RmiProtocol
\ No newline at end of file
diff --git a/dubbo-rpc-rmi/src/main/resources/META-INF/services/com.alibaba.dubbo.rpc.protocol.rmi.RmiProxyFactory b/dubbo-rpc-rmi/src/main/resources/META-INF/services/com.alibaba.dubbo.rpc.protocol.rmi.RmiProxyFactory
new file mode 100644
index 0000000..2c2b9fc
--- /dev/null
+++ b/dubbo-rpc-rmi/src/main/resources/META-INF/services/com.alibaba.dubbo.rpc.protocol.rmi.RmiProxyFactory
@@ -0,0 +1,4 @@
+com.alibaba.dubbo.rpc.protocol.rmi.proxy.RmiProxyFactoryAdaptive
+com.alibaba.dubbo.rpc.protocol.rmi.proxy.NativeProxyFactory
+com.alibaba.dubbo.rpc.protocol.rmi.proxy.DubboRmiProxyFactory
+com.alibaba.dubbo.rpc.protocol.rmi.proxy.SpringRmiProxyFactory
diff --git a/dubbo-rpc-rmi/src/test/java/com/alibaba/dubbo/rpc/protocol/rmi/DemoService.java b/dubbo-rpc-rmi/src/test/java/com/alibaba/dubbo/rpc/protocol/rmi/DemoService.java
new file mode 100644
index 0000000..6bcb769
--- /dev/null
+++ b/dubbo-rpc-rmi/src/test/java/com/alibaba/dubbo/rpc/protocol/rmi/DemoService.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.protocol.rmi;
+
+/**
+ * <code>TestService</code>
+ */
+
+public interface DemoService
+{
+ void sayHello(String name);
+
+ String echo(String text);
+
+ long timestamp();
+
+ void throwTimeout();
+
+ String getThreadName();
+
+ int getSize(String[] strs);
+
+ int getSize(Object[] os);
+
+ Object invoke(String service, String method) throws Exception;
+
+ int stringLength(String str);
+
+ Type enumlength(Type... types);
+}
\ No newline at end of file
diff --git a/dubbo-rpc-rmi/src/test/java/com/alibaba/dubbo/rpc/protocol/rmi/DemoServiceImpl.java b/dubbo-rpc-rmi/src/test/java/com/alibaba/dubbo/rpc/protocol/rmi/DemoServiceImpl.java
new file mode 100644
index 0000000..5359122
--- /dev/null
+++ b/dubbo-rpc-rmi/src/test/java/com/alibaba/dubbo/rpc/protocol/rmi/DemoServiceImpl.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.protocol.rmi;
+
+import com.alibaba.dubbo.rpc.RpcContext;
+
+/**
+ * DemoServiceImpl
+ */
+
+public class DemoServiceImpl implements DemoService
+{
+ public DemoServiceImpl()
+ {
+ super();
+ }
+
+ public void sayHello(String name) {
+ System.out.println("hello "+name);
+ }
+
+ public String echo(String text)
+ {
+ return text;
+ }
+
+ public long timestamp() {
+ return System.currentTimeMillis();
+ }
+
+ public String getThreadName()
+ {
+ return Thread.currentThread().getName();
+ }
+
+ public int getSize(String[] strs)
+ {
+ if( strs == null )
+ return -1;
+ return strs.length;
+ }
+
+ public int getSize(Object[] os)
+ {
+ if( os == null )
+ return -1;
+ return os.length;
+ }
+
+ public Object invoke(String service, String method) throws Exception
+ {
+ System.out.println("RpcContext.getContext().getRemoteHost()="+RpcContext.getContext().getRemoteHost());
+ return service + ":" + method;
+ }
+
+ public Type enumlength(Type... types)
+ {
+ if( types.length == 0 )
+ return Type.Lower;
+ return types[0];
+ }
+
+ public int stringLength(String str)
+ {
+ return str.length();
+ }
+
+ public void throwTimeout() {
+ try {
+ Thread.sleep(6000);
+ } catch (InterruptedException e) {
+ }
+ }
+}
\ No newline at end of file
diff --git a/dubbo-rpc-rmi/src/test/java/com/alibaba/dubbo/rpc/protocol/rmi/RemoteService.java b/dubbo-rpc-rmi/src/test/java/com/alibaba/dubbo/rpc/protocol/rmi/RemoteService.java
new file mode 100644
index 0000000..dc7a98c
--- /dev/null
+++ b/dubbo-rpc-rmi/src/test/java/com/alibaba/dubbo/rpc/protocol/rmi/RemoteService.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.protocol.rmi;
+
+import java.rmi.Remote;
+import java.rmi.RemoteException;
+
+public interface RemoteService extends Remote
+{
+ String sayHello(String name) throws RemoteException;
+
+ String getThreadName() throws RemoteException;
+}
\ No newline at end of file
diff --git a/dubbo-rpc-rmi/src/test/java/com/alibaba/dubbo/rpc/protocol/rmi/RemoteServiceImpl.java b/dubbo-rpc-rmi/src/test/java/com/alibaba/dubbo/rpc/protocol/rmi/RemoteServiceImpl.java
new file mode 100644
index 0000000..33ab4eb
--- /dev/null
+++ b/dubbo-rpc-rmi/src/test/java/com/alibaba/dubbo/rpc/protocol/rmi/RemoteServiceImpl.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.protocol.rmi;
+
+import java.rmi.RemoteException;
+
+import com.alibaba.dubbo.rpc.RpcContext;
+
+public class RemoteServiceImpl implements RemoteService
+{
+ public String getThreadName() throws RemoteException
+ {
+ System.out.println("RpcContext.getContext().getRemoteHost()="+RpcContext.getContext().getRemoteHost());
+ return Thread.currentThread().getName();
+ }
+
+ public String sayHello(String name) throws RemoteException
+ {
+ return "hello " + name + "@" + RemoteServiceImpl.class.getName();
+ }
+}
\ No newline at end of file
diff --git a/dubbo-rpc-rmi/src/test/java/com/alibaba/dubbo/rpc/protocol/rmi/RmiProtocolTest.java b/dubbo-rpc-rmi/src/test/java/com/alibaba/dubbo/rpc/protocol/rmi/RmiProtocolTest.java
new file mode 100644
index 0000000..4dfbf88
--- /dev/null
+++ b/dubbo-rpc-rmi/src/test/java/com/alibaba/dubbo/rpc/protocol/rmi/RmiProtocolTest.java
@@ -0,0 +1,196 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.protocol.rmi;
+
+
+import static junit.framework.Assert.assertEquals;
+
+import org.junit.Ignore;
+import org.junit.Test;
+
+import com.alibaba.dubbo.common.ExtensionLoader;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.rpc.Exporter;
+import com.alibaba.dubbo.rpc.Protocol;
+import com.alibaba.dubbo.rpc.ProxyFactory;
+import com.alibaba.dubbo.rpc.RpcException;
+import com.alibaba.dubbo.rpc.service.EchoService;
+
+public class RmiProtocolTest
+{
+ private Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
+ private ProxyFactory proxy = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();
+
+
+ public static interface NonStdRmiInterface {
+ void bark();
+ }
+
+ @Test
+ public void test_getRemoteClass() throws Exception {
+ Class<NonStdRmiInterface> clazz = RmiProtocol.getRemoteClass(NonStdRmiInterface.class);
+ assertEquals(clazz, RmiProtocol.getRemoteClass(NonStdRmiInterface.class));
+ }
+
+ @Test
+ public void testRmiProtocolTimeout() throws Exception
+ {
+ System.setProperty("sun.rmi.transport.tcp.responseTimeout", "1000");
+ DemoService service = new DemoServiceImpl();
+ Exporter<?> rpcExporter = protocol.export(proxy.getInvoker(service, DemoService.class, URL.valueOf("rmi://127.0.0.1:9001/TestService")));
+ service = proxy.getProxy(protocol.refer(DemoService.class, URL.valueOf("rmi://127.0.0.1:9001/TestService")));
+ try {
+ try {
+ service.throwTimeout();
+ } catch (RpcException e) {
+ assertEquals(true, e.isTimeout());
+ assertEquals(true, e.getMessage().contains("Read timed out"));
+ }
+ } finally {
+ rpcExporter.unexport();
+ }
+ }
+
+ @Test
+ public void testRmiProtocol() throws Exception
+ {
+ {
+ DemoService service = new DemoServiceImpl();
+ Exporter<?> rpcExporter = protocol.export(proxy.getInvoker(service, DemoService.class, URL.valueOf("rmi://127.0.0.1:9001/TestService")));
+
+ service = proxy.getProxy(protocol.refer(DemoService.class, URL.valueOf("rmi://127.0.0.1:9001/TestService")));
+ assertEquals(service.getSize(null), -1);
+ assertEquals(service.getSize(new String[]{"", "", ""}), 3);
+ Object result = service.invoke("rmi://127.0.0.1:9001/TestService", "invoke");
+ assertEquals("rmi://127.0.0.1:9001/TestService:invoke", result);
+
+ rpcExporter.unexport();
+ }
+
+ {
+ RemoteService remoteService = new RemoteServiceImpl();
+ Exporter<?> rpcExporter = protocol.export(proxy.getInvoker(remoteService, RemoteService.class, URL.valueOf("rmi://127.0.0.1:9001/remoteService")));
+
+ remoteService = proxy.getProxy(protocol.refer(RemoteService.class, URL.valueOf("rmi://127.0.0.1:9001/remoteService")));
+ remoteService.getThreadName();
+ for(int i=0;i<100;i++) {
+ String say = remoteService.sayHello("abcd");
+ assertEquals("hello abcd@" + RemoteServiceImpl.class.getName(), say);
+ }
+ rpcExporter.unexport();
+ }
+ }
+
+ // FIXME RMI协议目前的实现不支持转型成 EchoService
+ @Ignore
+ @Test
+ public void testRmiProtocol_echoService() throws Exception
+ {
+ DemoService service = new DemoServiceImpl();
+ Exporter<?> rpcExporter = protocol.export(proxy.getInvoker(service, DemoService.class, URL.valueOf("rmi://127.0.0.1:9002/TestService")));
+
+ // cast to EchoService
+ EchoService echo = proxy.getProxy(protocol.refer(EchoService.class, URL.valueOf("rmi://127.0.0.1:9002/TestService")));
+ assertEquals(echo.$echo("test"), "test");
+ assertEquals(echo.$echo("abcdefg"), "abcdefg");
+ assertEquals(echo.$echo(1234), 1234);
+
+ rpcExporter.unexport();
+
+ RemoteService remoteService = new RemoteServiceImpl();
+ rpcExporter = protocol.export(proxy.getInvoker(remoteService, RemoteService.class, URL.valueOf("rmi://127.0.0.1:9002/remoteService")));
+
+ // cast to EchoService
+ echo = proxy.getProxy(protocol.refer(EchoService.class, URL.valueOf("rmi://127.0.0.1:9002/remoteService")));
+ assertEquals(echo.$echo("test"), "test");
+ assertEquals(echo.$echo("abcdefg"), "abcdefg");
+ assertEquals(echo.$echo(1234), 1234);
+
+ rpcExporter.unexport();
+ }
+
+ /*@Test
+ public void testRpcInvokerGroup() throws Exception
+ {
+ DemoService service = new DemoServiceImpl();
+ RpcUtils.export("demo://127.0.0.1:9030/com.alibaba.dubbo.rpc.TestService",DemoService.class,service);
+ RpcUtils.export("dubbo://127.0.0.1:9031/TestService",DemoService.class,service);
+ RpcUtils.export("rmi://127.0.0.1:9032/com.alibaba.dubbo.rpc.TestService",DemoService.class,service);
+ RpcUtils.export("rmi://127.0.0.1:9033/com.alibaba.dubbo.rpc.TestService",DemoService.class,service);
+
+ service = RpcUtils.createProxy(DemoService.class,
+ new String[]{
+ "demo://127.0.0.1:9030/com.alibaba.dubbo.rpc.TestService?weight=20",
+ "dubbo://127.0.0.1:9031/TestService?weight=20",
+ "rmi://127.0.0.1:9032/com.alibaba.dubbo.rpc.TestService",
+ });
+ assertEquals(service.getSize(null), -1);
+ assertEquals(service.getSize(new String[]{"","",""}), 3);
+
+ // cast to EchoService
+ EchoService echo = RpcUtils.createProxy(EchoService.class,
+ new String[]{
+ "demo://127.0.0.1:9030/com.alibaba.dubbo.rpc.TestService?weight=20",
+ "dubbo://127.0.0.1:9031/TestService?weight=20",
+ "rmi://127.0.0.1:9032/com.alibaba.dubbo.rpc.TestService",
+ });
+ assertEquals(echo.$echo("test"), "test");
+ assertEquals(echo.$echo("abcdefg"), "abcdefg");
+ assertEquals(echo.$echo(1234), 1234);
+ }*/
+
+ /*public void testForkInvoke() throws Exception
+ {
+ DemoService service = new DemoServiceImpl();
+ protocol.export(proxy.createInvoker("dubbo://127.0.0.1:9040/TestService", DemoService.class, service);
+ protocol.export(proxy.createInvoker("dubbo://127.0.0.1:9041/TestService", DemoService.class, service);
+ protocol.export(proxy.createInvoker("rmi://127.0.0.1:9042/com.alibaba.dubbo.rpc.TestService", DemoService.class, service);
+ protocol.export(proxy.createInvoker("rmi://127.0.0.1:9043/com.alibaba.dubbo.rpc.TestService", DemoService.class, service);
+
+ RpcInvokerGroup group = Proxies.createInvoker(DemoService.class, new String[]{
+ "dubbo://127.0.0.1:9040/TestService",
+ "dubbo://127.0.0.1:9041/TestService",
+ "rmi://127.0.0.1:9042/com.alibaba.dubbo.rpc.TestService",
+ "rmi://127.0.0.1:9043/com.alibaba.dubbo.rpc.TestService",
+ });
+ group.getMethodSettings("echo").setFork(true);
+ group.getMethodSettings("echo").setForkInvokeCallback(new ForkInvokeCallback(){
+ public Object merge(RpcInvocation invocation, RpcResult[] results) throws Throwable
+ {
+ System.out.println("merge result begin:");
+ for( RpcResult result : results )
+ {
+ if( result.hasException() )
+ System.out.println("exception:"+result.getException().getMessage());
+ else
+ System.out.println("result:"+result.getResult());
+ }
+ System.out.println("merge result end:");
+ return "aaaa";
+ }
+ });
+
+ service = proxy.createProxy(protocol.refer(DemoService.class, group);
+ service.echo("test");
+
+ // cast to EchoService
+ EchoService echo = proxy.createProxy(protocol.refer(EchoService.class, group);
+ assertEquals(echo.$echo("test"), "test");
+ assertEquals(echo.$echo("abcdefg"), "abcdefg");
+ assertEquals(echo.$echo(1234), 1234);
+ }*/
+
+}
\ No newline at end of file
diff --git a/dubbo-rpc-rmi/src/test/java/com/alibaba/dubbo/rpc/protocol/rmi/Type.java b/dubbo-rpc-rmi/src/test/java/com/alibaba/dubbo/rpc/protocol/rmi/Type.java
new file mode 100644
index 0000000..eaa4fda
--- /dev/null
+++ b/dubbo-rpc-rmi/src/test/java/com/alibaba/dubbo/rpc/protocol/rmi/Type.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.protocol.rmi;
+
+public enum Type
+{
+ High, Normal, Lower
+}
\ No newline at end of file
diff --git a/dubbo-rpc/pom.xml b/dubbo-rpc/pom.xml
new file mode 100644
index 0000000..ced9d04
--- /dev/null
+++ b/dubbo-rpc/pom.xml
@@ -0,0 +1,38 @@
+<!--
+ - Copyright 1999-2011 Alibaba Group.
+ -
+ - Licensed under the Apache License, Version 2.0 (the "License");
+ - you may not use this file except in compliance with the License.
+ - You may obtain a copy of the License at
+ -
+ - http://www.apache.org/licenses/LICENSE-2.0
+ -
+ - Unless required by applicable law or agreed to in writing, software
+ - distributed under the License is distributed on an "AS IS" BASIS,
+ - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ - See the License for the specific language governing permissions and
+ - limitations under the License.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>com.alibaba</groupId>
+ <artifactId>dubbo-parent</artifactId>
+ <version>2.0.8</version>
+ </parent>
+ <artifactId>dubbo-rpc</artifactId>
+ <packaging>jar</packaging>
+ <name>Dubbo RPC Module</name>
+ <description>The rpc module of dubbo project</description>
+ <properties>
+ <skip_maven_deploy>false</skip_maven_deploy>
+ </properties>
+ <dependencies>
+ <dependency>
+ <groupId>com.alibaba</groupId>
+ <artifactId>dubbo-common</artifactId>
+ <version>${project.parent.version}</version>
+ </dependency>
+ </dependencies>
+</project>
\ No newline at end of file
diff --git a/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/Exporter.java b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/Exporter.java
new file mode 100644
index 0000000..1494390
--- /dev/null
+++ b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/Exporter.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc;
+
+/**
+ * Exporter. (API/SPI, Prototype, ThreadSafe)
+ *
+ * @see com.alibaba.dubbo.rpc.Protocol#export(Invoker)
+ * @see com.alibaba.dubbo.rpc.ExporterListener
+ * @see com.alibaba.dubbo.rpc.protocol.AbstractExporter
+ * @author william.liangf
+ */
+public interface Exporter<T> {
+
+ /**
+ * get invoker.
+ *
+ * @return invoker
+ */
+ Invoker<T> getInvoker();
+
+ /**
+ * unexport.
+ *
+ * <code>
+ * getInvoker().destroy();
+ * </code>
+ */
+ void unexport();
+
+}
\ No newline at end of file
diff --git a/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/ExporterListener.java b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/ExporterListener.java
new file mode 100644
index 0000000..0de6536
--- /dev/null
+++ b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/ExporterListener.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc;
+
+/**
+ * ExporterListener. (SPI, Singleton, ThreadSafe)
+ *
+ * @author william.liangf
+ */
+public interface ExporterListener {
+
+ /**
+ * The exporter exported.
+ *
+ * @see com.alibaba.dubbo.rpc.Protocol#export(Invoker)
+ * @param exporter
+ * @throws RpcException
+ */
+ void exported(Exporter<?> exporter) throws RpcException;
+
+ /**
+ * The exporter unexported.
+ *
+ * @see com.alibaba.dubbo.rpc.Exporter#unexport()
+ * @param exporter
+ * @throws RpcException
+ */
+ void unexported(Exporter<?> exporter);
+
+}
\ No newline at end of file
diff --git a/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/Filter.java b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/Filter.java
new file mode 100644
index 0000000..2a0573f
--- /dev/null
+++ b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/Filter.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc;
+
+import com.alibaba.dubbo.common.Extension;
+
+/**
+ * Filter. (SPI, Singleton, ThreadSafe)
+ *
+ * @author william.liangf
+ */
+@Extension
+public interface Filter {
+
+ /**
+ * do invoke filter.
+ *
+ * <code>
+ * return invoker.invoke(invocation);
+ * </code>
+ *
+ * @see com.alibaba.dubbo.rpc.Invoker#invoke(Invocation)
+ * @param invoker service
+ * @param invocation invocation.
+ * @return invoke result.
+ * @throws RpcException
+ */
+ Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException;
+
+}
\ No newline at end of file
diff --git a/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/Invocation.java b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/Invocation.java
new file mode 100644
index 0000000..9bda780
--- /dev/null
+++ b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/Invocation.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;
+
+import java.util.Map;
+
+/**
+ * Rpc invocation. (API, Prototype, ThreadSafe)
+ *
+ * @serial Don't change the class name.
+ * @see com.alibaba.dubbo.rpc.Invoker#invoke(Invocation)
+ * @see com.alibaba.dubbo.rpc.RpcInvocation
+ * @author qian.lei
+ * @author william.liangf
+ */
+public interface Invocation {
+
+ /**
+ * get method name.
+ *
+ * @return method name.
+ */
+ String getMethodName();
+
+ /**
+ * get parameter types.
+ *
+ * @return parameter types.
+ */
+ Class<?>[] getParameterTypes();
+
+ /**
+ * get arguments.
+ *
+ * @return arguments.
+ */
+ Object[] getArguments();
+
+ /**
+ * get attachments.
+ *
+ * @return attachments.
+ */
+ Map<String, String> getAttachments();
+
+}
\ No newline at end of file
diff --git a/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/Invoker.java b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/Invoker.java
new file mode 100644
index 0000000..f55b223
--- /dev/null
+++ b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/Invoker.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc;
+
+import com.alibaba.dubbo.common.URL;
+
+/**
+ * Invoker. (API/SPI, Prototype, ThreadSafe)
+ *
+ * @see com.alibaba.dubbo.rpc.Protocol#refer(Class, URL)
+ * @see com.alibaba.dubbo.rpc.InvokerListener
+ * @see com.alibaba.dubbo.rpc.protocol.AbstractInvoker
+ * @author william.liangf
+ */
+public interface Invoker<T> {
+
+ /**
+ * get service interface.
+ *
+ * @return service interface.
+ */
+ Class<T> getInterface();
+
+ /**
+ * get service url.
+ *
+ * @return service url.
+ */
+ URL getUrl();
+
+ /**
+ * is available.
+ *
+ * @return available.
+ */
+ boolean isAvailable();
+
+ /**
+ * invoke.
+ *
+ * @param invocation
+ * @return result
+ * @throws RpcException
+ */
+ Result invoke(Invocation invocation) throws RpcException;
+
+ /**
+ * destroy.
+ */
+ void destroy();
+
+}
\ No newline at end of file
diff --git a/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/InvokerListener.java b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/InvokerListener.java
new file mode 100644
index 0000000..2be31ce
--- /dev/null
+++ b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/InvokerListener.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc;
+
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.common.URL;
+
+/**
+ * InvokerListener. (SPI, Singleton, ThreadSafe)
+ *
+ * @author william.liangf
+ */
+@Extension
+public interface InvokerListener {
+
+ /**
+ * The invoker referred
+ *
+ * @see com.alibaba.dubbo.rpc.Protocol#refer(Class, URL)
+ * @param invoker
+ * @throws RpcException
+ */
+ void referred(Invoker<?> invoker) throws RpcException;
+
+ /**
+ * The invoker destroyed.
+ *
+ * @see com.alibaba.dubbo.rpc.Invoker#destroy()
+ * @param invoker
+ */
+ void destroyed(Invoker<?> invoker);
+
+}
\ No newline at end of file
diff --git a/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/Protocol.java b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/Protocol.java
new file mode 100644
index 0000000..2158bd3
--- /dev/null
+++ b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/Protocol.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc;
+
+import com.alibaba.dubbo.common.Adaptive;
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.common.URL;
+
+/**
+ * Protocol. (SPI, Singleton, ThreadSafe)
+ *
+ * @author william.liangf
+ */
+@Extension("dubbo")
+public interface Protocol {
+
+ /**
+ * get default port.
+ *
+ * @return default port.
+ */
+ int getDefaultPort();
+
+ /**
+ * export.
+ *
+ * @param <T>
+ * @param invoker
+ * @return exporter
+ * @throws RpcException
+ */
+ @Adaptive
+ <T> Exporter<T> export(Invoker<T> invoker) throws RpcException;
+
+ /**
+ * refer.
+ *
+ * @param <T>
+ * @param type
+ * @param url
+ * @return invoker
+ * @throws RpcException
+ */
+ @Adaptive
+ <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException;
+
+ /**
+ * destory.
+ */
+ void destroy();
+
+}
\ No newline at end of file
diff --git a/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/ProxyFactory.java b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/ProxyFactory.java
new file mode 100644
index 0000000..edcdc62
--- /dev/null
+++ b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/ProxyFactory.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc;
+
+import com.alibaba.dubbo.common.Adaptive;
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.common.URL;
+
+/**
+ * ProxyFactory. (SPI, Singleton, ThreadSafe)
+ *
+ * @author william.liangf
+ */
+@Extension("javassist")
+public interface ProxyFactory {
+
+ /**
+ * create proxy.
+ *
+ * @param invoker
+ * @return proxy
+ */
+ @Adaptive({Constants.PROXY_KEY})
+ <T> T getProxy(Invoker<T> invoker) throws RpcException;
+
+ /**
+ * create invoker.
+ *
+ * @param <T>
+ * @param proxy
+ * @param type
+ * @param url
+ * @return invoker
+ */
+ @Adaptive({Constants.PROXY_KEY})
+ <T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) throws RpcException;
+
+}
\ No newline at end of file
diff --git a/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/Result.java b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/Result.java
new file mode 100644
index 0000000..57402cd
--- /dev/null
+++ b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/Result.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc;
+
+/**
+ * RPC invoke result. (API, Prototype, ThreadSafe)
+ *
+ * @serial Don't change the class name.
+ * @see com.alibaba.dubbo.rpc.Invoker#invoke(Invocation)
+ * @see com.alibaba.dubbo.rpc.RpcResult
+ * @author qianlei
+ * @author william.liangf
+ */
+public interface Result {
+
+ /**
+ * Has exception.
+ *
+ * @return has exception.
+ */
+ boolean hasException();
+
+ /**
+ * Get invoke result.
+ *
+ * @return result if has exception throw it.
+ * @throws Throwable.
+ */
+ Object getResult();
+
+ /**
+ * Get exception.
+ *
+ * @return exception if no exception return null.
+ */
+ Throwable getException();
+
+ /**
+ * Recreate.
+ *
+ * <code>
+ * if (hasException()) {
+ * throw getException();
+ * } else {
+ * return getResult();
+ * }
+ * </code>
+ */
+ Object recreate() throws Throwable;
+
+}
\ No newline at end of file
diff --git a/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/RpcConstants.java b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/RpcConstants.java
new file mode 100644
index 0000000..4c98c46
--- /dev/null
+++ b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/RpcConstants.java
@@ -0,0 +1,175 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * RpcConstants
+ *
+ * @author william.liangf
+ */
+public final class RpcConstants {
+
+ public static final List<String> DEFAULT_REFERENCE_FILTERS = Collections.unmodifiableList(Arrays.asList(new String[] {
+ "consumercontext", "compatible", "deprecated", "collect", "genericimpl", "activelimit", "monitor", "future" }));
+
+ public static final List<String> DEFAULT_SERVICE_FILTERS = Collections.unmodifiableList(Arrays.asList(new String[] {
+ "context", "token", "exception", "echo", "generic", "accesslog", "trace", "classloader", "executelimit", "monitor" ,"timeout"}));
+
+ public static final List<String> DEFAULT_INVOKER_LISTENERS = Collections.unmodifiableList(Arrays.asList(new String[] {
+ "deprecated" }));
+
+ public static final List<String> DEFAULT_EXPORTER_LISTENERS = Collections.unmodifiableList(Arrays.asList(new String[] { }));
+
+ /**
+ * 集群时是否排除非available的invoker
+ */
+ public static final String CLUSTER_AVAILABLE_CHECK_KEY = "cluster.availablecheck";
+
+ /**
+ */
+ public static final boolean DEFAULT_CLUSTER_AVAILABLE_CHECK = true;
+
+ /**
+ * 集群时是否启用sticky策略
+ */
+ public static final String CLUSTER_STICKY_KEY = "sticky";
+
+ /**
+ * sticky默认值.
+ */
+ public static final boolean DEFAULT_CLUSTER_STICKY = false;
+
+ /**
+ * 创建client时,是否先要建立连接。
+ */
+ public static final String LAZY_CONNECT_KEY = "lazy";
+
+ /**
+ * lazy连接的初始状态是连接状态还是非连接状态?
+ */
+ public static final String LAZY_CONNECT_INITIAL_STATE_KEY = "connect.lazy.initial.state";
+
+ /**
+ * lazy连接的初始状态默认是连接状态.
+ */
+ public static final boolean DEFAULT_LAZY_CONNECT_INITIAL_STATE = true;
+
+ /**
+ * 注册中心是否同步存储文件,默认异步
+ */
+ public static final String REGISTRY_FILESAVE_SYNC_KEY = "save.file";
+
+ /**
+ *注册中心失败事件重试事件
+ */
+ public static final String REGISTRY_RETRY_PERIOD_KEY = "retry.period";
+
+ /**
+ *注册中心自动重连时间
+ */
+ public static final String REGISTRY_RECONNECT_PERIOD_KEY = "reconnect.period";
+
+ /**
+ * 注册中心导出URL参数的KEY
+ */
+ public static final String EXPORT_KEY = "export";
+
+ /**
+ * 注册中心引用URL参数的KEY
+ */
+ public static final String REFER_KEY = "refer";
+
+ /**
+ * callback inst id
+ */
+ public static final String CALLBACK_SERVICE_KEY = "callback.service.instid";
+
+ /**
+ * 每个客户端同一个接口 callback服务实例的限制
+ */
+ public static final String CALLBACK_INSTANCES_LIMIT_KEY = "callbacks";
+
+ /**
+ * 每个客户端同一个接口 callback服务实例的限制
+ */
+ public static final int DEFAULT_CALLBACK_INSTANCES = 1;
+
+ public static final String CALLBACK_SERVICE_PROXY_KEY = "callback.service.proxy";
+
+ public static final String IS_CALLBACK_SERVICE = "is_callback_service";
+
+ /**
+ * channel中callback的invokers
+ */
+ public static final String CHANNEL_CALLBACK_KEY = "channel.callback.invokers.key";
+
+ @Deprecated
+ public static final String SHUTDOWN_TIMEOUT_SECONDS_KEY = "dubbo.service.shutdown.wait.seconds";
+
+ public static final String SHUTDOWN_TIMEOUT_KEY = "dubbo.service.shutdown.wait";
+
+ public static final String IS_SERVER_KEY = "isserver";
+
+ /**
+ * 默认值毫秒,避免重新计算.
+ */
+ public static final int DEFAULT_SERVER_SHUTDOWN_TIMEOUT = 10000;
+
+ public static final String ON_CONNECT_KEY = "onconnect";
+
+ public static final String ON_DISCONNECT_KEY = "ondisconnect";
+
+ public static final String RETURN_KEY = "return";
+
+ public static final String ON_INVOKE_METHOD_KEY = "oninvoke.method";
+
+ public static final String ON_RETURN_METHOD_KEY = "onreturn.method";
+
+ public static final String ON_THROW_METHOD_KEY = "onthrow.method";
+
+ public static final String ON_INVOKE_INSTANCE_KEY = "oninvoke.instance";
+
+ public static final String ON_RETURN_INSTANCE_KEY = "onreturn.instance";
+
+ public static final String ON_THROW_INSTANCE_KEY = "onthrow.instance";
+
+ public static final String ROUTE_PROTOCOL = "route";
+
+ public static final String RULE_KEY = "rule";
+
+ public static final String TYPE_KEY = "type";
+
+ // key for router type, for e.g., "script"/"file", corresponding to ScriptRouterFactory.NAME, FileRouterFactory.NAME
+ public static final String ROUTER_KEY = "router";
+
+ // when ROUTER_KEY's value is set to ROUTER_TYPE_CLEAR, RegistryDirectory will clean all current routers
+ public static final String ROUTER_TYPE_CLEAR = "clean";
+
+ public static final String DEFAULT_SCRIPT_TYPE_KEY = "javascript";
+
+ public static final String STUB_EVENT_KEY = "dubbo.stub.event";
+
+ public static final boolean DEFAULT_STUB_EVENT = false;
+
+ public static final String STUB_EVENT_METHODS_KEY = "dubbo.stub.event.methods";
+
+ private RpcConstants() {}
+
+}
\ No newline at end of file
diff --git a/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/RpcContext.java b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/RpcContext.java
new file mode 100644
index 0000000..1b73218
--- /dev/null
+++ b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/RpcContext.java
@@ -0,0 +1,444 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.HashMap;
+import java.util.Map;
+import java.util.concurrent.Future;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.utils.NetUtils;
+
+/**
+ * Thread local context. (API, ThreadLocal, ThreadSafe)
+ *
+ * @see com.alibaba.dubbo.rpc.filter.ContextFilter
+ * @see com.alibaba.dubbo.rpc.protocol.AbstractExporter#invoke(Invocation, InetSocketAddress)
+ * @author qian.lei
+ * @author william.liangf
+ */
+public class RpcContext {
+
+ private static final ThreadLocal<RpcContext> LOCAL = new ThreadLocal<RpcContext>() {
+ @Override
+ protected RpcContext initialValue() {
+ return new RpcContext();
+ }
+ };
+
+ /**
+ * get context.
+ *
+ * @return context
+ */
+ public static RpcContext getContext() {
+ return LOCAL.get();
+ }
+
+ /**
+ * remove context.
+ *
+ * @see com.alibaba.dubbo.rpc.filter.ContextFilter
+ */
+ public static void removeContext() {
+ LOCAL.remove();
+ }
+
+ private final Map<String, Object> values = new HashMap<String, Object>();
+
+ private final Map<String, String> attachments = new HashMap<String, String>();
+
+ private Invoker<?> invoker;
+
+ private Invocation invocation;
+
+ private InetSocketAddress localAddress;
+
+ private InetSocketAddress remoteAddress;
+
+ private Future<?> future;
+
+ protected RpcContext() {
+ }
+
+ /**
+ * is server side.
+ *
+ * @return server side.
+ */
+ public boolean isServerSide() {
+ return ! isClientSide();
+ }
+
+ /**
+ * is client side.
+ *
+ * @return client side.
+ */
+ public boolean isClientSide() {
+ Invoker<?> invoker = getInvoker();
+ if (invoker == null) {
+ return false;
+ }
+ URL url = invoker.getUrl();
+ if (url == null) {
+ return false;
+ }
+ InetSocketAddress address = getRemoteAddress();
+ if (address == null) {
+ return false;
+ }
+ String host;
+ if (address.getAddress() == null) {
+ host = address.getHostName();
+ } else {
+ host = address.getAddress().getHostAddress();
+ }
+ return url.getPort() == address.getPort() &&
+ NetUtils.filterLocalHost(url.getHost()).equals(NetUtils.filterLocalHost(host));
+ }
+
+ /**
+ * set current invoker.
+ *
+ * @param invoker
+ * @return context
+ */
+ public RpcContext setInvoker(Invoker<?> invoker) {
+ this.invoker = invoker;
+ return this;
+ }
+
+ /**
+ * get current invoker.
+ *
+ * @return invoker
+ */
+ public Invoker<?> getInvoker() {
+ return invoker;
+ }
+
+ /**
+ * set invocation.
+ *
+ * @param invocation
+ * @return context
+ */
+ public RpcContext setInvocation(Invocation invocation) {
+ this.invocation = invocation;
+ return this;
+ }
+
+ /**
+ * get invocation.
+ *
+ * @return invocation
+ */
+ public Invocation getInvocation() {
+ return invocation;
+ }
+
+ /**
+ * set local address.
+ *
+ * @param address
+ * @return context
+ */
+ public RpcContext setLocalAddress(InetSocketAddress address) {
+ this.localAddress = address;
+ return this;
+ }
+
+ /**
+ * set local address.
+ *
+ * @param host
+ * @param port
+ * @return context
+ */
+ public RpcContext setLocalAddress(String host, int port) {
+ if (port < 0) {
+ port = 0;
+ }
+ this.localAddress = InetSocketAddress.createUnresolved(host, port);
+ return this;
+ }
+
+ /**
+ * get local address.
+ *
+ * @return local address
+ */
+ public InetSocketAddress getLocalAddress() {
+ return localAddress;
+ }
+
+ public String getLocalAddressString() {
+ return getLocalHost() + ":" + getLocalPort();
+ }
+
+ /**
+ * get local host name.
+ *
+ * @return local host name
+ */
+ public String getLocalHostName() {
+ String host = localAddress == null ? null : localAddress.getHostName();
+ if (host == null || host.length() == 0) {
+ return getLocalHost();
+ }
+ return host;
+ }
+
+ /**
+ * set remote address.
+ *
+ * @param address
+ * @return context
+ */
+ public RpcContext setRemoteAddress(InetSocketAddress address) {
+ this.remoteAddress = address;
+ return this;
+ }
+
+ /**
+ * set remote address.
+ *
+ * @param host
+ * @param port
+ * @return context
+ */
+ public RpcContext setRemoteAddress(String host, int port) {
+ if (port < 0) {
+ port = 0;
+ }
+ this.remoteAddress = InetSocketAddress.createUnresolved(host, port);
+ return this;
+ }
+
+ /**
+ * get remote address.
+ *
+ * @return remote address
+ */
+ public InetSocketAddress getRemoteAddress() {
+ return remoteAddress;
+ }
+
+ /**
+ * get remote address string.
+ *
+ * @return remote address string.
+ */
+ public String getRemoteAddressString() {
+ return getRemoteHost() + ":" + getRemotePort();
+ }
+
+ /**
+ * get remote host name.
+ *
+ * @return remote host name
+ */
+ public String getRemoteHostName() {
+ return remoteAddress == null ? null : remoteAddress.getHostName();
+ }
+
+ /**
+ * get local host.
+ *
+ * @return local host
+ */
+ public String getLocalHost() {
+ String host = localAddress == null ? null :
+ localAddress.getAddress() == null ? localAddress.getHostName()
+ : NetUtils.filterLocalHost(localAddress.getAddress().getHostAddress());
+ if (host == null || host.length() == 0) {
+ return NetUtils.getLocalHost();
+ }
+ return host;
+ }
+
+ /**
+ * get local port.
+ *
+ * @return port
+ */
+ public int getLocalPort() {
+ return localAddress == null ? 0 : localAddress.getPort();
+ }
+
+ /**
+ * get remote host.
+ *
+ * @return remote host
+ */
+ public String getRemoteHost() {
+ return remoteAddress == null ? null :
+ remoteAddress.getAddress() == null ? remoteAddress.getHostName()
+ : NetUtils.filterLocalHost(remoteAddress.getAddress().getHostAddress());
+ }
+
+ /**
+ * get remote port.
+ *
+ * @return remote port
+ */
+ public int getRemotePort() {
+ return remoteAddress == null ? 0 : remoteAddress.getPort();
+ }
+
+ /**
+ * get values.
+ *
+ * @return
+ */
+ public Map<String, Object> get() {
+ return values;
+ }
+
+ /**
+ * set values
+ *
+ * @param values
+ * @return
+ */
+ public RpcContext set(Map<String, Object> value) {
+ this.values.clear();
+ if (value != null && value.size() > 0) {
+ this.values.putAll(value);
+ }
+ return this;
+ }
+
+ /**
+ * set value.
+ *
+ * @param key
+ * @param value
+ * @return context
+ */
+ public RpcContext set(String key, Object value) {
+ if (value == null) {
+ values.remove(key);
+ } else {
+ values.put(key, value);
+ }
+ return this;
+ }
+
+ /**
+ * remove value.
+ *
+ * @param key
+ * @return value
+ */
+ public RpcContext remove(String key) {
+ values.remove(key);
+ return this;
+ }
+
+ /**
+ * get value.
+ *
+ * @param key
+ * @return value
+ */
+ public Object get(String key) {
+ return values.get(key);
+ }
+
+ /**
+ * get attachments.
+ *
+ * @return
+ */
+ public Map<String, String> getAttachments() {
+ return attachments;
+ }
+
+ /**
+ * set attachments
+ *
+ * @param attachment
+ * @return
+ */
+ public RpcContext setAttachments(Map<String, String> attachment) {
+ this.attachments.clear();
+ if (attachment != null && attachment.size() > 0) {
+ this.attachments.putAll(attachment);
+ }
+ return this;
+ }
+
+ /**
+ * set attachment.
+ *
+ * @param key
+ * @param value
+ * @return
+ */
+ 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
+ */
+ public RpcContext removeAttachment(String key) {
+ attachments.remove(key);
+ return this;
+ }
+
+ /**
+ * get attachment.
+ *
+ * @param key
+ * @return
+ */
+ public Object getAttachment(String key) {
+ return attachments.get(key);
+ }
+
+ /**
+ * get future.
+ *
+ * @param <T>
+ * @return
+ */
+ @SuppressWarnings("unchecked")
+ public <T> Future<T> getFuture() {
+ return (Future<T>) future;
+ }
+
+ /**
+ * set future.
+ *
+ * @param future
+ */
+ public void setFuture(Future<?> future) {
+ this.future = future;
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/RpcException.java b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/RpcException.java
new file mode 100644
index 0000000..803e32e
--- /dev/null
+++ b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/RpcException.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc;
+
+/**
+ * RPC Exception. (API, Prototype, ThreadSafe)
+ *
+ * @serial Don't change the class name and properties.
+ * @since 1.0
+ * @see com.alibaba.dubbo.rpc.Invoker#invoke(RpcInvocation)
+ * @author shawn.qianx
+ * @author william.liangf
+ */
+public class RpcException extends RuntimeException {
+
+ private static final long serialVersionUID = 7815426752583648734L;
+
+ public static final int UNKNOWN_EXCEPTION = 0;
+
+ public static final int NETWORK_EXCEPTION = 1;
+
+ public static final int TIMEOUT_EXCEPTION = 2;
+
+ public static final int BIZ_EXCEPTION = 3;
+
+ public static final int FORBIDDEN_EXCEPTION = 4;
+
+ public static final int SERIALIZATION_EXCEPTION = 5;
+
+ private int code;
+
+ public RpcException() {
+ super();
+ }
+
+ public RpcException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public RpcException(String message) {
+ super(message);
+ }
+
+ public RpcException(Throwable cause) {
+ super(cause);
+ }
+
+ public RpcException(int code) {
+ super();
+ this.code = code;
+ }
+
+ public RpcException(int code, String message, Throwable cause) {
+ super(message, cause);
+ this.code = code;
+ }
+
+ public RpcException(int code, String message) {
+ super(message);
+ this.code = code;
+ }
+
+ public RpcException(int code, Throwable cause) {
+ super(cause);
+ this.code = code;
+ }
+
+ public void setCode(int code) {
+ this.code = code;
+ }
+
+ public int getCode() {
+ return code;
+ }
+
+ public boolean isBiz() {
+ return code == BIZ_EXCEPTION;
+ }
+
+ public boolean isForbidded() {
+ return code == FORBIDDEN_EXCEPTION;
+ }
+
+ public boolean isTimeout() {
+ return code == TIMEOUT_EXCEPTION;
+ }
+
+ public boolean isNetwork() {
+ return code == NETWORK_EXCEPTION;
+ }
+
+ public boolean isSerialization() {
+ return code == SERIALIZATION_EXCEPTION;
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/RpcInvocation.java b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/RpcInvocation.java
new file mode 100644
index 0000000..c68dc2f
--- /dev/null
+++ b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/RpcInvocation.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;
+
+import java.io.Serializable;
+import java.lang.reflect.Method;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 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;
+
+ public RpcInvocation() {
+ }
+
+ public RpcInvocation(Method method, Object[] arguments) {
+ this(method.getName(), method.getParameterTypes(), arguments, null);
+ }
+
+ public RpcInvocation(Method method, Object[] arguments, Map<String, String> attachment) {
+ this(method.getName(), method.getParameterTypes(), arguments, attachment);
+ }
+
+ public RpcInvocation(String methodName, Class<?>[] parameterTypes, Object[] arguments) {
+ this(methodName, parameterTypes, arguments, null);
+ }
+
+ public RpcInvocation(String methodName, Class<?>[] parameterTypes, Object[] arguments, Map<String, String> attachments) {
+ 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;
+ }
+
+ public String getMethodName() {
+ return methodName;
+ }
+
+ public Class<?>[] getParameterTypes() {
+ return parameterTypes;
+ }
+
+ public Object[] getArguments() {
+ return arguments;
+ }
+
+ public Map<String, String> getAttachments() {
+ return attachments;
+ }
+
+ public void setMethodName(String methodName) {
+ this.methodName = methodName;
+ }
+
+ public void setParameterTypes(Class<?>[] parameterTypes) {
+ this.parameterTypes = parameterTypes == null ? new Class<?>[0] : parameterTypes;
+ }
+
+ public void setArguments(Object[] arguments) {
+ this.arguments = arguments == null ? new Object[0] : arguments;
+ }
+
+ public void setAttachments(Map<String, String> attachments) {
+ this.attachments = attachments == null ? new HashMap<String, String>() : attachments;
+ }
+
+ public void setAttachment(String key, String value) {
+ if (attachments == null) {
+ attachments = new HashMap<String, String>();
+ }
+ attachments.put(key, value);
+ }
+
+ public String getAttachment(String key) {
+ if (attachments == null) {
+ return null;
+ }
+ return attachments.get(key);
+ }
+
+ public String getAttachment(String key, String defaultValue) {
+ if (attachments == null) {
+ return defaultValue;
+ }
+ String value = attachments.get(key);
+ if (value == null || value.length() == 0) {
+ return defaultValue;
+ }
+ return value;
+ }
+
+ @Override
+ public String toString() {
+ return "RpcInvocation [methodName=" + methodName + ", parameterTypes="
+ + Arrays.toString(parameterTypes) + ", arguments=" + Arrays.toString(arguments)
+ + ", attachments=" + attachments + "]";
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/RpcResult.java b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/RpcResult.java
new file mode 100644
index 0000000..b4c6846
--- /dev/null
+++ b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/RpcResult.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.io.Serializable;
+
+/**
+ * Generic rpc invoke result.
+ *
+ * @serial Don't change the class name and properties.
+ * @author qianlei
+ */
+public class RpcResult implements Result, Serializable {
+
+ private static final long serialVersionUID = -6925924956850004727L;
+
+ private Object result;
+
+ private Throwable exception;
+
+ public RpcResult(){
+ }
+
+ public RpcResult(Object result){
+ this.result = result;
+ }
+
+ public RpcResult(Throwable exception){
+ this.exception = exception;
+ }
+
+ public Object recreate() throws Throwable {
+ if (exception != null) {
+ throw exception;
+ }
+ return result;
+ }
+
+ public Object getResult() {
+ return result;
+ }
+
+ public void setResult(Object result) {
+ this.result = result;
+ }
+
+ public Throwable getException() {
+ return exception;
+ }
+
+ public void setException(Throwable e) {
+ this.exception = e;
+ }
+
+ public boolean hasException() {
+ return exception != null;
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/RpcStatus.java b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/RpcStatus.java
new file mode 100644
index 0000000..1a0079b
--- /dev/null
+++ b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/RpcStatus.java
@@ -0,0 +1,309 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc;
+
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicLong;
+
+import com.alibaba.dubbo.common.URL;
+
+/**
+ * URL statistics. (API, Cached, ThreadSafe)
+ *
+ * @see com.alibaba.dubbo.rpc.filter.CountFilter
+ * @see com.alibaba.dubbo.rpc.filter.ActiveLimitFilter
+ * @see com.alibaba.dubbo.routing.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
+ */
+ 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
+ */
+ public static RpcStatus getStatus(URL url, String methodName) {
+ String uri = url.toIdentityString();
+ ConcurrentMap<String, RpcStatus> map = METHOD_STATISTICS.get(uri);
+ if (map == null) {
+ METHOD_STATISTICS.putIfAbsent(uri, new ConcurrentHashMap<String, RpcStatus>());
+ map = METHOD_STATISTICS.get(uri);
+ }
+ RpcStatus status = map.get(methodName);
+ if (status == null) {
+ map.putIfAbsent(methodName, new RpcStatus());
+ status = map.get(methodName);
+ }
+ return status;
+ }
+
+ /**
+ *
+ * @param url
+ */
+ public static void removeStatus(URL url, String methodName) {
+ String uri = url.toIdentityString();
+ ConcurrentMap<String, RpcStatus> map = METHOD_STATISTICS.get(uri);
+ if (map != null) {
+ map.remove(methodName);
+ }
+ }
+
+ /**
+ *
+ * @param url
+ */
+ public static void beginCount(URL url, String methodName) {
+ beginCount(getStatus(url));
+ beginCount(getStatus(url, methodName));
+ }
+
+ private static void beginCount(RpcStatus status) {
+ status.active.incrementAndGet();
+ }
+
+ /**
+ *
+ * @param url
+ * @param elapsed
+ * @param succeeded
+ */
+ public static void endCount(URL url, String methodName, long elapsed, boolean succeeded) {
+ endCount(getStatus(url), elapsed, succeeded);
+ endCount(getStatus(url, methodName), elapsed, succeeded);
+ }
+
+ private static void endCount(RpcStatus status, long elapsed, boolean succeeded) {
+ status.active.decrementAndGet();
+ status.total.incrementAndGet();
+ status.totalElapsed.addAndGet(elapsed);
+ if (status.maxElapsed.get() < elapsed) {
+ status.maxElapsed.set(elapsed);
+ }
+ if (succeeded) {
+ if (status.succeededMaxElapsed.get() < elapsed) {
+ status.succeededMaxElapsed.set(elapsed);
+ }
+ } else {
+ status.failed.incrementAndGet();
+ status.failedElapsed.addAndGet(elapsed);
+ if (status.failedMaxElapsed.get() < elapsed) {
+ status.failedMaxElapsed.set(elapsed);
+ }
+ }
+ }
+
+ private final ConcurrentMap<String, Object> values = new ConcurrentHashMap<String, Object>();
+
+ private final AtomicInteger active = new AtomicInteger();
+
+ private final AtomicLong total = new AtomicLong();
+
+ private final AtomicInteger failed = new AtomicInteger();
+
+ private final AtomicLong totalElapsed = new AtomicLong();
+
+ private final AtomicLong failedElapsed = new AtomicLong();
+
+ private final AtomicLong maxElapsed = new AtomicLong();
+
+ private final AtomicLong failedMaxElapsed = new AtomicLong();
+
+ private final AtomicLong succeededMaxElapsed = new AtomicLong();
+
+ private RpcStatus() {}
+
+ /**
+ * set value.
+ *
+ * @param key
+ * @param value
+ */
+ public void set(String key, Object value) {
+ values.put(key, value);
+ }
+
+ /**
+ * get value.
+ *
+ * @param key
+ * @return value
+ */
+ public Object get(String key) {
+ return values.get(key);
+ }
+
+ /**
+ * get active.
+ *
+ * @return active
+ */
+ public int getActive() {
+ return active.get();
+ }
+
+ /**
+ * get total.
+ *
+ * @return total
+ */
+ public long getTotal() {
+ return total.longValue();
+ }
+
+ /**
+ * get total elapsed.
+ *
+ * @return total elapsed
+ */
+ public long getTotalElapsed() {
+ return totalElapsed.get();
+ }
+
+ /**
+ * get average elapsed.
+ *
+ * @return average elapsed
+ */
+ public long getAverageElapsed() {
+ long total = getTotal();
+ if (total == 0) {
+ return 0;
+ }
+ return getTotalElapsed() / total;
+ }
+
+ /**
+ * get max elapsed.
+ *
+ * @return max elapsed
+ */
+ public long getMaxElapsed() {
+ return maxElapsed.get();
+ }
+
+ /**
+ * get failed.
+ *
+ * @return failed
+ */
+ public int getFailed() {
+ return failed.get();
+ }
+
+ /**
+ * get failed elapsed.
+ *
+ * @return failed elapsed
+ */
+ public long getFailedElapsed() {
+ return failedElapsed.get();
+ }
+
+ /**
+ * get failed average elapsed.
+ *
+ * @return failed average elapsed
+ */
+ public long getFailedAverageElapsed() {
+ long failed = getFailed();
+ if (failed == 0) {
+ return 0;
+ }
+ return getFailedElapsed() / failed;
+ }
+
+ /**
+ * get failed max elapsed.
+ *
+ * @return failed max elapsed
+ */
+ public long getFailedMaxElapsed() {
+ return failedMaxElapsed.get();
+ }
+
+ /**
+ * get succeeded.
+ *
+ * @return succeeded
+ */
+ public long getSucceeded() {
+ return getTotal() - getFailed();
+ }
+
+ /**
+ * get succeeded elapsed.
+ *
+ * @return succeeded elapsed
+ */
+ public long getSucceededElapsed() {
+ return getTotalElapsed() - getFailedElapsed();
+ }
+
+ /**
+ * get succeeded average elapsed.
+ *
+ * @return succeeded average elapsed
+ */
+ public long getSucceededAverageElapsed() {
+ long succeeded = getSucceeded();
+ if (succeeded == 0) {
+ return 0;
+ }
+ return getSucceededElapsed() / succeeded;
+ }
+
+ /**
+ * get succeeded max elapsed.
+ *
+ * @return succeeded max elapsed.
+ */
+ public long getSucceededMaxElapsed() {
+ return succeededMaxElapsed.get();
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/StaticContext.java b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/StaticContext.java
new file mode 100644
index 0000000..14825b7
--- /dev/null
+++ b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/StaticContext.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.utils.StringUtils;
+
+/**
+ * 系统存储,内部类.
+ */
+public class StaticContext extends ConcurrentHashMap<Object, Object>{
+ private static final long serialVersionUID = 1L;
+ private static final String SYSTEMNAME = "system";
+ private String name ;
+
+ private static final ConcurrentMap<String, StaticContext> context_map = new ConcurrentHashMap<String, StaticContext>() ;
+
+ private StaticContext(String name) {
+ super();
+ this.name = name;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public static StaticContext getSystemContext() {
+ return getContext(SYSTEMNAME);
+ }
+
+ public static StaticContext getContext(String name) {
+ StaticContext appContext = context_map.get(name);
+ if (appContext == null){
+ appContext = context_map.putIfAbsent(name, new StaticContext(name));
+ if (appContext == null){
+ appContext = context_map.get(name);
+ }
+ }
+ return appContext;
+ }
+ public static StaticContext remove(String name){
+ return context_map.remove(name);
+ }
+
+ public static String getKey(URL url, String methodName, String suffix) {
+ return getKey(url.getServiceKey(), methodName, suffix);
+ }
+ public static String getKey(Map<String, String> paras, String methodName, String suffix) {
+ return getKey(StringUtils.getServiceKey(paras), methodName, suffix);
+ }
+ private static String getKey(String servicekey, String methodName, String suffix) {
+ StringBuffer sb = new StringBuffer().append(servicekey).append(".").append(methodName).append(".").append(suffix);
+ return sb.toString();
+ }
+}
\ No newline at end of file
diff --git a/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/filter/AccessLogFilter.java b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/filter/AccessLogFilter.java
new file mode 100644
index 0000000..a6ea174
--- /dev/null
+++ b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/filter/AccessLogFilter.java
@@ -0,0 +1,202 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.filter;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.common.json.JSON;
+import com.alibaba.dubbo.common.logger.Logger;
+import com.alibaba.dubbo.common.logger.LoggerFactory;
+import com.alibaba.dubbo.common.utils.ConcurrentHashSet;
+import com.alibaba.dubbo.common.utils.ConfigUtils;
+import com.alibaba.dubbo.common.utils.NamedThreadFactory;
+import com.alibaba.dubbo.rpc.Filter;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.RpcContext;
+import com.alibaba.dubbo.rpc.RpcException;
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Result;
+
+/**
+ * 记录Service的Access Log。
+ * <p>
+ * 使用的Logger key是<code><b>dubbo.accesslog</b></code>。
+ * 如果想要配置Access Log只出现在指定的Appender中,可以在Log4j中注意配置上additivity。配置示例:
+ * <code>
+ * <pre>
+ * <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
+ */
+@Extension("accesslog")
+public class AccessLogFilter implements Filter {
+
+ private static final Logger logger = LoggerFactory.getLogger(AccessLogFilter.class);
+
+ private static final String ACCESS_LOG_KEY = "dubbo.accesslog";
+
+ private static final String FILE_DATE_FORMAT = "yyyyMMdd";
+
+ private static final String MESSAGE_DATE_FORMAT = "yyyy-MM-dd HH:mm:ss";
+
+ private static final int LOG_MAX_BUFFER = 5000;
+
+ private static final long LOG_OUTPUT_INTERVAL = 5000;
+
+ private final ConcurrentMap<String, Set<String>> logQueue = new ConcurrentHashMap<String, Set<String>>();
+
+ private final ScheduledExecutorService logScheduled = Executors.newScheduledThreadPool(2, new NamedThreadFactory("Dubbo-Access-Log", true));
+
+ private volatile ScheduledFuture<?> logFuture = null;
+
+ private class LogTask implements Runnable {
+ public void run() {
+ try {
+ if (logQueue != null && logQueue.size() > 0) {
+ for (Map.Entry<String, Set<String>> entry : logQueue.entrySet()) {
+ try {
+ String accesslog = entry.getKey();
+ Set<String> logSet = entry.getValue();
+ File file = new File(accesslog);
+ File dir = file.getParentFile();
+ if (null!=dir&&! dir.exists()) {
+ dir.mkdirs();
+ }
+ if (logger.isDebugEnabled()) {
+ logger.debug("Append log to " + accesslog);
+ }
+ if (file.exists()) {
+ String now = new SimpleDateFormat(FILE_DATE_FORMAT).format(new Date());
+ String last = new SimpleDateFormat(FILE_DATE_FORMAT).format(new Date(file.lastModified()));
+ if (! now.equals(last)) {
+ File archive = new File(file.getAbsolutePath() + "." + last);
+ file.renameTo(archive);
+ }
+ }
+ FileWriter writer = new FileWriter(file, true);
+ try {
+ for (String msg : logSet) {
+ writer.write(msg);
+ writer.write("\r\n");
+ }
+ writer.flush();
+ } finally {
+ writer.close();
+ }
+ logSet.clear();
+ } catch (Exception e) {
+ logger.error(e.getMessage(), e);
+ }
+ }
+ }
+ } catch (Exception e) {
+ logger.error(e.getMessage(), e);
+ }
+ }
+ }
+
+ private void init() {
+ if (logFuture == null) {
+ synchronized (logScheduled) {
+ if (logFuture == null) {
+ logFuture = logScheduled.scheduleWithFixedDelay(new LogTask(), LOG_OUTPUT_INTERVAL, LOG_OUTPUT_INTERVAL, TimeUnit.MILLISECONDS);
+ }
+ }
+ }
+ }
+
+ private void log(String accesslog, String logmessage) {
+ init();
+ Set<String> logSet = logQueue.get(accesslog);
+ if (logSet == null) {
+ logQueue.putIfAbsent(accesslog, new ConcurrentHashSet<String>());
+ logSet = logQueue.get(accesslog);
+ }
+ if (logSet.size() < LOG_MAX_BUFFER) {
+ logSet.add(logmessage);
+ }
+ }
+
+ public Result invoke(Invoker<?> invoker, Invocation inv) throws RpcException {
+ try {
+ String accesslog = invoker.getUrl().getParameter(Constants.ACCESS_LOG_KEY);
+ if (ConfigUtils.isNotEmpty(accesslog)) {
+ RpcContext context = RpcContext.getContext();
+ String serviceName = invoker.getInterface().getName();
+ String version = invoker.getUrl().getParameter(Constants.VERSION_KEY);
+ String group = invoker.getUrl().getParameter(Constants.GROUP_KEY);
+ StringBuilder sn = new StringBuilder();
+ sn.append("[").append(new SimpleDateFormat(MESSAGE_DATE_FORMAT).format(new Date())).append("] ").append(context.getRemoteHost()).append(":").append(context.getRemotePort())
+ .append(" -> ").append(context.getLocalHost()).append(":").append(context.getLocalPort())
+ .append(" - ");
+ if (null != group && group.length() > 0) {
+ sn.append(group).append("/");
+ }
+ sn.append(serviceName);
+ if (null != version && version.length() > 0) {
+ sn.append(":").append(version);
+ }
+ sn.append(" ");
+ sn.append(inv.getMethodName());
+ sn.append("(");
+ Class<?>[] types = inv.getParameterTypes();
+ if (types != null && types.length > 0) {
+ boolean first = true;
+ for (Class<?> type : types) {
+ if (first) {
+ first = false;
+ } else {
+ sn.append(",");
+ }
+ sn.append(type.getName());
+ }
+ }
+ sn.append(") ");
+ Object[] args = inv.getArguments();
+ if (args != null && args.length > 0) {
+ sn.append(JSON.json(args));
+ }
+ String msg = sn.toString();
+ if (ConfigUtils.isDefault(accesslog)) {
+ LoggerFactory.getLogger(ACCESS_LOG_KEY + "." + invoker.getInterface().getName()).info(msg);
+ } else {
+ log(accesslog, msg);
+ }
+ }
+ } catch (Throwable t) {
+ logger.warn("Exception in AcessLogFilter of service(" + invoker + " -> " + inv + ")", t);
+ }
+ return invoker.invoke(inv);
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/filter/ActiveLimitFilter.java b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/filter/ActiveLimitFilter.java
new file mode 100644
index 0000000..f3c86bd
--- /dev/null
+++ b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/filter/ActiveLimitFilter.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.filter;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.rpc.Filter;
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.Result;
+import com.alibaba.dubbo.rpc.RpcException;
+import com.alibaba.dubbo.rpc.RpcStatus;
+
+/**
+ * LimitInvokerFilter
+ *
+ * @author william.liangf
+ */
+@Extension("activelimit")
+public class ActiveLimitFilter implements Filter {
+
+ public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
+ URL url = invoker.getUrl();
+ String methodName = invocation.getMethodName();
+ int max = invoker.getUrl().getMethodParameter(methodName, Constants.ACTIVES_KEY, 0);
+ RpcStatus count = RpcStatus.getStatus(invoker.getUrl(), invocation.getMethodName());
+ if (max > 0) {
+ long timeout = invoker.getUrl().getMethodParameter(invocation.getMethodName(), Constants.TIMEOUT_KEY, 0);
+ long start = System.currentTimeMillis();
+ long remain = timeout;
+ int active = count.getActive();
+ if (active >= max) {
+ synchronized (count) {
+ active = count.getActive();
+ while (active >= max) {
+ try {
+ count.wait(remain);
+ } catch (InterruptedException e) {
+ }
+ long elapsed = System.currentTimeMillis() - start;
+ remain = timeout - elapsed;
+ if (remain <= 0) {
+ throw new RpcException("Waiting concurrent invoke timeout in client-side for service: "
+ + invoker.getInterface().getName() + ", method: "
+ + invocation.getMethodName() + ", elapsed: " + elapsed
+ + ", timeout: " + timeout + ". concurrent invokes: " + active
+ + ". max concurrent invoke limit: " + max);
+ }
+ }
+ }
+ }
+ }
+ try {
+ long begin = System.currentTimeMillis();
+ RpcStatus.beginCount(url, methodName);
+ try {
+ Result result = invoker.invoke(invocation);
+ RpcStatus.endCount(url, methodName, System.currentTimeMillis() - begin, true);
+ return result;
+ } catch (RuntimeException t) {
+ RpcStatus.endCount(url, methodName, System.currentTimeMillis() - begin, false);
+ throw t;
+ }
+ } finally {
+ if(max>0){
+ synchronized (count) {
+ count.notify();
+ }
+ }
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/filter/ClassLoaderFilter.java b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/filter/ClassLoaderFilter.java
new file mode 100644
index 0000000..f5b9a90
--- /dev/null
+++ b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/filter/ClassLoaderFilter.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.filter;
+
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.rpc.Filter;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.RpcException;
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Result;
+
+/**
+ * ClassLoaderInvokerFilter
+ *
+ * @author william.liangf
+ */
+@Extension("classloader")
+public class ClassLoaderFilter implements Filter {
+
+ public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
+ ClassLoader ocl = Thread.currentThread().getContextClassLoader();
+ Thread.currentThread().setContextClassLoader(invoker.getInterface().getClassLoader());
+ try {
+ return invoker.invoke(invocation);
+ } finally {
+ Thread.currentThread().setContextClassLoader(ocl);
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/filter/CompatibleFilter.java b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/filter/CompatibleFilter.java
new file mode 100644
index 0000000..278591f
--- /dev/null
+++ b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/filter/CompatibleFilter.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.filter;
+
+import java.lang.reflect.Method;
+import java.lang.reflect.Type;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.common.logger.Logger;
+import com.alibaba.dubbo.common.logger.LoggerFactory;
+import com.alibaba.dubbo.common.utils.CompatibleTypeUtils;
+import com.alibaba.dubbo.common.utils.PojoUtils;
+import com.alibaba.dubbo.rpc.Filter;
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.Result;
+import com.alibaba.dubbo.rpc.RpcException;
+import com.alibaba.dubbo.rpc.RpcResult;
+
+/**
+ * CompatibleFilter
+ *
+ * @author william.liangf
+ */
+@Extension("compatible")
+public class CompatibleFilter implements Filter {
+
+ private static Logger logger = LoggerFactory.getLogger(CompatibleFilter.class);
+
+ public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
+ Result result = invoker.invoke(invocation);
+ if (! invocation.getMethodName().startsWith("$") && ! result.hasException()) {
+ Object value = result.getResult();
+ if (value != null) {
+ try {
+ Method method = invoker.getInterface().getMethod(invocation.getMethodName(), invocation.getParameterTypes());
+ Class<?> type = method.getReturnType();
+ Object newValue;
+ String serialization = invoker.getUrl().getParameter(Constants.SERIALIZATION_KEY);
+ if ("json".equals(serialization)
+ || "fastjson".equals(serialization)){
+ Type gtype = method.getGenericReturnType();
+ newValue = PojoUtils.realize(value, type, gtype);
+ } else if (! type.isInstance(value)) {
+ newValue = PojoUtils.isPojo(type)
+ ? PojoUtils.realize(value, type)
+ : CompatibleTypeUtils.compatibleTypeConvert(value, type);
+
+ } else {
+ newValue = value;
+ }
+ if (newValue != value) {
+ result = new RpcResult(newValue);
+ }
+ } catch (Throwable t) {
+ logger.warn(t.getMessage(), t);
+ }
+ }
+ }
+ return result;
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/filter/ConsumerContextFilter.java b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/filter/ConsumerContextFilter.java
new file mode 100644
index 0000000..3e337d3
--- /dev/null
+++ b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/filter/ConsumerContextFilter.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.filter;
+
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.common.utils.NetUtils;
+import com.alibaba.dubbo.rpc.Filter;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.RpcContext;
+import com.alibaba.dubbo.rpc.RpcException;
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Result;
+
+/**
+ * ConsumerContextInvokerFilter
+ *
+ * @author william.liangf
+ */
+@Extension("consumercontext")
+public class ConsumerContextFilter implements Filter {
+
+ public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
+ RpcContext.getContext()
+ .setInvoker(invoker)
+ .setInvocation(invocation)
+ .setLocalAddress(NetUtils.getLocalHost(), 0)
+ .setRemoteAddress(invoker.getUrl().getHost(),
+ invoker.getUrl().getPort());
+ return invoker.invoke(invocation);
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/filter/ContextFilter.java b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/filter/ContextFilter.java
new file mode 100644
index 0000000..734a3a6
--- /dev/null
+++ b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/filter/ContextFilter.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.filter;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.rpc.Filter;
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.Result;
+import com.alibaba.dubbo.rpc.RpcContext;
+import com.alibaba.dubbo.rpc.RpcException;
+
+/**
+ * ContextInvokerFilter
+ *
+ * @author william.liangf
+ */
+@Extension("context")
+public class ContextFilter implements Filter {
+
+ public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
+ Map<String, String> attachments = invocation.getAttachments();
+ if (attachments != null) {
+ attachments = new HashMap<String, String>(attachments);
+ attachments.remove(Constants.PATH_KEY);
+ attachments.remove(Constants.GROUP_KEY);
+ attachments.remove(Constants.VERSION_KEY);
+ attachments.remove(Constants.DUBBO_VERSION_KEY);
+ attachments.remove(Constants.TOKEN_KEY);
+ attachments.remove(Constants.TIMEOUT_KEY);
+ }
+ RpcContext.getContext()
+ .setInvoker(invoker)
+ .setInvocation(invocation)
+ .setAttachments(attachments)
+ .setLocalAddress(invoker.getUrl().getHost(),
+ invoker.getUrl().getPort());
+ try {
+ return invoker.invoke(invocation);
+ } finally {
+ RpcContext.removeContext();
+ }
+ }
+}
\ No newline at end of file
diff --git a/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/filter/DeprecatedFilter.java b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/filter/DeprecatedFilter.java
new file mode 100644
index 0000000..f61cda8
--- /dev/null
+++ b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/filter/DeprecatedFilter.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.filter;
+
+import java.util.Set;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.common.logger.Logger;
+import com.alibaba.dubbo.common.logger.LoggerFactory;
+import com.alibaba.dubbo.common.utils.ConcurrentHashSet;
+import com.alibaba.dubbo.rpc.Filter;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.RpcException;
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Result;
+
+/**
+ * DeprecatedInvokerFilter
+ *
+ * @author william.liangf
+ */
+@Extension("deprecated")
+public class DeprecatedFilter implements Filter {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(DeprecatedFilter.class);
+
+ private static final Set<String> logged = new ConcurrentHashSet<String>();
+
+ public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
+ String key = invoker.getInterface().getName() + "." + invocation.getMethodName();
+ if (! logged.contains(key)) {
+ logged.add(key);
+ if (invoker.getUrl().getMethodParameter(invocation.getMethodName(), Constants.DEPRECATED_KEY, false)) {
+ LOGGER.error("The service method " + invoker.getInterface().getName() + "." + getMethodSignature(invocation) + " is DEPRECATED! Declare from " + invoker.getUrl());
+ }
+ }
+ return invoker.invoke(invocation);
+ }
+
+ private String getMethodSignature(Invocation invocation) {
+ StringBuilder buf = new StringBuilder(invocation.getMethodName());
+ buf.append("(");
+ Class<?>[] types = invocation.getParameterTypes();
+ if (types != null && types.length > 0) {
+ boolean first = true;
+ for (Class<?> type : types) {
+ if (first) {
+ first = false;
+ } else {
+ buf.append(", ");
+ }
+ buf.append(type.getSimpleName());
+ }
+ }
+ buf.append(")");
+ return buf.toString();
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/filter/EchoFilter.java b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/filter/EchoFilter.java
new file mode 100644
index 0000000..83fb1a1
--- /dev/null
+++ b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/filter/EchoFilter.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.filter;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.rpc.Filter;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.RpcException;
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Result;
+import com.alibaba.dubbo.rpc.RpcResult;
+
+/**
+ * EchoInvokerFilter
+ *
+ * @author william.liangf
+ */
+@Extension("echo")
+public class EchoFilter implements Filter {
+
+ public Result invoke(Invoker<?> invoker, Invocation inv) throws RpcException {
+ if(inv.getMethodName().equals(Constants.$ECHO) && inv.getArguments() != null && inv.getArguments().length == 1 )
+ return new RpcResult(inv.getArguments()[0]);
+ return invoker.invoke(inv);
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/filter/ExceptionFilter.java b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/filter/ExceptionFilter.java
new file mode 100644
index 0000000..3a58d33
--- /dev/null
+++ b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/filter/ExceptionFilter.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.filter;
+
+import java.lang.reflect.Method;
+
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.common.logger.Logger;
+import com.alibaba.dubbo.common.logger.LoggerFactory;
+import com.alibaba.dubbo.common.utils.ReflectUtils;
+import com.alibaba.dubbo.common.utils.StringUtils;
+import com.alibaba.dubbo.rpc.Filter;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.RpcException;
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Result;
+import com.alibaba.dubbo.rpc.RpcResult;
+import com.alibaba.dubbo.rpc.service.GenericService;
+
+/**
+ * ExceptionInvokerFilter
+ *
+ * @author william.liangf
+ */
+@Extension("exception")
+public class ExceptionFilter implements Filter {
+
+ private static final Logger logger = LoggerFactory.getLogger(ExceptionFilter.class);
+
+ public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
+ Result result = invoker.invoke(invocation);
+ if (result.hasException() && GenericService.class != invoker.getInterface()) {
+ try {
+ Throwable exception = result.getException();
+ try {
+ Method method = invoker.getInterface().getMethod(invocation.getMethodName(), invocation.getParameterTypes());
+ Class<?>[] exceptionClassses = method.getExceptionTypes();
+ for (Class<?> exceptionClass : exceptionClassses) {
+ if (exception.getClass().equals(exceptionClass)) {
+ return result;
+ }
+ }
+ } catch (NoSuchMethodException e) {
+ return result;
+ }
+ //在package里有类,直接抛出
+ String serviceFile = ReflectUtils.getCodeBase(invoker.getInterface());
+ String exceptionFile = ReflectUtils.getCodeBase(exception.getClass());
+ if (serviceFile == null || exceptionFile == null || serviceFile.equals(exceptionFile)){
+ return result;
+ }
+ //除上面二种情况外,server端打印日志,并包装成runtimeException抛给客户端
+ logger.error("Got unchecked and undeclare service method invoke exception: " + exception.getMessage(), exception);
+ return new RpcResult(new RuntimeException(StringUtils.toString(exception)));
+ } catch (Throwable e) {
+ logger.warn(e.getMessage(), e);
+ return result;
+ }
+ }
+ return result;
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/filter/ExecuteLimitFilter.java b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/filter/ExecuteLimitFilter.java
new file mode 100644
index 0000000..3dcd5f1
--- /dev/null
+++ b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/filter/ExecuteLimitFilter.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.filter;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.rpc.Filter;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.RpcStatus;
+import com.alibaba.dubbo.rpc.RpcException;
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Result;
+
+/**
+ * ThreadLimitInvokerFilter
+ *
+ * @author william.liangf
+ */
+@Extension("executelimit")
+public class ExecuteLimitFilter implements Filter {
+
+ public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
+ URL url = invoker.getUrl();
+ String methodName = invocation.getMethodName();
+ int max = url.getMethodParameter(methodName, Constants.EXECUTES_KEY, 0);
+ if (max > 0) {
+ RpcStatus count = RpcStatus.getStatus(url, invocation.getMethodName());
+ if (count.getActive() >= max) {
+ throw new RpcException("Failed to invoke invocation " + invocation + " in provider " + url + ", cause: The service using threads greater than <dubbo:service threads=\"" + max + "\" /> limited.");
+ }
+ }
+ long begin = System.currentTimeMillis();
+ RpcStatus.beginCount(url, methodName);
+ try {
+ Result result = invoker.invoke(invocation);
+ RpcStatus.endCount(url, methodName, System.currentTimeMillis() - begin, true);
+ return result;
+ } catch (RuntimeException t) {
+ RpcStatus.endCount(url, methodName, System.currentTimeMillis() - begin, false);
+ throw t;
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/filter/GenericFilter.java b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/filter/GenericFilter.java
new file mode 100644
index 0000000..23d2d84
--- /dev/null
+++ b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/filter/GenericFilter.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.rpc.filter;
+
+import java.lang.reflect.Method;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.common.utils.PojoUtils;
+import com.alibaba.dubbo.common.utils.ReflectUtils;
+import com.alibaba.dubbo.rpc.Filter;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.RpcException;
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Result;
+import com.alibaba.dubbo.rpc.RpcInvocation;
+import com.alibaba.dubbo.rpc.RpcResult;
+import com.alibaba.dubbo.rpc.service.GenericException;
+
+/**
+ * GenericInvokerFilter.
+ *
+ * @author william.liangf
+ */
+@Extension("generic")
+public class GenericFilter implements Filter {
+
+ public Result invoke(Invoker<?> invoker, Invocation inv) throws RpcException {
+ if (inv.getMethodName().equals(Constants.$INVOKE)
+ && inv.getArguments() != null
+ && inv.getArguments().length == 3
+ && ! invoker.getUrl().getParameter(Constants.GENERIC_KEY, false)) {
+ String name = ((String) inv.getArguments()[0]).trim();
+ String[] types = (String[]) inv.getArguments()[1];
+ Object[] args = (Object[]) inv.getArguments()[2];
+ try {
+ Method method = ReflectUtils.findMethodByMethodSignature(invoker.getInterface(), name, types);
+ Class<?>[] params = method.getParameterTypes();
+ if (args == null) {
+ args = new Object[params.length];
+ }
+ args = PojoUtils.realize(args, params);
+ Result result = invoker.invoke(new RpcInvocation(method, args));
+ if (result.hasException()) {
+ return new RpcResult(new GenericException(result.getException()));
+ }
+ return new RpcResult(PojoUtils.generalize(result.getResult()));
+ } catch (NoSuchMethodException e) {
+ throw new RpcException(e.getMessage(), e);
+ } catch (ClassNotFoundException e) {
+ throw new RpcException(e.getMessage(), e);
+ }
+ }
+ return invoker.invoke(inv);
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/filter/GenericImplFilter.java b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/filter/GenericImplFilter.java
new file mode 100644
index 0000000..6033585
--- /dev/null
+++ b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/filter/GenericImplFilter.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 com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.common.utils.PojoUtils;
+import com.alibaba.dubbo.common.utils.ReflectUtils;
+import com.alibaba.dubbo.rpc.Filter;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.RpcException;
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Result;
+import com.alibaba.dubbo.rpc.RpcInvocation;
+import com.alibaba.dubbo.rpc.RpcResult;
+
+/**
+ * GenericImplInvokerFilter
+ *
+ * @author william.liangf
+ */
+@Extension("genericimpl")
+public class GenericImplFilter implements Filter {
+
+ private static final Class<?>[] GENERIC_PARAMETER_TYPES = new Class<?>[] {String.class, String[].class, Object[].class};
+
+ public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
+ if (invoker.getUrl().getParameter(Constants.GENERIC_KEY, false)
+ && ! Constants.$INVOKE.equals(invocation.getMethodName())
+ && invocation instanceof RpcInvocation) {
+ RpcInvocation invocation2 = (RpcInvocation) invocation;
+ String methodName = invocation2.getMethodName();
+ Class<?>[] parameterTypes = invocation2.getParameterTypes();
+ Object[] arguments = invocation2.getArguments();
+
+ String[] types = new String[parameterTypes.length];
+ for (int i = 0; i < parameterTypes.length; i ++) {
+ types[i] = ReflectUtils.getName(parameterTypes[i]);
+ }
+ Object[] args = PojoUtils.generalize(arguments);
+
+ invocation2.setMethodName(Constants.$INVOKE);
+ invocation2.setParameterTypes(GENERIC_PARAMETER_TYPES);
+ invocation2.setArguments(new Object[] {methodName, types, args});
+ Result result = invoker.invoke(invocation2);
+
+ if (! result.hasException()) {
+ Object value = result.getResult();
+ try {
+ return new RpcResult(PojoUtils.realize(value, invoker.getInterface().getMethod(methodName, parameterTypes).getReturnType()));
+ } catch (NoSuchMethodException e) {
+ throw new RpcException(e.getMessage(), e);
+ }
+ }
+ return result;
+ }
+ return invoker.invoke(invocation);
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/filter/TimeoutFilter.java b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/filter/TimeoutFilter.java
new file mode 100644
index 0000000..1781ddb
--- /dev/null
+++ b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/filter/TimeoutFilter.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.filter;
+
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.common.logger.Logger;
+import com.alibaba.dubbo.common.logger.LoggerFactory;
+import com.alibaba.dubbo.rpc.Filter;
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.Result;
+import com.alibaba.dubbo.rpc.RpcException;
+
+/**
+ * 如果执行timeout,则log记录下,不干涉服务的运行
+ *
+ * @author chao.liuc
+ */
+@Extension("timeout")
+public class TimeoutFilter implements Filter {
+ private static final Logger logger = LoggerFactory.getLogger(TimeoutFilter.class);
+ public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
+ long start = System.currentTimeMillis();
+ Result result = invoker.invoke(invocation);
+ long elapsed = System.currentTimeMillis() - start;
+ if (invoker.getUrl() != null
+ && elapsed > invoker.getUrl().getMethodParameter(invocation.getMethodName(), "timeout", Integer.MAX_VALUE) ){
+ if(logger.isWarnEnabled()){
+ logger.warn("invoke time out. method: " + invocation.getMethodName() + "arguments: "+invocation.getArguments()+" , url is "+invoker.getUrl() + ", invoke elapsed "+elapsed+" ms.");
+ }
+ }
+ return result;
+ }
+}
\ No newline at end of file
diff --git a/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/filter/TokenFilter.java b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/filter/TokenFilter.java
new file mode 100644
index 0000000..6d2008c
--- /dev/null
+++ b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/filter/TokenFilter.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.filter;
+
+import java.util.Map;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.common.utils.ConfigUtils;
+import com.alibaba.dubbo.rpc.Filter;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.RpcContext;
+import com.alibaba.dubbo.rpc.RpcException;
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Result;
+
+/**
+ * TokenInvokerFilter
+ *
+ * @author william.liangf
+ */
+@Extension("token")
+public class TokenFilter implements Filter {
+
+ public Result invoke(Invoker<?> invoker, Invocation inv)
+ throws RpcException {
+ String token = invoker.getUrl().getParameter(Constants.TOKEN_KEY);
+ if (ConfigUtils.isNotEmpty(token)) {
+ Class<?> serviceType = invoker.getInterface();
+ Map<String, String> attachments = inv.getAttachments();
+ String remoteToken = attachments == null ? null : attachments.get(Constants.TOKEN_KEY);
+ if (! token.equals(remoteToken)) {
+ throw new RpcException("Invalid token! Forbid invoke remote service " + serviceType + " method " + inv.getMethodName() + "() from consumer " + RpcContext.getContext().getRemoteHost() + " to provider " + RpcContext.getContext().getLocalHost());
+ }
+ }
+ return invoker.invoke(inv);
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/listener/DeprecatedInvokerListener.java b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/listener/DeprecatedInvokerListener.java
new file mode 100644
index 0000000..f3a689c
--- /dev/null
+++ b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/listener/DeprecatedInvokerListener.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.listener;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.common.logger.Logger;
+import com.alibaba.dubbo.common.logger.LoggerFactory;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.RpcException;
+
+/**
+ * DeprecatedProtocolFilter
+ *
+ * @author william.liangf
+ */
+@Extension("deprecated")
+public class DeprecatedInvokerListener extends InvokerListenerAdapter {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(DeprecatedInvokerListener.class);
+
+ public void referred(Invoker<?> invoker) throws RpcException {
+ if (invoker.getUrl().getParameter(Constants.DEPRECATED_KEY, false)) {
+ LOGGER.error("The service " + invoker.getInterface().getName() + " is DEPRECATED! Declare from " + invoker.getUrl());
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/listener/ExporterListenerAdapter.java b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/listener/ExporterListenerAdapter.java
new file mode 100644
index 0000000..1859ac1
--- /dev/null
+++ b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/listener/ExporterListenerAdapter.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.listener;
+
+import com.alibaba.dubbo.rpc.Exporter;
+import com.alibaba.dubbo.rpc.ExporterListener;
+import com.alibaba.dubbo.rpc.RpcException;
+
+/**
+ * ExporterListenerAdapter
+ *
+ * @author william.liangf
+ */
+public abstract class ExporterListenerAdapter implements ExporterListener {
+
+ public void exported(Exporter<?> exporter) throws RpcException {
+ }
+
+ public void unexported(Exporter<?> exporter) throws RpcException {
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/listener/InvokerListenerAdapter.java b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/listener/InvokerListenerAdapter.java
new file mode 100644
index 0000000..57cfc3c
--- /dev/null
+++ b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/listener/InvokerListenerAdapter.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.listener;
+
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.InvokerListener;
+import com.alibaba.dubbo.rpc.RpcException;
+
+/**
+ * InvokerListenerAdapter
+ *
+ * @author william.liangf
+ */
+public abstract class InvokerListenerAdapter implements InvokerListener {
+
+ public void referred(Invoker<?> invoker) throws RpcException {
+ }
+
+ public void destroyed(Invoker<?> invoker) {
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/listener/ListenerExporterWrapper.java b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/listener/ListenerExporterWrapper.java
new file mode 100644
index 0000000..af37329
--- /dev/null
+++ b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/listener/ListenerExporterWrapper.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.listener;
+
+import java.util.List;
+
+import com.alibaba.dubbo.common.logger.Logger;
+import com.alibaba.dubbo.common.logger.LoggerFactory;
+import com.alibaba.dubbo.rpc.Exporter;
+import com.alibaba.dubbo.rpc.ExporterListener;
+import com.alibaba.dubbo.rpc.Invoker;
+
+/**
+ * ListenerExporter
+ *
+ * @author william.liangf
+ */
+public class ListenerExporterWrapper<T> implements Exporter<T> {
+
+ private static final Logger logger = LoggerFactory.getLogger(ListenerExporterWrapper.class);
+
+ private final Exporter<T> exporter;
+
+ private final List<ExporterListener> listeners;
+
+ public ListenerExporterWrapper(Exporter<T> exporter, List<ExporterListener> listeners){
+ if (exporter == null) {
+ throw new IllegalArgumentException("exporter == null");
+ }
+ this.exporter = exporter;
+ this.listeners = listeners;
+ if (listeners != null && listeners.size() > 0) {
+ RuntimeException exception = null;
+ for (ExporterListener listener : listeners) {
+ if (listener != null) {
+ try {
+ listener.exported(this);
+ } catch (RuntimeException t) {
+ logger.error(t.getMessage(), t);
+ exception = t;
+ }
+ }
+ }
+ if (exception != null) {
+ throw exception;
+ }
+ }
+ }
+
+ public Invoker<T> getInvoker() {
+ return exporter.getInvoker();
+ }
+
+ public void unexport() {
+ try {
+ exporter.unexport();
+ } finally {
+ if (listeners != null && listeners.size() > 0) {
+ RuntimeException exception = null;
+ for (ExporterListener listener : listeners) {
+ if (listener != null) {
+ try {
+ listener.unexported(this);
+ } catch (RuntimeException t) {
+ logger.error(t.getMessage(), t);
+ exception = t;
+ }
+ }
+ }
+ if (exception != null) {
+ throw exception;
+ }
+ }
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/listener/ListenerInvokerWrapper.java b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/listener/ListenerInvokerWrapper.java
new file mode 100644
index 0000000..767c733
--- /dev/null
+++ b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/listener/ListenerInvokerWrapper.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.listener;
+
+import java.util.List;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.logger.Logger;
+import com.alibaba.dubbo.common.logger.LoggerFactory;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.InvokerListener;
+import com.alibaba.dubbo.rpc.RpcException;
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Result;
+
+/**
+ * ListenerInvoker
+ *
+ * @author william.liangf
+ */
+public class ListenerInvokerWrapper<T> implements Invoker<T> {
+
+ private static final Logger logger = LoggerFactory.getLogger(ListenerInvokerWrapper.class);
+
+ private final Invoker<T> invoker;
+
+ private final List<InvokerListener> listeners;
+
+ public ListenerInvokerWrapper(Invoker<T> invoker, List<InvokerListener> listeners){
+ if (invoker == null) {
+ throw new IllegalArgumentException("invoker == null");
+ }
+ this.invoker = invoker;
+ this.listeners = listeners;
+ if (listeners != null && listeners.size() > 0) {
+ for (InvokerListener listener : listeners) {
+ if (listener != null) {
+ try {
+ listener.referred(invoker);
+ } catch (Throwable t) {
+ logger.error(t.getMessage(), t);
+ }
+ }
+ }
+ }
+ }
+
+ public Class<T> getInterface() {
+ return invoker.getInterface();
+ }
+
+ public URL getUrl() {
+ return invoker.getUrl();
+ }
+
+ public boolean isAvailable() {
+ return invoker.isAvailable();
+ }
+
+ public Result invoke(Invocation invocation) throws RpcException {
+ return invoker.invoke(invocation);
+ }
+
+ @Override
+ public String toString() {
+ return getInterface() + " -> " + getUrl()==null?" ":getUrl().toString();
+ }
+
+ public void destroy() {
+ try {
+ invoker.destroy();
+ } finally {
+ if (listeners != null && listeners.size() > 0) {
+ for (InvokerListener listener : listeners) {
+ if (listener != null) {
+ try {
+ listener.destroyed(invoker);
+ } catch (Throwable t) {
+ logger.error(t.getMessage(), t);
+ }
+ }
+ }
+ }
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/protocol/AbstractExporter.java b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/protocol/AbstractExporter.java
new file mode 100644
index 0000000..3b3c98f
--- /dev/null
+++ b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/protocol/AbstractExporter.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.protocol;
+
+import com.alibaba.dubbo.common.logger.Logger;
+import com.alibaba.dubbo.common.logger.LoggerFactory;
+import com.alibaba.dubbo.rpc.Exporter;
+import com.alibaba.dubbo.rpc.Invoker;
+
+/**
+ * AbstractExporter.
+ *
+ * @author qianlei
+ * @author william.liangf
+ */
+public abstract class AbstractExporter<T> implements Exporter<T> {
+
+ protected final Logger logger = LoggerFactory.getLogger(getClass());
+
+ private final Invoker<T> invoker;
+
+ private volatile boolean unexported = false;
+
+ public AbstractExporter(Invoker<T> invoker) {
+ if (invoker == null)
+ throw new IllegalStateException("service invoker == null");
+ if (invoker.getInterface() == null)
+ throw new IllegalStateException("service type == null");
+ if (invoker.getUrl() == null)
+ throw new IllegalStateException("service url == null");
+ this.invoker = invoker;
+ }
+
+ public Invoker<T> getInvoker() {
+ return invoker;
+ }
+
+ public void unexport() {
+ if (unexported) {
+ throw new IllegalStateException("The exporter " + this + " unexported!");
+ }
+ unexported = true;
+ getInvoker().destroy();
+ }
+
+ public String toString() {
+ return getInvoker().toString();
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/protocol/AbstractInvoker.java b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/protocol/AbstractInvoker.java
new file mode 100644
index 0000000..c67d729
--- /dev/null
+++ b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/protocol/AbstractInvoker.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.rpc.protocol;
+
+import java.lang.reflect.InvocationTargetException;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.Version;
+import com.alibaba.dubbo.common.logger.Logger;
+import com.alibaba.dubbo.common.logger.LoggerFactory;
+import com.alibaba.dubbo.common.utils.NetUtils;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.RpcContext;
+import com.alibaba.dubbo.rpc.RpcException;
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Result;
+import com.alibaba.dubbo.rpc.RpcInvocation;
+import com.alibaba.dubbo.rpc.RpcResult;
+
+/**
+ * AbstractInvoker.
+ *
+ * @author qian.lei
+ * @author william.liangf
+ */
+public abstract class AbstractInvoker<T> implements Invoker<T> {
+
+ protected final Logger logger = LoggerFactory.getLogger(getClass());
+
+ private final Class<T> type;
+
+ private final URL url;
+
+ private final Map<String, String> attachment;
+
+ private volatile boolean available = true;
+
+ private volatile boolean destroyed = false;
+
+ public AbstractInvoker(Class<T> type, URL url){
+ this(type, url, (Map<String, String>) null);
+ }
+
+ public AbstractInvoker(Class<T> type, URL url, String[] keys) {
+ this(type, url, convertAttachment(url, keys));
+ }
+
+ public AbstractInvoker(Class<T> type, URL url, Map<String, String> attachment) {
+ if (type == null)
+ throw new IllegalArgumentException("service type == null");
+ if (url == null)
+ throw new IllegalArgumentException("service url == null");
+ this.type = type;
+ this.url = url;
+ this.attachment = attachment == null ? null : Collections.unmodifiableMap(attachment);
+ }
+
+ private static Map<String, String> convertAttachment(URL url, String[] keys) {
+ if (keys == null || keys.length == 0) {
+ return null;
+ }
+ Map<String, String> attachment = new HashMap<String, String>();
+ for (String key : keys) {
+ String value = url.getParameter(key);
+ if (value != null && value.length() > 0) {
+ attachment.put(key, value);
+ }
+ }
+ return attachment;
+ }
+
+ public Class<T> getInterface() {
+ return type;
+ }
+
+ public URL getUrl() {
+ return url;
+ }
+
+ public boolean isAvailable() {
+ return available;
+ }
+
+ protected void setAvailable(boolean available) {
+ this.available = available;
+ }
+
+ public void destroy() {
+ if (destroyed) {
+ return;
+ }
+ destroyed = true;
+ setAvailable(false);
+ }
+
+ 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;
+ Map<String, String> attachments = new HashMap<String, String>();
+ if (attachment != null && attachment.size() > 0) {
+ attachments.putAll(attachment);
+ }
+ Map<String, String> context = RpcContext.getContext().getAttachments();
+ if (context != null) {
+ attachments.putAll(context);
+ }
+ if (invocation.getAttachments() != null) {
+ attachments.putAll(invocation.getAttachments());
+ }
+ invocation.setAttachments(attachments);
+ try {
+ return doInvoke(invocation);
+ } catch (InvocationTargetException e) { // biz exception
+ Throwable te = e.getTargetException();
+ if (te == null) {
+ return new RpcResult(e);
+ } else {
+ if (te instanceof RpcException) {
+ ((RpcException) te).setCode(RpcException.BIZ_EXCEPTION);
+ }
+ return new RpcResult(te);
+ }
+ } catch (RpcException e) {
+ if (e.isBiz()) {
+ return new RpcResult(e);
+ } else {
+ throw e;
+ }
+ } catch (Throwable e) {
+ return new RpcResult(e);
+ }
+ }
+
+ protected abstract Result doInvoke(Invocation invocation) throws Throwable;
+
+}
\ No newline at end of file
diff --git a/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/protocol/AbstractProtocol.java b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/protocol/AbstractProtocol.java
new file mode 100644
index 0000000..2c48225
--- /dev/null
+++ b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/protocol/AbstractProtocol.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.protocol;
+
+import java.util.ArrayList;
+import java.util.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.rpc.Exporter;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.Protocol;
+import com.alibaba.dubbo.rpc.RpcConstants;
+
+/**
+ * abstract ProtocolSupport.
+ *
+ * @author qian.lei
+ * @author william.liangf
+ */
+public abstract class AbstractProtocol implements Protocol {
+
+ protected final Logger logger = LoggerFactory.getLogger(getClass());
+
+ protected final Map<String, Exporter<?>> exporterMap = new ConcurrentHashMap<String, Exporter<?>>();
+
+ protected final Set<Invoker<?>> invokers = new ConcurrentHashSet<Invoker<?>>();
+
+ protected static String serviceKey(URL url) {
+ return serviceKey(url.getPort(), url.getPath(), url.getParameter(Constants.VERSION_KEY),
+ url.getParameter(Constants.GROUP_KEY));
+ }
+
+ protected static String serviceKey(int port, String serviceName, String serviceVersion, String serviceGroup) {
+ StringBuilder buf = new StringBuilder();
+ if (serviceGroup != null && serviceGroup.length() > 0) {
+ buf.append(serviceGroup);
+ buf.append("/");
+ }
+ buf.append(serviceName);
+ if (serviceVersion != null && serviceVersion.length() > 0 && ! "0.0.0".equals(serviceVersion)) {
+ buf.append(":");
+ buf.append(serviceVersion);
+ }
+ buf.append(":");
+ buf.append(port);
+ return buf.toString();
+ }
+
+ public void destroy() {
+ for (Invoker<?> invoker : invokers){
+ if (invoker != null) {
+ invokers.remove(invoker);
+ try {
+ if (logger.isInfoEnabled()) {
+ logger.info("Destroy reference: " + invoker.getUrl());
+ }
+ invoker.destroy();
+ } catch (Throwable t) {
+ logger.warn(t.getMessage(), t);
+ }
+ }
+ }
+ for (String key : new ArrayList<String>(exporterMap.keySet())) {
+ Exporter<?> exporter = exporterMap.remove(key);
+ if (exporter != null) {
+ try {
+ if (logger.isInfoEnabled()) {
+ logger.info("Unexport service: " + exporter.getInvoker().getUrl());
+ }
+ exporter.unexport();
+ } catch (Throwable t) {
+ logger.warn(t.getMessage(), t);
+ }
+ }
+ }
+ }
+ @SuppressWarnings("deprecation")
+ protected static int getServerShutdownTimeout() {
+ int timeout = RpcConstants.DEFAULT_SERVER_SHUTDOWN_TIMEOUT;
+ String value = System.getProperty(RpcConstants.SHUTDOWN_TIMEOUT_KEY);
+ if (value != null && value.length() > 0) {
+ try{
+ timeout = Integer.parseInt(value);
+ }catch (Exception e) {
+ }
+ } else {
+ value = System.getProperty(RpcConstants.SHUTDOWN_TIMEOUT_SECONDS_KEY);
+ if (value != null && value.length() > 0) {
+ try{
+ timeout = Integer.parseInt(value) * 1000;
+ }catch (Exception e) {
+ }
+ }
+ }
+
+ return timeout;
+ }
+}
\ No newline at end of file
diff --git a/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/protocol/InvokerWrapper.java b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/protocol/InvokerWrapper.java
new file mode 100644
index 0000000..4f54dc6
--- /dev/null
+++ b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/protocol/InvokerWrapper.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.protocol;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.Result;
+import com.alibaba.dubbo.rpc.RpcException;
+
+/**
+ * InvokerWrapper
+ *
+ * @author william.liangf
+ */
+public class InvokerWrapper<T> implements Invoker<T> {
+
+ private final Invoker<T> invoker;
+
+ private final URL url;
+
+ public InvokerWrapper(Invoker<T> invoker, URL url){
+ this.invoker = invoker;
+ this.url = url;
+ }
+
+ public Class<T> getInterface() {
+ return invoker.getInterface();
+ }
+
+ public URL getUrl() {
+ return url;
+ }
+
+ public boolean isAvailable() {
+ return invoker.isAvailable();
+ }
+
+ public Result invoke(Invocation invocation) throws RpcException {
+ return invoker.invoke(invocation);
+ }
+
+ public void destroy() {
+ invoker.destroy();
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/protocol/ProtocolFilterWrapper.java b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/protocol/ProtocolFilterWrapper.java
new file mode 100644
index 0000000..de915dc
--- /dev/null
+++ b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/protocol/ProtocolFilterWrapper.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.protocol;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.ExtensionLoader;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.utils.ConfigUtils;
+import com.alibaba.dubbo.rpc.Exporter;
+import com.alibaba.dubbo.rpc.Filter;
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.Protocol;
+import com.alibaba.dubbo.rpc.Result;
+import com.alibaba.dubbo.rpc.RpcConstants;
+import com.alibaba.dubbo.rpc.RpcException;
+
+/**
+ * ListenerProtocol
+ *
+ * @author william.liangf
+ */
+public class ProtocolFilterWrapper implements Protocol {
+
+ private final Protocol protocol;
+
+ public ProtocolFilterWrapper(Protocol protocol){
+ if (protocol == null) {
+ throw new IllegalArgumentException("protocol == null");
+ }
+ this.protocol = protocol;
+ }
+
+ public int getDefaultPort() {
+ return protocol.getDefaultPort();
+ }
+
+ public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
+ if (Constants.REGISTRY_PROTOCOL.equals(invoker.getUrl().getProtocol())) {
+ return protocol.export(invoker);
+ }
+ return protocol.export(buildInvokerChain(invoker, invoker.getUrl().getParameter(Constants.SERVICE_FILTER_KEY), RpcConstants.DEFAULT_SERVICE_FILTERS));
+ }
+
+ public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {
+ if (Constants.REGISTRY_PROTOCOL.equals(url.getProtocol())) {
+ return protocol.refer(type, url);
+ }
+ return buildInvokerChain(protocol.refer(type, url), url.getParameter(Constants.REFERENCE_FILTER_KEY), RpcConstants.DEFAULT_REFERENCE_FILTERS);
+ }
+
+ public void destroy() {
+ protocol.destroy();
+ }
+
+ private static <T> Invoker<T> buildInvokerChain(final Invoker<T> invoker, String config, List<String> defaults) {
+ List<String> names = ConfigUtils.mergeValues(Filter.class, config, defaults);
+ Invoker<T> last = invoker;
+ if (names.size() > 0) {
+ List<Filter> filters = new ArrayList<Filter>(names.size());
+ for (String name : names) {
+ filters.add(ExtensionLoader.getExtensionLoader(Filter.class).getExtension(name));
+ }
+ if (filters.size() > 0) {
+ for (int i = filters.size() - 1; i >= 0; i --) {
+ final Filter filter = filters.get(i);
+ final Invoker<T> next = last;
+ last = new Invoker<T>() {
+
+ public Class<T> getInterface() {
+ return invoker.getInterface();
+ }
+
+ public URL getUrl() {
+ return invoker.getUrl();
+ }
+
+ public boolean isAvailable() {
+ return invoker.isAvailable();
+ }
+
+ public Result invoke(Invocation invocation) throws RpcException {
+ return filter.invoke(next, invocation);
+ }
+
+ public void destroy() {
+ invoker.destroy();
+ }
+
+ @Override
+ public String toString() {
+ return invoker.toString();
+ }
+ };
+ }
+ }
+ }
+ return last;
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/protocol/ProtocolListenerWrapper.java b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/protocol/ProtocolListenerWrapper.java
new file mode 100644
index 0000000..369af61
--- /dev/null
+++ b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/protocol/ProtocolListenerWrapper.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.protocol;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.ExtensionLoader;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.utils.ConfigUtils;
+import com.alibaba.dubbo.rpc.Exporter;
+import com.alibaba.dubbo.rpc.ExporterListener;
+import com.alibaba.dubbo.rpc.Filter;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.InvokerListener;
+import com.alibaba.dubbo.rpc.Protocol;
+import com.alibaba.dubbo.rpc.RpcConstants;
+import com.alibaba.dubbo.rpc.RpcException;
+import com.alibaba.dubbo.rpc.listener.ListenerExporterWrapper;
+import com.alibaba.dubbo.rpc.listener.ListenerInvokerWrapper;
+
+/**
+ * ListenerProtocol
+ *
+ * @author william.liangf
+ */
+public class ProtocolListenerWrapper implements Protocol {
+
+ private final Protocol protocol;
+
+ public ProtocolListenerWrapper(Protocol protocol){
+ if (protocol == null) {
+ throw new IllegalArgumentException("protocol == null");
+ }
+ this.protocol = protocol;
+ }
+
+ public int getDefaultPort() {
+ return protocol.getDefaultPort();
+ }
+
+ public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
+ if (Constants.REGISTRY_PROTOCOL.equals(invoker.getUrl().getProtocol())) {
+ return protocol.export(invoker);
+ }
+ return new ListenerExporterWrapper<T>(protocol.export(invoker),
+ buildServiceListeners(invoker.getUrl().getParameter(Constants.EXPORTER_LISTENER_KEY), RpcConstants.DEFAULT_EXPORTER_LISTENERS));
+ }
+
+ public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {
+ if (Constants.REGISTRY_PROTOCOL.equals(url.getProtocol())) {
+ return protocol.refer(type, url);
+ }
+ return new ListenerInvokerWrapper<T>(protocol.refer(type, url),
+ buildReferenceListeners(url.getParameter(Constants.INVOKER_LISTENER_KEY), RpcConstants.DEFAULT_INVOKER_LISTENERS));
+ }
+
+ public void destroy() {
+ protocol.destroy();
+ }
+
+ private static List<ExporterListener> buildServiceListeners(String config, List<String> defaults) {
+ List<String> names = ConfigUtils.mergeValues(Filter.class, config, defaults);
+ List<ExporterListener> listeners = new ArrayList<ExporterListener>();
+ if (names.size() > 0) {
+ for (String name : names) {
+ listeners.add(ExtensionLoader.getExtensionLoader(ExporterListener.class).getExtension(name));
+ }
+ }
+ return Collections.unmodifiableList(listeners);
+ }
+
+ private static List<InvokerListener> buildReferenceListeners(String config, List<String> defaults) {
+ List<String> names = ConfigUtils.mergeValues(Filter.class, config, defaults);
+ List<InvokerListener> listeners = new ArrayList<InvokerListener>();
+ if (names.size() > 0) {
+ for (String name : names) {
+ listeners.add(ExtensionLoader.getExtensionLoader(InvokerListener.class).getExtension(name));
+ }
+ }
+ return Collections.unmodifiableList(listeners);
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/proxy/AbstractProxyFactory.java b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/proxy/AbstractProxyFactory.java
new file mode 100644
index 0000000..3ee576b
--- /dev/null
+++ b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/proxy/AbstractProxyFactory.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.proxy;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.utils.ReflectUtils;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.ProxyFactory;
+import com.alibaba.dubbo.rpc.RpcException;
+import com.alibaba.dubbo.rpc.service.EchoService;
+
+/**
+ * AbstractProxyFactory
+ *
+ * @author william.liangf
+ */
+public abstract class AbstractProxyFactory implements ProxyFactory {
+
+ public <T> T getProxy(Invoker<T> invoker) throws RpcException {
+ Class<?>[] interfaces = null;
+ String config = invoker.getUrl().getParameter("interfaces");
+ if (config != null && config.length() > 0) {
+ String[] types = Constants.COMMA_SPLIT_PATTERN.split(config);
+ if (types != null && types.length > 0) {
+ interfaces = new Class<?>[types.length + 2];
+ interfaces[0] = invoker.getInterface();
+ interfaces[1] = EchoService.class;
+ for (int i = 0; i < types.length; i ++) {
+ interfaces[i + 1] = ReflectUtils.forName(types[i]);
+ }
+ }
+ }
+ if (interfaces == null) {
+ interfaces = new Class<?>[] {invoker.getInterface(), EchoService.class};
+ }
+ return getProxy(invoker, interfaces);
+ }
+
+ public abstract <T> T getProxy(Invoker<T> invoker, Class<?>[] types);
+
+}
\ No newline at end of file
diff --git a/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/proxy/AbstractProxyInvoker.java b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/proxy/AbstractProxyInvoker.java
new file mode 100644
index 0000000..b242cc6
--- /dev/null
+++ b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/proxy/AbstractProxyInvoker.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.proxy;
+
+import java.lang.reflect.InvocationTargetException;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.RpcException;
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Result;
+import com.alibaba.dubbo.rpc.RpcResult;
+
+/**
+ * InvokerWrapper
+ *
+ * @author william.liangf
+ */
+public abstract class AbstractProxyInvoker<T> implements Invoker<T> {
+
+ private final T proxy;
+
+ private final Class<T> type;
+
+ private final URL url;
+
+ public AbstractProxyInvoker(T proxy, Class<T> type, URL url){
+ if (proxy == null) {
+ throw new IllegalArgumentException("proxy == null");
+ }
+ if (type == null) {
+ throw new IllegalArgumentException("interface == null");
+ }
+ if (! type.isInstance(proxy)) {
+ throw new IllegalArgumentException(proxy.getClass().getName() + " not implement interface " + type);
+ }
+ this.proxy = proxy;
+ this.type = type;
+ this.url = url;
+ }
+
+ public Class<T> getInterface() {
+ return type;
+ }
+
+ public URL getUrl() {
+ return url;
+ }
+
+ public boolean isAvailable() {
+ return true;
+ }
+
+ public void destroy() {
+ }
+
+ public Result invoke(Invocation invocation) throws RpcException {
+ try {
+ return new RpcResult(doInvoke(proxy, invocation.getMethodName(), invocation.getParameterTypes(), invocation.getArguments()));
+ } catch (InvocationTargetException e) {
+ return new RpcResult(e.getTargetException());
+ } catch (Throwable e) {
+ throw new RpcException(e.getMessage(), e);
+ }
+ }
+
+ protected abstract Object doInvoke(T proxy, String methodName, Class<?>[] parameterTypes, Object[] arguments) throws Throwable;
+
+ @Override
+ public String toString() {
+ return getInterface() + " -> " + getUrl()==null?" ":getUrl().toString();
+ }
+
+
+}
\ No newline at end of file
diff --git a/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/proxy/InvokerInvocationHandler.java b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/proxy/InvokerInvocationHandler.java
new file mode 100644
index 0000000..9e21bec
--- /dev/null
+++ b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/proxy/InvokerInvocationHandler.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.proxy;
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.RpcInvocation;
+
+/**
+ * InvokerHandler
+ *
+ * @author william.liangf
+ */
+public class InvokerInvocationHandler implements InvocationHandler {
+
+ private final Invoker<?> invoker;
+
+ public InvokerInvocationHandler(Invoker<?> handler){
+ this.invoker = handler;
+ }
+
+ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+ String methodName = method.getName();
+ Class<?>[] parameterTypes = method.getParameterTypes();
+ if (method.getDeclaringClass() == Object.class) {
+ return method.invoke(invoker, args);
+ }
+ if ("toString".equals(methodName) && parameterTypes.length == 0) {
+ return invoker.toString();
+ }
+ if ("hashCode".equals(methodName) && parameterTypes.length == 0) {
+ return invoker.hashCode();
+ }
+ if ("equals".equals(methodName) && parameterTypes.length == 1) {
+ return invoker.equals(args[0]);
+ }
+ return invoker.invoke(new RpcInvocation(method, args)).recreate();
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/proxy/javassist/JavassistProxyFactory.java b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/proxy/javassist/JavassistProxyFactory.java
new file mode 100644
index 0000000..2101c79
--- /dev/null
+++ b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/proxy/javassist/JavassistProxyFactory.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.proxy.javassist;
+
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.bytecode.Proxy;
+import com.alibaba.dubbo.common.bytecode.Wrapper;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.proxy.AbstractProxyFactory;
+import com.alibaba.dubbo.rpc.proxy.InvokerInvocationHandler;
+import com.alibaba.dubbo.rpc.proxy.AbstractProxyInvoker;
+
+/**
+ * JavaassistRpcProxyFactory
+
+ * @author william.liangf
+ */
+@Extension("javassist")
+public class JavassistProxyFactory extends AbstractProxyFactory {
+
+ @SuppressWarnings("unchecked")
+ public <T> T getProxy(Invoker<T> invoker, Class<?>[] interfaces) {
+ return (T) Proxy.getProxy(interfaces).newInstance(new InvokerInvocationHandler(invoker));
+ }
+
+ public <T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) {
+ // TODO Wrapper类不能正确处理带$的类名
+ final Wrapper wrapper = Wrapper.getWrapper(proxy.getClass().getName().indexOf('$') < 0 ? proxy.getClass() : type);
+ return new AbstractProxyInvoker<T>(proxy, type, url) {
+ @Override
+ protected Object doInvoke(T proxy, String methodName,
+ Class<?>[] parameterTypes,
+ Object[] arguments) throws Throwable {
+ return wrapper.invokeMethod(proxy, methodName, parameterTypes, arguments);
+ }
+ };
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/proxy/jdk/JdkProxyFactory.java b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/proxy/jdk/JdkProxyFactory.java
new file mode 100644
index 0000000..dace6c2
--- /dev/null
+++ b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/proxy/jdk/JdkProxyFactory.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.proxy.jdk;
+
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.proxy.AbstractProxyFactory;
+import com.alibaba.dubbo.rpc.proxy.InvokerInvocationHandler;
+import com.alibaba.dubbo.rpc.proxy.AbstractProxyInvoker;
+
+/**
+ * JavaassistRpcProxyFactory
+
+ * @author william.liangf
+ */
+@Extension("jdk")
+public class JdkProxyFactory extends AbstractProxyFactory {
+
+ @SuppressWarnings("unchecked")
+ public <T> T getProxy(Invoker<T> invoker, Class<?>[] interfaces) {
+ return (T) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), interfaces, new InvokerInvocationHandler(invoker));
+ }
+
+ public <T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) {
+ return new AbstractProxyInvoker<T>(proxy, type, url) {
+ @Override
+ protected Object doInvoke(T proxy, String methodName,
+ Class<?>[] parameterTypes,
+ Object[] arguments) throws Throwable {
+ Method method = proxy.getClass().getMethod(methodName, parameterTypes);
+ return method.invoke(proxy, arguments);
+ }
+ };
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/proxy/wrapper/MockProxyFactoryWrapper.java b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/proxy/wrapper/MockProxyFactoryWrapper.java
new file mode 100644
index 0000000..2494460
--- /dev/null
+++ b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/proxy/wrapper/MockProxyFactoryWrapper.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.proxy.wrapper;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.Version;
+import com.alibaba.dubbo.common.logger.Logger;
+import com.alibaba.dubbo.common.logger.LoggerFactory;
+import com.alibaba.dubbo.common.utils.ConfigUtils;
+import com.alibaba.dubbo.common.utils.NetUtils;
+import com.alibaba.dubbo.common.utils.ReflectUtils;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.ProxyFactory;
+import com.alibaba.dubbo.rpc.RpcException;
+import com.alibaba.dubbo.rpc.service.GenericService;
+
+/**
+ * MockProxyFactoryWrapper
+ *
+ * @author william.liangf
+ */
+public class MockProxyFactoryWrapper implements ProxyFactory {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(MockProxyFactoryWrapper.class);
+
+ private final ProxyFactory proxyFactory;
+
+ public MockProxyFactoryWrapper(ProxyFactory proxyFactory) {
+ this.proxyFactory = proxyFactory;
+ }
+
+ @SuppressWarnings({ "unchecked"})
+ public <T> T getProxy(Invoker<T> invoker) throws RpcException {
+ String mock = invoker.getUrl().getParameter(Constants.MOCK_KEY);
+ if (ConfigUtils.isNotEmpty(mock) && GenericService.class != invoker.getInterface()) {
+ Class<?> serviceType = invoker.getInterface();
+ if (ConfigUtils.isDefault(mock)) {
+ mock = serviceType.getName() + "Mock";
+ }
+ try {
+ Class<?> mockClass = ReflectUtils.forName(mock);
+ if (! serviceType.isAssignableFrom(mockClass)) {
+ throw new IllegalArgumentException("The mock implemention class " + mockClass.getName() + " not implement interface " + serviceType.getName());
+ }
+ try {
+ T mockObject = (T) mockClass.newInstance();
+ invoker = new MockProxyInvoker<T>(invoker, proxyFactory.getInvoker(mockObject, invoker.getInterface(), invoker.getUrl()));
+ } catch (InstantiationException e) {
+ throw new IllegalStateException("No such empty constructor \"public " + mockClass.getSimpleName() + "()\" in mock implemention class " + mockClass.getName(), e);
+ }
+ } catch (Throwable t) {
+ LOGGER.error("Failed to create mock implemention class " + mock + " in consumer " + NetUtils.getLocalHost() + " use dubbo version " + Version.getVersion() + ", cause: " + t.getMessage(), t);
+ // ignore
+ }
+ }
+ return proxyFactory.getProxy(invoker);
+ }
+
+ public <T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) throws RpcException {
+ return proxyFactory.getInvoker(proxy, type, url);
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/proxy/wrapper/MockProxyInvoker.java b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/proxy/wrapper/MockProxyInvoker.java
new file mode 100644
index 0000000..63869c4
--- /dev/null
+++ b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/proxy/wrapper/MockProxyInvoker.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.proxy.wrapper;
+
+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;
+
+/**
+ * MockInvoker
+ *
+ * @author william.liangf
+ */
+public class MockProxyInvoker<T> implements Invoker<T> {
+
+ private final Invoker<T> invoker;
+
+ private final Invoker<T> mockInvoker;
+
+ public MockProxyInvoker(Invoker<T> invoker, Invoker<T> mockInvoker) {
+ this.invoker = invoker;
+ this.mockInvoker = mockInvoker;
+ }
+
+ public Class<T> getInterface() {
+ return invoker.getInterface();
+ }
+
+ public URL getUrl() {
+ return invoker.getUrl();
+ }
+
+ public boolean isAvailable() {
+ return invoker.isAvailable() && mockInvoker.isAvailable();
+ }
+
+ public Result invoke(Invocation invocation) throws RpcException {
+ try {
+ return invoker.invoke(invocation);
+ } catch (RpcException e) {
+ return mockInvoker.invoke(invocation);
+ }
+ }
+
+ public void destroy() {
+ try {
+ invoker.destroy();
+ } finally {
+ mockInvoker.destroy();
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/proxy/wrapper/StubProxyFactoryWrapper.java b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/proxy/wrapper/StubProxyFactoryWrapper.java
new file mode 100644
index 0000000..47ad11d
--- /dev/null
+++ b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/proxy/wrapper/StubProxyFactoryWrapper.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.proxy.wrapper;
+
+import java.lang.reflect.Constructor;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.Version;
+import com.alibaba.dubbo.common.bytecode.Wrapper;
+import com.alibaba.dubbo.common.logger.Logger;
+import com.alibaba.dubbo.common.logger.LoggerFactory;
+import com.alibaba.dubbo.common.utils.ConfigUtils;
+import com.alibaba.dubbo.common.utils.NetUtils;
+import com.alibaba.dubbo.common.utils.ReflectUtils;
+import com.alibaba.dubbo.common.utils.StringUtils;
+import com.alibaba.dubbo.rpc.Exporter;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.Protocol;
+import com.alibaba.dubbo.rpc.ProxyFactory;
+import com.alibaba.dubbo.rpc.RpcConstants;
+import com.alibaba.dubbo.rpc.RpcException;
+import com.alibaba.dubbo.rpc.service.GenericService;
+
+/**
+ * StubProxyFactoryWrapper
+ *
+ * @author william.liangf
+ */
+public class StubProxyFactoryWrapper implements ProxyFactory {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(StubProxyFactoryWrapper.class);
+
+ private final ProxyFactory proxyFactory;
+
+ private Protocol protocol;
+
+ public StubProxyFactoryWrapper(ProxyFactory proxyFactory) {
+ this.proxyFactory = proxyFactory;
+ }
+
+ public void setProtocol(Protocol protocol) {
+ this.protocol = protocol;
+ }
+
+ @SuppressWarnings({ "unchecked", "rawtypes" })
+ public <T> T getProxy(Invoker<T> invoker) throws RpcException {
+ T proxy = proxyFactory.getProxy(invoker);
+ if (GenericService.class != invoker.getInterface()) {
+ String stub = invoker.getUrl().getParameter(Constants.STUB_KEY, invoker.getUrl().getParameter(Constants.LOCAL_KEY));
+ if (ConfigUtils.isNotEmpty(stub)) {
+ Class<?> serviceType = invoker.getInterface();
+ if (ConfigUtils.isDefault(stub)) {
+ if (invoker.getUrl().hasParameter(Constants.STUB_KEY)) {
+ stub = serviceType.getName() + "Stub";
+ } else {
+ stub = serviceType.getName() + "Local";
+ }
+ }
+ try {
+ Class<?> stubClass = ReflectUtils.forName(stub);
+ if (! serviceType.isAssignableFrom(stubClass)) {
+ throw new IllegalStateException("The stub implemention class " + stubClass.getName() + " not implement interface " + serviceType.getName());
+ }
+ try {
+ Constructor<?> constructor = ReflectUtils.findConstructor(stubClass, serviceType);
+ proxy = (T) constructor.newInstance(new Object[] {proxy});
+ //export stub service
+ URL url = invoker.getUrl();
+ if (url.getParameter(RpcConstants.STUB_EVENT_KEY, RpcConstants.DEFAULT_STUB_EVENT)){
+ url = url.addParameter(RpcConstants.STUB_EVENT_METHODS_KEY, StringUtils.join(Wrapper.getWrapper(proxy.getClass()).getDeclaredMethodNames(), ","));
+ url = url.addParameter(RpcConstants.IS_SERVER_KEY, Boolean.FALSE.toString());
+ try{
+ export(proxy, (Class)invoker.getInterface(), url);
+ }catch (Exception e) {
+ LOGGER.error("export a stub service error.", e);
+ }
+ }
+ } catch (NoSuchMethodException e) {
+ throw new IllegalStateException("No such constructor \"public " + stubClass.getSimpleName() + "(" + serviceType.getName() + ")\" in stub implemention class " + stubClass.getName(), e);
+ }
+ } catch (Throwable t) {
+ LOGGER.error("Failed to create stub implemention class " + stub + " in consumer " + NetUtils.getLocalHost() + " use dubbo version " + Version.getVersion() + ", cause: " + t.getMessage(), t);
+ // ignore
+ }
+ }
+ }
+ return proxy;
+ }
+
+ public <T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) throws RpcException {
+ return proxyFactory.getInvoker(proxy, type, url);
+ }
+
+ private <T> Exporter<T> export(T instance, Class<T> type, URL url) {
+ return protocol.export(proxyFactory.getInvoker(instance, type, url));
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/service/EchoService.java b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/service/EchoService.java
new file mode 100644
index 0000000..c09fcd9
--- /dev/null
+++ b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/service/EchoService.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.service;
+
+/**
+ * Echo service.
+ *
+ * @author qian.lei
+ */
+public interface EchoService {
+
+ /**
+ * echo test.
+ *
+ * @param message message.
+ * @return message.
+ */
+ Object $echo(Object message);
+
+}
\ No newline at end of file
diff --git a/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/service/GenericException.java b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/service/GenericException.java
new file mode 100644
index 0000000..81be78f
--- /dev/null
+++ b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/service/GenericException.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.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(Throwable cause) {
+ super(StringUtils.toString(cause));
+ this.exceptionClass = cause.getClass().getName();
+ this.exceptionMessage = cause.getMessage();
+ }
+
+ public String getExceptionClass() {
+ return exceptionClass;
+ }
+
+ public void setExceptionClass(String exceptionClass) {
+ this.exceptionClass = exceptionClass;
+ }
+
+ public String getExceptionMessage() {
+ return exceptionMessage;
+ }
+
+ public void setExceptionMessage(String exceptionMessage) {
+ this.exceptionMessage = exceptionMessage;
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/service/GenericService.java b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/service/GenericService.java
new file mode 100644
index 0000000..bd621b7
--- /dev/null
+++ b/dubbo-rpc/src/main/java/com/alibaba/dubbo/rpc/service/GenericService.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.service;
+
+/**
+ * 通用服务接口
+ *
+ * @author william.liangf
+ */
+public interface GenericService {
+
+ /**
+ * 泛化调用
+ *
+ * @param method 方法名,如:findPerson,如果有重载方法,需带上参数列表,如:findPerson(java.lang.String)
+ * @param parameterTypes 参数类型
+ * @param args 参数列表
+ * @return 返回值
+ * @throws Throwable 方法抛出的异常
+ */
+ Object $invoke(String method, String[] parameterTypes, Object[] args) throws GenericException;
+
+}
\ No newline at end of file
diff --git a/dubbo-rpc/src/main/resources/META-INF/services/com.alibaba.dubbo.remoting.Codec b/dubbo-rpc/src/main/resources/META-INF/services/com.alibaba.dubbo.remoting.Codec
new file mode 100644
index 0000000..5b10445
--- /dev/null
+++ b/dubbo-rpc/src/main/resources/META-INF/services/com.alibaba.dubbo.remoting.Codec
@@ -0,0 +1 @@
+com.alibaba.dubbo.rpc.protocol.dubbo.DubboCodec
\ No newline at end of file
diff --git a/dubbo-rpc/src/main/resources/META-INF/services/com.alibaba.dubbo.remoting.telnet.TelnetHandler b/dubbo-rpc/src/main/resources/META-INF/services/com.alibaba.dubbo.remoting.telnet.TelnetHandler
new file mode 100644
index 0000000..6b17325
--- /dev/null
+++ b/dubbo-rpc/src/main/resources/META-INF/services/com.alibaba.dubbo.remoting.telnet.TelnetHandler
@@ -0,0 +1,8 @@
+com.alibaba.dubbo.rpc.protocol.dubbo.telnet.ListTelnetHandler
+com.alibaba.dubbo.rpc.protocol.dubbo.telnet.ChangeTelnetHandler
+com.alibaba.dubbo.rpc.protocol.dubbo.telnet.CurrentTelnetHandler
+com.alibaba.dubbo.rpc.protocol.dubbo.telnet.InvokeTelnetHandler
+com.alibaba.dubbo.rpc.protocol.dubbo.telnet.TraceTelnetHandler
+com.alibaba.dubbo.rpc.protocol.dubbo.telnet.CountTelnetHandler
+com.alibaba.dubbo.rpc.protocol.dubbo.telnet.PortTelnetHandler
+com.alibaba.dubbo.rpc.protocol.dubbo.telnet.LogTelnetHandler
\ No newline at end of file
diff --git a/dubbo-rpc/src/main/resources/META-INF/services/com.alibaba.dubbo.rpc.Filter b/dubbo-rpc/src/main/resources/META-INF/services/com.alibaba.dubbo.rpc.Filter
new file mode 100644
index 0000000..a054250
--- /dev/null
+++ b/dubbo-rpc/src/main/resources/META-INF/services/com.alibaba.dubbo.rpc.Filter
@@ -0,0 +1,14 @@
+com.alibaba.dubbo.rpc.filter.EchoFilter
+com.alibaba.dubbo.rpc.filter.GenericFilter
+com.alibaba.dubbo.rpc.filter.GenericImplFilter
+com.alibaba.dubbo.rpc.filter.TokenFilter
+com.alibaba.dubbo.rpc.filter.AccessLogFilter
+com.alibaba.dubbo.rpc.filter.ActiveLimitFilter
+com.alibaba.dubbo.rpc.filter.ClassLoaderFilter
+com.alibaba.dubbo.rpc.filter.ContextFilter
+com.alibaba.dubbo.rpc.filter.ConsumerContextFilter
+com.alibaba.dubbo.rpc.filter.ExceptionFilter
+com.alibaba.dubbo.rpc.filter.ExecuteLimitFilter
+com.alibaba.dubbo.rpc.filter.DeprecatedFilter
+com.alibaba.dubbo.rpc.filter.CompatibleFilter
+com.alibaba.dubbo.rpc.filter.TimeoutFilter
\ No newline at end of file
diff --git a/dubbo-rpc/src/main/resources/META-INF/services/com.alibaba.dubbo.rpc.InvokerListener b/dubbo-rpc/src/main/resources/META-INF/services/com.alibaba.dubbo.rpc.InvokerListener
new file mode 100644
index 0000000..014211c
--- /dev/null
+++ b/dubbo-rpc/src/main/resources/META-INF/services/com.alibaba.dubbo.rpc.InvokerListener
@@ -0,0 +1 @@
+com.alibaba.dubbo.rpc.listener.DeprecatedInvokerListener
\ No newline at end of file
diff --git a/dubbo-rpc/src/main/resources/META-INF/services/com.alibaba.dubbo.rpc.Protocol b/dubbo-rpc/src/main/resources/META-INF/services/com.alibaba.dubbo.rpc.Protocol
new file mode 100644
index 0000000..2793819
--- /dev/null
+++ b/dubbo-rpc/src/main/resources/META-INF/services/com.alibaba.dubbo.rpc.Protocol
@@ -0,0 +1,2 @@
+com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper
+com.alibaba.dubbo.rpc.protocol.ProtocolListenerWrapper
\ No newline at end of file
diff --git a/dubbo-rpc/src/main/resources/META-INF/services/com.alibaba.dubbo.rpc.ProxyFactory b/dubbo-rpc/src/main/resources/META-INF/services/com.alibaba.dubbo.rpc.ProxyFactory
new file mode 100644
index 0000000..041e1c9
--- /dev/null
+++ b/dubbo-rpc/src/main/resources/META-INF/services/com.alibaba.dubbo.rpc.ProxyFactory
@@ -0,0 +1,4 @@
+com.alibaba.dubbo.rpc.proxy.wrapper.MockProxyFactoryWrapper
+com.alibaba.dubbo.rpc.proxy.wrapper.StubProxyFactoryWrapper
+com.alibaba.dubbo.rpc.proxy.jdk.JdkProxyFactory
+com.alibaba.dubbo.rpc.proxy.javassist.JavassistProxyFactory
\ No newline at end of file
diff --git a/dubbo-rpc/src/test/java/com/alibaba/dubbo/rpc/CustomArgument.java b/dubbo-rpc/src/test/java/com/alibaba/dubbo/rpc/CustomArgument.java
new file mode 100644
index 0000000..fc8af58
--- /dev/null
+++ b/dubbo-rpc/src/test/java/com/alibaba/dubbo/rpc/CustomArgument.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc;
+
+import java.io.Serializable;
+
+import com.alibaba.dubbo.rpc.support.Type;
+
+/**
+ * @author chao.liuc
+ *
+ */
+@SuppressWarnings("serial")
+public class CustomArgument implements Serializable{
+
+ public CustomArgument(){}
+
+ public CustomArgument(Type type, String name) {
+ super();
+ this.type = type;
+ this.name = name;
+ }
+ Type type;
+ String name;
+ public Type getType() {
+ return type;
+ }
+ public void setType(Type type) {
+ this.type = type;
+ }
+ public String getName() {
+ return name;
+ }
+ public void setName(String name) {
+ this.name = name;
+ }
+}
\ No newline at end of file
diff --git a/dubbo-rpc/src/test/java/com/alibaba/dubbo/rpc/DemoRequest.java b/dubbo-rpc/src/test/java/com/alibaba/dubbo/rpc/DemoRequest.java
new file mode 100644
index 0000000..665180d
--- /dev/null
+++ b/dubbo-rpc/src/test/java/com/alibaba/dubbo/rpc/DemoRequest.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc;
+
+import java.io.Serializable;
+
+/**
+ * TestRequest.
+ *
+ * @author qian.lei
+ */
+
+class DemoRequest implements Serializable
+{
+ private static final long serialVersionUID = -2579095288792344869L;
+
+ private String mServiceName;
+
+ private String mMethodName;
+
+ private Class<?>[] mParameterTypes;
+
+ private Object[] mArguments;
+
+ public DemoRequest(String serviceName,String methodName, Class<?>[] parameterTypes,Object[] args)
+ {
+ mServiceName = serviceName;
+ mMethodName = methodName;
+ mParameterTypes = parameterTypes;
+ mArguments = args;
+ }
+
+ public String getServiceName()
+ {
+ return mServiceName;
+ }
+
+ public String getMethodName()
+ {
+ return mMethodName;
+ }
+
+ public Class<?>[] getParameterTypes() {
+ return mParameterTypes;
+ }
+
+ public Object[] getArguments()
+ {
+ return mArguments;
+ }
+}
\ No newline at end of file
diff --git a/dubbo-rpc/src/test/java/com/alibaba/dubbo/rpc/ProtocolUtils.java b/dubbo-rpc/src/test/java/com/alibaba/dubbo/rpc/ProtocolUtils.java
new file mode 100644
index 0000000..46e5940
--- /dev/null
+++ b/dubbo-rpc/src/test/java/com/alibaba/dubbo/rpc/ProtocolUtils.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc;
+
+import com.alibaba.dubbo.common.ExtensionLoader;
+import com.alibaba.dubbo.common.URL;
+
+/**
+ * TODO Comment of ProtocolUtils
+ * @author william.liangf
+ *
+ */
+public class ProtocolUtils {
+
+ private static Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
+ private static ProxyFactory proxy = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();
+
+ public static <T> T refer(Class<T> type, String url) {
+ return refer(type, URL.valueOf(url));
+ }
+
+ public static <T> T refer(Class<T> type, URL url) {
+ return proxy.getProxy(protocol.refer(type, url));
+ }
+
+ public static <T> Exporter<T> export(T instance, Class<T> type, String url) {
+ return export(instance, type, URL.valueOf(url));
+ }
+
+ public static <T> Exporter<T> export(T instance, Class<T> type, URL url) {
+ return protocol.export(proxy.getInvoker(instance, type, url));
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-rpc/src/test/java/com/alibaba/dubbo/rpc/filter/AccessLogFilterTest.java b/dubbo-rpc/src/test/java/com/alibaba/dubbo/rpc/filter/AccessLogFilterTest.java
new file mode 100644
index 0000000..06571d7
--- /dev/null
+++ b/dubbo-rpc/src/test/java/com/alibaba/dubbo/rpc/filter/AccessLogFilterTest.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.filter;
+
+import static org.junit.Assert.assertEquals;
+
+import org.junit.Test;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.utils.LogUtil;
+import com.alibaba.dubbo.rpc.Filter;
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.support.MockInvocation;
+import com.alibaba.dubbo.rpc.support.MockInvoker;
+
+/**
+ * AccessLogFilterTest.java
+ *
+ * @author tony.chenl
+ */
+public class AccessLogFilterTest {
+
+ Filter accessLogFilter = new AccessLogFilter();
+
+ // 测试filter不会抛出异常
+ @Test
+ public void testInvokeException() {
+ Invoker<AccessLogFilterTest> invoker = new MockInvoker<AccessLogFilterTest>(null);
+ Invocation invocation = new MockInvocation();
+ LogUtil.start();
+ accessLogFilter.invoke(invoker, invocation);
+ assertEquals(1, LogUtil.findMessage("Exception in AcessLogFilter of service"));
+ LogUtil.stop();
+ }
+
+ // TODO how to assert thread action
+ @Test
+ public void testDefault() {
+ URL url = URL.valueOf("test://test:11/test?accesslog=true&group=dubbo&version=1.1");
+ Invoker<AccessLogFilterTest> invoker = new MockInvoker<AccessLogFilterTest>(url);
+ Invocation invocation = new MockInvocation();
+ accessLogFilter.invoke(invoker, invocation);
+ }
+
+ @Test
+ public void testCustom() {
+ URL url = URL.valueOf("test://test:11/test?accesslog=alibaba");
+ Invoker<AccessLogFilterTest> invoker = new MockInvoker<AccessLogFilterTest>(url);
+ Invocation invocation = new MockInvocation();
+ accessLogFilter.invoke(invoker, invocation);
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-rpc/src/test/java/com/alibaba/dubbo/rpc/filter/ActiveLimitFilterTest.java b/dubbo-rpc/src/test/java/com/alibaba/dubbo/rpc/filter/ActiveLimitFilterTest.java
new file mode 100644
index 0000000..30b2ddf
--- /dev/null
+++ b/dubbo-rpc/src/test/java/com/alibaba/dubbo/rpc/filter/ActiveLimitFilterTest.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.filter;
+
+import static org.junit.Assert.assertNotSame;
+
+import org.junit.Test;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.rpc.Filter;
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.RpcException;
+import com.alibaba.dubbo.rpc.support.MockInvocation;
+import com.alibaba.dubbo.rpc.support.MockInvoker;
+
+/**
+ * ActiveLimitFilterTest.java
+ *
+ * @author tony.chenl
+ */
+public class ActiveLimitFilterTest {
+
+ Filter activeLimitFilter = new ActiveLimitFilter();
+ private static volatile int count = 0;
+
+ @Test
+ public void testInvokeNoActives() {
+ URL url = URL.valueOf("test://test:11/test?accesslog=true&group=dubbo&version=1.1&actives=0");
+ Invoker<ActiveLimitFilterTest> invoker = new MockInvoker<ActiveLimitFilterTest>(url);
+ Invocation invocation = new MockInvocation();
+ activeLimitFilter.invoke(invoker, invocation);
+ }
+
+ @Test
+ public void testInvokeLessActives() {
+ URL url = URL.valueOf("test://test:11/test?accesslog=true&group=dubbo&version=1.1&actives=10");
+ Invoker<ActiveLimitFilterTest> invoker = new MockInvoker<ActiveLimitFilterTest>(url);
+ Invocation invocation = new MockInvocation();
+ activeLimitFilter.invoke(invoker, invocation);
+ }
+
+ @Test
+ public void testInvokeGreaterActives() {
+ URL url = URL.valueOf("test://test:11/test?accesslog=true&group=dubbo&version=1.1&actives=1&timeout=1");
+ final Invoker<ActiveLimitFilterTest> invoker = new MockInvoker<ActiveLimitFilterTest>(url);
+ final Invocation invocation = new MockInvocation();
+ for (int i = 0; i < 100; i++) {
+ Thread thread = new Thread(new Runnable() {
+
+ public void run() {
+ for (int i = 0; i < 100; i++) {
+ try {
+ activeLimitFilter.invoke(invoker, invocation);
+ } catch (RpcException expected) {
+ count++;
+ }
+ }
+ }
+ });
+ thread.start();
+ }
+ try {
+ Thread.sleep(1000);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ assertNotSame(0, count);
+ }
+}
\ No newline at end of file
diff --git a/dubbo-rpc/src/test/java/com/alibaba/dubbo/rpc/filter/CompatibleFilterFilterTest.java b/dubbo-rpc/src/test/java/com/alibaba/dubbo/rpc/filter/CompatibleFilterFilterTest.java
new file mode 100644
index 0000000..c4ec681
--- /dev/null
+++ b/dubbo-rpc/src/test/java/com/alibaba/dubbo/rpc/filter/CompatibleFilterFilterTest.java
@@ -0,0 +1,170 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.filter;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+
+import org.easymock.EasyMock;
+import org.junit.After;
+import org.junit.Test;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.rpc.Filter;
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.Result;
+import com.alibaba.dubbo.rpc.RpcResult;
+import com.alibaba.dubbo.rpc.support.DemoService;
+import com.alibaba.dubbo.rpc.support.Type;
+
+/**
+ * CompatibleFilterTest.java
+ *
+ * @author tony.chenl
+ */
+public class CompatibleFilterFilterTest {
+
+ Filter compatibleFilter = new CompatibleFilter();
+ Invocation invocation;
+ Invoker<DemoService> invoker;
+
+ @After
+ public void tearDown() {
+ EasyMock.reset(invocation, invoker);
+ }
+
+ @Test
+ public void testInvokerGeneric() {
+ invocation = EasyMock.createMock(Invocation.class);
+ EasyMock.expect(invocation.getMethodName()).andReturn("$enumlength").anyTimes();
+ EasyMock.expect(invocation.getParameterTypes()).andReturn(new Class<?>[] { Enum.class }).anyTimes();
+ EasyMock.expect(invocation.getArguments()).andReturn(new Object[] { "hello" }).anyTimes();
+ EasyMock.replay(invocation);
+ invoker = EasyMock.createMock(Invoker.class);
+ EasyMock.expect(invoker.isAvailable()).andReturn(true).anyTimes();
+ EasyMock.expect(invoker.getInterface()).andReturn(DemoService.class).anyTimes();
+ RpcResult result = new RpcResult();
+ result.setResult("High");
+ EasyMock.expect(invoker.invoke(invocation)).andReturn(result).anyTimes();
+ URL url = URL.valueOf("test://test:11/test?group=dubbo&version=1.1");
+ EasyMock.expect(invoker.getUrl()).andReturn(url).anyTimes();
+ EasyMock.replay(invoker);
+ Result filterResult = compatibleFilter.invoke(invoker, invocation);
+ assertEquals(filterResult, result);
+ }
+
+ @Test
+ public void testResulthasException() {
+ invocation = EasyMock.createMock(Invocation.class);
+ EasyMock.expect(invocation.getMethodName()).andReturn("enumlength").anyTimes();
+ EasyMock.expect(invocation.getParameterTypes()).andReturn(new Class<?>[] { Enum.class }).anyTimes();
+ EasyMock.expect(invocation.getArguments()).andReturn(new Object[] { "hello" }).anyTimes();
+ EasyMock.replay(invocation);
+ invoker = EasyMock.createMock(Invoker.class);
+ EasyMock.expect(invoker.isAvailable()).andReturn(true).anyTimes();
+ EasyMock.expect(invoker.getInterface()).andReturn(DemoService.class).anyTimes();
+ RpcResult result = new RpcResult();
+ result.setException(new RuntimeException());
+ result.setResult("High");
+ EasyMock.expect(invoker.invoke(invocation)).andReturn(result).anyTimes();
+ URL url = URL.valueOf("test://test:11/test?group=dubbo&version=1.1");
+ EasyMock.expect(invoker.getUrl()).andReturn(url).anyTimes();
+ EasyMock.replay(invoker);
+ Result filterResult = compatibleFilter.invoke(invoker, invocation);
+ assertEquals(filterResult, result);
+ }
+
+ @Test
+ public void testInvokerJsonPojoSerialization() {
+ invocation = EasyMock.createMock(Invocation.class);
+ EasyMock.expect(invocation.getMethodName()).andReturn("enumlength").anyTimes();
+ EasyMock.expect(invocation.getParameterTypes()).andReturn(new Class<?>[] { Type[].class }).anyTimes();
+ EasyMock.expect(invocation.getArguments()).andReturn(new Object[] { "hello" }).anyTimes();
+ EasyMock.replay(invocation);
+ invoker = EasyMock.createMock(Invoker.class);
+ EasyMock.expect(invoker.isAvailable()).andReturn(true).anyTimes();
+ EasyMock.expect(invoker.getInterface()).andReturn(DemoService.class).anyTimes();
+ RpcResult result = new RpcResult();
+ result.setResult("High");
+ EasyMock.expect(invoker.invoke(invocation)).andReturn(result).anyTimes();
+ URL url = URL.valueOf("test://test:11/test?group=dubbo&version=1.1&serialization=json");
+ EasyMock.expect(invoker.getUrl()).andReturn(url).anyTimes();
+ EasyMock.replay(invoker);
+ Result filterResult = compatibleFilter.invoke(invoker, invocation);
+ assertEquals(Type.High, filterResult.getResult());
+ }
+
+ @Test
+ public void testInvokerNonJsonEnumSerialization() {
+ invocation = EasyMock.createMock(Invocation.class);
+ EasyMock.expect(invocation.getMethodName()).andReturn("enumlength").anyTimes();
+ EasyMock.expect(invocation.getParameterTypes()).andReturn(new Class<?>[] { Type[].class }).anyTimes();
+ EasyMock.expect(invocation.getArguments()).andReturn(new Object[] { "hello" }).anyTimes();
+ EasyMock.replay(invocation);
+ invoker = EasyMock.createMock(Invoker.class);
+ EasyMock.expect(invoker.isAvailable()).andReturn(true).anyTimes();
+ EasyMock.expect(invoker.getInterface()).andReturn(DemoService.class).anyTimes();
+ RpcResult result = new RpcResult();
+ result.setResult("High");
+ EasyMock.expect(invoker.invoke(invocation)).andReturn(result).anyTimes();
+ URL url = URL.valueOf("test://test:11/test?group=dubbo&version=1.1");
+ EasyMock.expect(invoker.getUrl()).andReturn(url).anyTimes();
+ EasyMock.replay(invoker);
+ Result filterResult = compatibleFilter.invoke(invoker, invocation);
+ assertEquals(Type.High, filterResult.getResult());
+ }
+
+ @Test
+ public void testInvokerNonJsonNonPojoSerialization() {
+ invocation = EasyMock.createMock(Invocation.class);
+ EasyMock.expect(invocation.getMethodName()).andReturn("echo").anyTimes();
+ EasyMock.expect(invocation.getParameterTypes()).andReturn(new Class<?>[] {String.class }).anyTimes();
+ EasyMock.expect(invocation.getArguments()).andReturn(new Object[] { "hello" }).anyTimes();
+ EasyMock.replay(invocation);
+ invoker = EasyMock.createMock(Invoker.class);
+ EasyMock.expect(invoker.isAvailable()).andReturn(true).anyTimes();
+ EasyMock.expect(invoker.getInterface()).andReturn(DemoService.class).anyTimes();
+ RpcResult result = new RpcResult();
+ result.setResult(new String[]{"High"});
+ EasyMock.expect(invoker.invoke(invocation)).andReturn(result).anyTimes();
+ URL url = URL.valueOf("test://test:11/test?group=dubbo&version=1.1");
+ EasyMock.expect(invoker.getUrl()).andReturn(url).anyTimes();
+ EasyMock.replay(invoker);
+ Result filterResult = compatibleFilter.invoke(invoker, invocation);
+ assertArrayEquals(new String[]{"High"}, (String[])filterResult.getResult());
+ }
+
+ @Test
+ public void testInvokerNonJsonPojoSerialization() {
+ invocation = EasyMock.createMock(Invocation.class);
+ EasyMock.expect(invocation.getMethodName()).andReturn("echo").anyTimes();
+ EasyMock.expect(invocation.getParameterTypes()).andReturn(new Class<?>[] { String.class }).anyTimes();
+ EasyMock.expect(invocation.getArguments()).andReturn(new Object[] { "hello" }).anyTimes();
+ EasyMock.replay(invocation);
+ invoker = EasyMock.createMock(Invoker.class);
+ EasyMock.expect(invoker.isAvailable()).andReturn(true).anyTimes();
+ EasyMock.expect(invoker.getInterface()).andReturn(DemoService.class).anyTimes();
+ RpcResult result = new RpcResult();
+ result.setResult("hello");
+ EasyMock.expect(invoker.invoke(invocation)).andReturn(result).anyTimes();
+ URL url = URL.valueOf("test://test:11/test?group=dubbo&version=1.1");
+ EasyMock.expect(invoker.getUrl()).andReturn(url).anyTimes();
+ EasyMock.replay(invoker);
+ Result filterResult = compatibleFilter.invoke(invoker, invocation);
+ assertEquals("hello", filterResult.getResult());
+ }
+}
\ No newline at end of file
diff --git a/dubbo-rpc/src/test/java/com/alibaba/dubbo/rpc/filter/ConsumerContextFilterTest.java b/dubbo-rpc/src/test/java/com/alibaba/dubbo/rpc/filter/ConsumerContextFilterTest.java
new file mode 100644
index 0000000..5d861fc
--- /dev/null
+++ b/dubbo-rpc/src/test/java/com/alibaba/dubbo/rpc/filter/ConsumerContextFilterTest.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.filter;
+
+import static org.junit.Assert.assertEquals;
+
+import org.junit.Test;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.utils.NetUtils;
+import com.alibaba.dubbo.rpc.Filter;
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.RpcContext;
+import com.alibaba.dubbo.rpc.support.DemoService;
+import com.alibaba.dubbo.rpc.support.MockInvocation;
+import com.alibaba.dubbo.rpc.support.MockInvoker;
+
+/**
+ * ConsumerContextFilterTest.java
+ * @author tony.chenl
+ */
+public class ConsumerContextFilterTest {
+ Filter consumerContextFilter = new ConsumerContextFilter();
+ @Test
+ public void testSetContext(){
+ URL url = URL.valueOf("test://test:11/test?group=dubbo&version=1.1");
+ Invoker<DemoService> invoker = new MockInvoker<DemoService>(url);
+ Invocation invocation = new MockInvocation();
+ consumerContextFilter.invoke(invoker, invocation);
+ assertEquals(invoker,RpcContext.getContext().getInvoker());
+ assertEquals(invocation,RpcContext.getContext().getInvocation());
+ assertEquals(NetUtils.getLocalHost() + ":0",RpcContext.getContext().getLocalAddressString());
+ assertEquals("test:11",RpcContext.getContext().getRemoteAddressString());
+
+ }
+}
\ No newline at end of file
diff --git a/dubbo-rpc/src/test/java/com/alibaba/dubbo/rpc/filter/ContextFilterTest.java b/dubbo-rpc/src/test/java/com/alibaba/dubbo/rpc/filter/ContextFilterTest.java
new file mode 100644
index 0000000..c10c801
--- /dev/null
+++ b/dubbo-rpc/src/test/java/com/alibaba/dubbo/rpc/filter/ContextFilterTest.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.filter;
+
+import static org.junit.Assert.assertNull;
+
+import org.easymock.EasyMock;
+import org.junit.Test;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.rpc.Filter;
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.Result;
+import com.alibaba.dubbo.rpc.RpcContext;
+import com.alibaba.dubbo.rpc.RpcResult;
+import com.alibaba.dubbo.rpc.support.DemoService;
+import com.alibaba.dubbo.rpc.support.MockInvocation;
+import com.alibaba.dubbo.rpc.support.MockInvoker;
+
+/**
+ * ContextFilterTest.java
+ * TODO 增强断言
+ * @author tony.chenl
+ */
+public class ContextFilterTest {
+
+ Filter contextFilter = new ContextFilter();
+ Invoker<DemoService> invoker;
+ Invocation invocation;
+
+ @SuppressWarnings("unchecked")
+ @Test
+ public void testSetContext() {
+ invocation = EasyMock.createMock(Invocation.class);
+ EasyMock.expect(invocation.getMethodName()).andReturn("$enumlength").anyTimes();
+ EasyMock.expect(invocation.getParameterTypes()).andReturn(new Class<?>[] { Enum.class }).anyTimes();
+ EasyMock.expect(invocation.getArguments()).andReturn(new Object[] { "hello" }).anyTimes();
+ EasyMock.expect(invocation.getAttachments()).andReturn(null).anyTimes();
+ EasyMock.replay(invocation);
+ invoker = EasyMock.createMock(Invoker.class);
+ EasyMock.expect(invoker.isAvailable()).andReturn(true).anyTimes();
+ EasyMock.expect(invoker.getInterface()).andReturn(DemoService.class).anyTimes();
+ RpcResult result = new RpcResult();
+ result.setResult("High");
+ EasyMock.expect(invoker.invoke(invocation)).andReturn(result).anyTimes();
+ URL url = URL.valueOf("test://test:11/test?group=dubbo&version=1.1");
+ EasyMock.expect(invoker.getUrl()).andReturn(url).anyTimes();
+ EasyMock.replay(invoker);
+ contextFilter.invoke(invoker, invocation);
+ assertNull(RpcContext.getContext().getInvoker());
+ }
+
+ @Test
+ public void testWithAttachments() {
+ URL url = URL.valueOf("test://test:11/test?group=dubbo&version=1.1");
+ Invoker<DemoService> invoker = new MockInvoker<DemoService>(url);
+ Invocation invocation = new MockInvocation();
+ Result result = contextFilter.invoke(invoker, invocation);
+ assertNull(RpcContext.getContext().getInvoker());
+ }
+}
\ No newline at end of file
diff --git a/dubbo-rpc/src/test/java/com/alibaba/dubbo/rpc/filter/DeprecatedFilterTest.java b/dubbo-rpc/src/test/java/com/alibaba/dubbo/rpc/filter/DeprecatedFilterTest.java
new file mode 100644
index 0000000..81cba99
--- /dev/null
+++ b/dubbo-rpc/src/test/java/com/alibaba/dubbo/rpc/filter/DeprecatedFilterTest.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.filter;
+
+import static org.junit.Assert.*;
+
+import org.junit.Test;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.utils.LogUtil;
+import com.alibaba.dubbo.rpc.Filter;
+import com.alibaba.dubbo.rpc.support.DemoService;
+import com.alibaba.dubbo.rpc.support.MockInvocation;
+import com.alibaba.dubbo.rpc.support.MockInvoker;
+
+/**
+ * DeprecatedFilterTest.java
+ *
+ * @author tony.chenl
+ */
+public class DeprecatedFilterTest {
+
+ Filter deprecatedFilter = new DeprecatedFilter();
+
+ @Test
+ public void testDeprecatedFilter() {
+ URL url = URL.valueOf("test://test:11/test?group=dubbo&version=1.1&echo." + Constants.DEPRECATED_KEY + "=true");
+ LogUtil.start();
+ deprecatedFilter.invoke(new MockInvoker<DemoService>(url), new MockInvocation());
+ assertEquals(1,
+ LogUtil.findMessage("The service method com.alibaba.dubbo.rpc.support.DemoService.echo(String) is DEPRECATED"));
+ LogUtil.stop();
+ }
+}
\ No newline at end of file
diff --git a/dubbo-rpc/src/test/java/com/alibaba/dubbo/rpc/filter/EchoFilterTest.java b/dubbo-rpc/src/test/java/com/alibaba/dubbo/rpc/filter/EchoFilterTest.java
new file mode 100644
index 0000000..00351d1
--- /dev/null
+++ b/dubbo-rpc/src/test/java/com/alibaba/dubbo/rpc/filter/EchoFilterTest.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.filter;
+
+import static org.junit.Assert.assertEquals;
+
+import org.easymock.EasyMock;
+import org.junit.Test;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.rpc.Filter;
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.Result;
+import com.alibaba.dubbo.rpc.RpcResult;
+import com.alibaba.dubbo.rpc.support.DemoService;
+
+/**
+ * EchoFilterTest.java
+ *
+ * @author tony.chenl
+ */
+public class EchoFilterTest {
+
+ Filter echoFilter = new EchoFilter();
+
+ @SuppressWarnings("unchecked")
+ @Test
+ public void testEcho() {
+ Invocation invocation = EasyMock.createMock(Invocation.class);
+ EasyMock.expect(invocation.getMethodName()).andReturn("$echo").anyTimes();
+ EasyMock.expect(invocation.getParameterTypes()).andReturn(new Class<?>[] { Enum.class }).anyTimes();
+ EasyMock.expect(invocation.getArguments()).andReturn(new Object[] { "hello" }).anyTimes();
+ EasyMock.expect(invocation.getAttachments()).andReturn(null).anyTimes();
+ EasyMock.replay(invocation);
+ Invoker<DemoService> invoker = EasyMock.createMock(Invoker.class);
+ EasyMock.expect(invoker.isAvailable()).andReturn(true).anyTimes();
+ EasyMock.expect(invoker.getInterface()).andReturn(DemoService.class).anyTimes();
+ RpcResult result = new RpcResult();
+ result.setResult("High");
+ EasyMock.expect(invoker.invoke(invocation)).andReturn(result).anyTimes();
+ URL url = URL.valueOf("test://test:11/test?group=dubbo&version=1.1");
+ EasyMock.expect(invoker.getUrl()).andReturn(url).anyTimes();
+ EasyMock.replay(invoker);
+ Result filterResult = echoFilter.invoke(invoker, invocation);
+ assertEquals("hello", filterResult.getResult());
+ }
+
+ @SuppressWarnings("unchecked")
+ @Test
+ public void testNonEcho() {
+ Invocation invocation = EasyMock.createMock(Invocation.class);
+ EasyMock.expect(invocation.getMethodName()).andReturn("echo").anyTimes();
+ EasyMock.expect(invocation.getParameterTypes()).andReturn(new Class<?>[] { Enum.class }).anyTimes();
+ EasyMock.expect(invocation.getArguments()).andReturn(new Object[] { "hello" }).anyTimes();
+ EasyMock.expect(invocation.getAttachments()).andReturn(null).anyTimes();
+ EasyMock.replay(invocation);
+ Invoker<DemoService> invoker = EasyMock.createMock(Invoker.class);
+ EasyMock.expect(invoker.isAvailable()).andReturn(true).anyTimes();
+ EasyMock.expect(invoker.getInterface()).andReturn(DemoService.class).anyTimes();
+ RpcResult result = new RpcResult();
+ result.setResult("High");
+ EasyMock.expect(invoker.invoke(invocation)).andReturn(result).anyTimes();
+ URL url = URL.valueOf("test://test:11/test?group=dubbo&version=1.1");
+ EasyMock.expect(invoker.getUrl()).andReturn(url).anyTimes();
+ EasyMock.replay(invoker);
+ Result filterResult = echoFilter.invoke(invoker, invocation);
+ assertEquals("High", filterResult.getResult());
+ }
+}
\ No newline at end of file
diff --git a/dubbo-rpc/src/test/java/com/alibaba/dubbo/rpc/proxy/DemoRequest.java b/dubbo-rpc/src/test/java/com/alibaba/dubbo/rpc/proxy/DemoRequest.java
new file mode 100644
index 0000000..9a2da25
--- /dev/null
+++ b/dubbo-rpc/src/test/java/com/alibaba/dubbo/rpc/proxy/DemoRequest.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.proxy;
+
+import java.io.Serializable;
+
+/**
+ * TestRequest.
+ *
+ * @author qian.lei
+ */
+
+class DemoRequest implements Serializable
+{
+ private static final long serialVersionUID = -2579095288792344869L;
+
+ private String mServiceName;
+
+ private String mMethodName;
+
+ private Class<?>[] mParameterTypes;
+
+ private Object[] mArguments;
+
+ public DemoRequest(String serviceName,String methodName, Class<?>[] parameterTypes,Object[] args)
+ {
+ mServiceName = serviceName;
+ mMethodName = methodName;
+ mParameterTypes = parameterTypes;
+ mArguments = args;
+ }
+
+ public String getServiceName()
+ {
+ return mServiceName;
+ }
+
+ public String getMethodName()
+ {
+ return mMethodName;
+ }
+
+ public Class<?>[] getParameterTypes() {
+ return mParameterTypes;
+ }
+
+ public Object[] getArguments()
+ {
+ return mArguments;
+ }
+}
\ No newline at end of file
diff --git a/dubbo-rpc/src/test/java/com/alibaba/dubbo/rpc/proxy/DemoService.java b/dubbo-rpc/src/test/java/com/alibaba/dubbo/rpc/proxy/DemoService.java
new file mode 100644
index 0000000..917262a
--- /dev/null
+++ b/dubbo-rpc/src/test/java/com/alibaba/dubbo/rpc/proxy/DemoService.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.proxy;
+
+/**
+ * <code>TestService</code>
+ */
+
+public interface DemoService
+{
+ void sayHello(String name);
+
+ String echo(String text);
+
+ long timestamp();
+
+ String getThreadName();
+
+ int getSize(String[] strs);
+
+ int getSize(Object[] os);
+
+ Object invoke(String service, String method) throws Exception;
+
+ int stringLength(String str);
+
+ Type enumlength(Type... types);
+}
\ No newline at end of file
diff --git a/dubbo-rpc/src/test/java/com/alibaba/dubbo/rpc/proxy/DemoServiceImpl.java b/dubbo-rpc/src/test/java/com/alibaba/dubbo/rpc/proxy/DemoServiceImpl.java
new file mode 100644
index 0000000..de02c54
--- /dev/null
+++ b/dubbo-rpc/src/test/java/com/alibaba/dubbo/rpc/proxy/DemoServiceImpl.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.proxy;
+
+import com.alibaba.dubbo.rpc.RpcContext;
+
+/**
+ * DemoServiceImpl
+ */
+
+public class DemoServiceImpl implements DemoService
+{
+ public DemoServiceImpl()
+ {
+ super();
+ }
+
+ public void sayHello(String name) {
+ System.out.println("hello "+name);
+ }
+
+ public String echo(String text)
+ {
+ return text;
+ }
+
+ public long timestamp() {
+ return System.currentTimeMillis();
+ }
+
+ public String getThreadName()
+ {
+ return Thread.currentThread().getName();
+ }
+
+ public int getSize(String[] strs)
+ {
+ if( strs == null )
+ return -1;
+ return strs.length;
+ }
+
+ public int getSize(Object[] os)
+ {
+ if( os == null )
+ return -1;
+ return os.length;
+ }
+
+ public Object invoke(String service, String method) throws Exception
+ {
+ System.out.println("RpcContext.getContext().getRemoteHost()="+RpcContext.getContext().getRemoteHost());
+ return service + ":" + method;
+ }
+
+ public Type enumlength(Type... types)
+ {
+ if( types.length == 0 )
+ return Type.Lower;
+ return types[0];
+ }
+
+ public int stringLength(String str)
+ {
+ return str.length();
+ }
+}
\ No newline at end of file
diff --git a/dubbo-rpc/src/test/java/com/alibaba/dubbo/rpc/proxy/RemoteService.java b/dubbo-rpc/src/test/java/com/alibaba/dubbo/rpc/proxy/RemoteService.java
new file mode 100644
index 0000000..194b0c7
--- /dev/null
+++ b/dubbo-rpc/src/test/java/com/alibaba/dubbo/rpc/proxy/RemoteService.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.proxy;
+
+import java.rmi.Remote;
+import java.rmi.RemoteException;
+
+public interface RemoteService extends Remote
+{
+ String sayHello(String name) throws RemoteException;
+
+ String getThreadName() throws RemoteException;
+}
\ No newline at end of file
diff --git a/dubbo-rpc/src/test/java/com/alibaba/dubbo/rpc/proxy/RemoteServiceImpl.java b/dubbo-rpc/src/test/java/com/alibaba/dubbo/rpc/proxy/RemoteServiceImpl.java
new file mode 100644
index 0000000..5426978
--- /dev/null
+++ b/dubbo-rpc/src/test/java/com/alibaba/dubbo/rpc/proxy/RemoteServiceImpl.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.proxy;
+
+import java.rmi.RemoteException;
+
+import com.alibaba.dubbo.rpc.RpcContext;
+
+public class RemoteServiceImpl implements RemoteService
+{
+ public String getThreadName() throws RemoteException
+ {
+ System.out.println("RpcContext.getContext().getRemoteHost()="+RpcContext.getContext().getRemoteHost());
+ return Thread.currentThread().getName();
+ }
+
+ public String sayHello(String name) throws RemoteException
+ {
+ return "hello " + name + "@" + RemoteServiceImpl.class.getName();
+ }
+}
\ No newline at end of file
diff --git a/dubbo-rpc/src/test/java/com/alibaba/dubbo/rpc/proxy/Type.java b/dubbo-rpc/src/test/java/com/alibaba/dubbo/rpc/proxy/Type.java
new file mode 100644
index 0000000..ead26d1
--- /dev/null
+++ b/dubbo-rpc/src/test/java/com/alibaba/dubbo/rpc/proxy/Type.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.proxy;
+
+public enum Type
+{
+ High, Normal, Lower
+}
\ No newline at end of file
diff --git a/dubbo-rpc/src/test/java/com/alibaba/dubbo/rpc/support/DemoService.java b/dubbo-rpc/src/test/java/com/alibaba/dubbo/rpc/support/DemoService.java
new file mode 100644
index 0000000..f191ae8
--- /dev/null
+++ b/dubbo-rpc/src/test/java/com/alibaba/dubbo/rpc/support/DemoService.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.support;
+
+import com.alibaba.dubbo.rpc.CustomArgument;
+
+
+/**
+ * <code>TestService</code>
+ */
+
+public interface DemoService
+{
+ void sayHello(String name);
+
+ String echo(String text);
+
+ long timestamp();
+
+ String getThreadName();
+
+ int getSize(String[] strs);
+
+ int getSize(Object[] os);
+
+ Object invoke(String service, String method) throws Exception;
+
+ int stringLength(String str);
+
+ Type enumlength(Type... types);
+
+// Type enumlength(Type type);
+
+ String get(CustomArgument arg1);
+
+ byte getbyte(byte arg);
+
+}
\ No newline at end of file
diff --git a/dubbo-rpc/src/test/java/com/alibaba/dubbo/rpc/support/DemoServiceImpl.java b/dubbo-rpc/src/test/java/com/alibaba/dubbo/rpc/support/DemoServiceImpl.java
new file mode 100644
index 0000000..8b602ee
--- /dev/null
+++ b/dubbo-rpc/src/test/java/com/alibaba/dubbo/rpc/support/DemoServiceImpl.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.support;
+
+import com.alibaba.dubbo.rpc.CustomArgument;
+import com.alibaba.dubbo.rpc.RpcContext;
+
+/**
+ * DemoServiceImpl
+ */
+
+public class DemoServiceImpl implements DemoService
+{
+ public DemoServiceImpl()
+ {
+ super();
+ }
+
+ public void sayHello(String name) {
+ System.out.println("hello "+name);
+ }
+
+ public String echo(String text)
+ {
+ return text;
+ }
+
+ public long timestamp() {
+ return System.currentTimeMillis();
+ }
+
+ public String getThreadName()
+ {
+ return Thread.currentThread().getName();
+ }
+
+ public int getSize(String[] strs)
+ {
+ if( strs == null )
+ return -1;
+ return strs.length;
+ }
+
+ public int getSize(Object[] os)
+ {
+ if( os == null )
+ return -1;
+ return os.length;
+ }
+
+ public Object invoke(String service, String method) throws Exception
+ {
+ System.out.println("RpcContext.getContext().getRemoteHost()="+RpcContext.getContext().getRemoteHost());
+ return service + ":" + method;
+ }
+
+ public Type enumlength(Type... types)
+ {
+ if( types.length == 0 )
+ return Type.Lower;
+ return types[0];
+ }
+
+ public Type enumlength(Type type)
+ {
+ return type;
+ }
+
+ public int stringLength(String str)
+ {
+ return str.length();
+ }
+ public String get(CustomArgument arg1){
+ return arg1.toString();
+ }
+
+ public byte getbyte(byte arg) {
+ return arg;
+ }
+
+ public Person gerPerson(Person person) {
+ return person;
+ }
+}
\ No newline at end of file
diff --git a/dubbo-rpc/src/test/java/com/alibaba/dubbo/rpc/support/IEcho.java b/dubbo-rpc/src/test/java/com/alibaba/dubbo/rpc/support/IEcho.java
new file mode 100644
index 0000000..4a63a5c
--- /dev/null
+++ b/dubbo-rpc/src/test/java/com/alibaba/dubbo/rpc/support/IEcho.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.support;
+
+public interface IEcho {
+ String echo(String e);
+}
\ No newline at end of file
diff --git a/dubbo-rpc/src/test/java/com/alibaba/dubbo/rpc/support/MockInvocation.java b/dubbo-rpc/src/test/java/com/alibaba/dubbo/rpc/support/MockInvocation.java
new file mode 100644
index 0000000..0cf791d
--- /dev/null
+++ b/dubbo-rpc/src/test/java/com/alibaba/dubbo/rpc/support/MockInvocation.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.support;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.rpc.Invocation;
+
+/**
+ * MockInvocation.java
+ *
+ * @author tony.chenl
+ */
+public class MockInvocation implements Invocation {
+
+ public String getMethodName() {
+ return "echo";
+ }
+
+ public Class<?>[] getParameterTypes() {
+ return new Class[] { String.class };
+ }
+
+ public Object[] getArguments() {
+ return new Object[] { "aa" };
+ }
+
+ public Map<String, String> getAttachments() {
+ Map<String, String> attachments = new HashMap<String, String>();
+ attachments.put(Constants.PATH_KEY, "dubbo");
+ attachments.put(Constants.GROUP_KEY, "dubbo");
+ attachments.put(Constants.VERSION_KEY, "1.0.0");
+ attachments.put(Constants.DUBBO_VERSION_KEY, "1.0.0");
+ attachments.put(Constants.TOKEN_KEY, "sfag");
+ attachments.put(Constants.TIMEOUT_KEY, "1000");
+ return attachments;
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-rpc/src/test/java/com/alibaba/dubbo/rpc/support/MockInvoker.java b/dubbo-rpc/src/test/java/com/alibaba/dubbo/rpc/support/MockInvoker.java
new file mode 100644
index 0000000..21c3171
--- /dev/null
+++ b/dubbo-rpc/src/test/java/com/alibaba/dubbo/rpc/support/MockInvoker.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.support;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.Result;
+import com.alibaba.dubbo.rpc.RpcException;
+import com.alibaba.dubbo.rpc.RpcResult;
+
+/**
+ * MockInvoker.java
+ *
+ * @author tony.chenl
+ */
+public class MockInvoker<T> implements Invoker<T> {
+
+ URL url;
+ Class<T> type;
+ boolean hasException = false;
+
+ public MockInvoker(URL url){
+ this.url = url;
+ type = (Class<T>) DemoService.class;
+ }
+
+ public MockInvoker(URL url, boolean hasException){
+ this.url = url;
+ type = (Class<T>) DemoService.class;
+ this.hasException = hasException;
+ }
+
+ public Class<T> getInterface() {
+ return type;
+ }
+
+ public URL getUrl() {
+ return url;
+ }
+
+ public boolean isAvailable() {
+ return false;
+ }
+
+ public Result invoke(Invocation invocation) throws RpcException {
+ RpcResult result = new RpcResult();
+ if (hasException == false) {
+ result.setResult("alibaba");
+ return result;
+ } else {
+ result.setException(new RuntimeException("mocked exception"));
+ return result;
+ }
+
+ }
+
+ public void destroy() {
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-rpc/src/test/java/com/alibaba/dubbo/rpc/support/Person.java b/dubbo-rpc/src/test/java/com/alibaba/dubbo/rpc/support/Person.java
new file mode 100644
index 0000000..19db863
--- /dev/null
+++ b/dubbo-rpc/src/test/java/com/alibaba/dubbo/rpc/support/Person.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.support;
+
+import java.io.Serializable;
+
+/**
+ * Person.java
+ *
+ * @author tony.chenl
+ */
+public class Person implements Serializable {
+
+ private static final long serialVersionUID = 1L;
+ private String name;
+ private int age;
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public int getAge() {
+ return age;
+ }
+
+ public void setAge(int age) {
+ this.age = age;
+ }
+}
\ No newline at end of file
diff --git a/dubbo-rpc/src/test/java/com/alibaba/dubbo/rpc/support/Type.java b/dubbo-rpc/src/test/java/com/alibaba/dubbo/rpc/support/Type.java
new file mode 100644
index 0000000..ed1dd9a
--- /dev/null
+++ b/dubbo-rpc/src/test/java/com/alibaba/dubbo/rpc/support/Type.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.support;
+
+public enum Type
+{
+ High, Normal, Lower
+}
\ No newline at end of file
diff --git a/dubbo-rpc/src/test/resources/log4j.xml b/dubbo-rpc/src/test/resources/log4j.xml
new file mode 100644
index 0000000..e6c81db
--- /dev/null
+++ b/dubbo-rpc/src/test/resources/log4j.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ - Copyright 1999-2011 Alibaba Group.
+ -
+ - Licensed under the Apache License, Version 2.0 (the "License");
+ - you may not use this file except in compliance with the License.
+ - You may obtain a copy of the License at
+ -
+ - http://www.apache.org/licenses/LICENSE-2.0
+ -
+ - Unless required by applicable law or agreed to in writing, software
+ - distributed under the License is distributed on an "AS IS" BASIS,
+ - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ - See the License for the specific language governing permissions and
+ - limitations under the License.
+-->
+<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
+<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
+ <!-- ===================================================================== -->
+ <!-- 以下是appender的定义 -->
+ <!-- ===================================================================== -->
+ <appender name="dubbo" class="com.alibaba.dubbo.common.utils.DubboAppender">
+ <param name="encoding" value="GBK" />
+ <layout class="org.apache.log4j.PatternLayout">
+ <param name="ConversionPattern" value="%d %p [%c:%M] - %m%n" />
+ </layout>
+ <!-- <filter class="org.apache.log4j.varia.LevelRangeFilter">
+ <param name="LevelMin" value="DEBUG" />
+ <param name="LevelMax" value="DEBUG" />
+ </filter> -->
+ </appender>
+ <root>
+ <level value="INFO" />
+ <appender-ref ref="dubbo" />
+ </root>
+</log4j:configuration>
\ No newline at end of file
diff --git a/dubbo/pom.xml b/dubbo/pom.xml
new file mode 100644
index 0000000..a4656e4
--- /dev/null
+++ b/dubbo/pom.xml
@@ -0,0 +1,197 @@
+<!--
+ - Copyright 1999-2011 Alibaba Group.
+ -
+ - Licensed under the Apache License, Version 2.0 (the "License");
+ - you may not use this file except in compliance with the License.
+ - You may obtain a copy of the License at
+ -
+ - http://www.apache.org/licenses/LICENSE-2.0
+ -
+ - Unless required by applicable law or agreed to in writing, software
+ - distributed under the License is distributed on an "AS IS" BASIS,
+ - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ - See the License for the specific language governing permissions and
+ - limitations under the License.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>com.alibaba</groupId>
+ <artifactId>dubbo-parent</artifactId>
+ <version>2.0.8</version>
+ </parent>
+ <artifactId>dubbo</artifactId>
+ <packaging>jar</packaging>
+ <name>Dubbo All In One</name>
+ <description>The all in one project of dubbo</description>
+ <properties>
+ <skip_maven_deploy>false</skip_maven_deploy>
+ </properties>
+ <dependencies>
+ <dependency>
+ <groupId>com.alibaba</groupId>
+ <artifactId>dubbo-container</artifactId>
+ <version>${project.parent.version}</version>
+ <exclusions>
+ <exclusion>
+ <groupId>org.mortbay.jetty</groupId>
+ <artifactId>jetty</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+ <dependency>
+ <groupId>com.alibaba</groupId>
+ <artifactId>dubbo-remoting-netty</artifactId>
+ <version>${project.parent.version}</version>
+ </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>
+ </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-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-simple</artifactId>
+ <version>${project.parent.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>com.alibaba</groupId>
+ <artifactId>dubbo-registry-multicast</artifactId>
+ <version>${project.parent.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>com.alibaba</groupId>
+ <artifactId>dubbo-registry-zookeeper</artifactId>
+ <version>${project.parent.version}</version>
+ <exclusions>
+ <exclusion>
+ <groupId>org.apache.zookeeper</groupId>
+ <artifactId>zookeeper</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+ <dependency>
+ <groupId>com.alibaba</groupId>
+ <artifactId>dubbo-monitor-simple</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>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-shade-plugin</artifactId>
+ <version>1.4</version>
+ <executions>
+ <execution>
+ <phase>package</phase>
+ <goals>
+ <goal>shade</goal>
+ </goals>
+ <configuration>
+ <createSourcesJar>true</createSourcesJar>
+ <promoteTransitiveDependencies>true</promoteTransitiveDependencies>
+ <artifactSet>
+ <includes>
+ <include>com.alibaba:hessian-lite</include>
+ <include>com.alibaba:dubbo-common</include>
+ <include>com.alibaba:dubbo-remoting</include>
+ <include>com.alibaba:dubbo-remoting-netty</include>
+ <include>com.alibaba:dubbo-remoting-mina</include>
+ <include>com.alibaba:dubbo-remoting-grizzly</include>
+ <include>com.alibaba:dubbo-remoting-http</include>
+ <include>com.alibaba:dubbo-rpc</include>
+ <include>com.alibaba:dubbo-rpc-default</include>
+ <include>com.alibaba:dubbo-rpc-injvm</include>
+ <include>com.alibaba:dubbo-rpc-rmi</include>
+ <include>com.alibaba:dubbo-rpc-hessian</include>
+ <include>com.alibaba:dubbo-cluster</include>
+ <include>com.alibaba:dubbo-registry</include>
+ <include>com.alibaba:dubbo-registry-simple</include>
+ <include>com.alibaba:dubbo-registry-multicast</include>
+ <include>com.alibaba:dubbo-registry-zookeeper</include>
+ <include>com.alibaba:dubbo-monitor</include>
+ <include>com.alibaba:dubbo-monitor-simple</include>
+ <include>com.alibaba:dubbo-config</include>
+ <include>com.alibaba:dubbo-container</include>
+ </includes>
+ </artifactSet>
+ <transformers>
+ <transformer
+ implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer" />
+ </transformers>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+</project>
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..d5bf103
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,393 @@
+<!--
+ - Copyright 1999-2011 Alibaba Group.
+ -
+ - Licensed under the Apache License, Version 2.0 (the "License");
+ - you may not use this file except in compliance with the License.
+ - You may obtain a copy of the License at
+ -
+ - http://www.apache.org/licenses/LICENSE-2.0
+ -
+ - Unless required by applicable law or agreed to in writing, software
+ - distributed under the License is distributed on an "AS IS" BASIS,
+ - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ - See the License for the specific language governing permissions and
+ - limitations under the License.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>com.alibaba</groupId>
+ <artifactId>opensesame</artifactId>
+ <version>1.0</version>
+ </parent>
+ <artifactId>dubbo-parent</artifactId>
+ <version>2.0.8</version>
+ <packaging>pom</packaging>
+ <name>Dubbo Parent POM</name>
+ <description>The top project of dubbo</description>
+ <modules>
+ <module>dubbo-common</module>
+ <module>dubbo-remoting</module>
+ <module>dubbo-remoting-netty</module>
+ <module>dubbo-remoting-mina</module>
+ <module>dubbo-remoting-grizzly</module>
+ <module>dubbo-remoting-http</module>
+ <module>dubbo-rpc</module>
+ <module>dubbo-rpc-default</module>
+ <module>dubbo-rpc-injvm</module>
+ <module>dubbo-rpc-rmi</module>
+ <module>dubbo-rpc-hessian</module>
+ <module>dubbo-cluster</module>
+ <module>dubbo-registry</module>
+ <module>dubbo-registry-simple</module>
+ <module>dubbo-registry-multicast</module>
+ <module>dubbo-registry-zookeeper</module>
+ <module>dubbo-monitor</module>
+ <module>dubbo-monitor-simple</module>
+ <module>dubbo-config</module>
+ <module>dubbo-container</module>
+ <module>dubbo</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</hessian_lite_version>
+ <xstream_version>1.4.1</xstream_version>
+ <fastjson_version>1.1.8</fastjson_version>
+ <bsf_version>3.1</bsf_version>
+ <zookeeper_version>3.3.3</zookeeper_version>
+ <jfreechart_version>1.0.13</jfreechart_version>
+ <hessian_version>4.0.7</hessian_version>
+ <servlet_version>2.5</servlet_version>
+ <jetty_version>6.1.26</jetty_version>
+ <!-- Log libs -->
+ <log4j_version>1.2.16</log4j_version>
+ <slf4j_version>1.6.2</slf4j_version>
+ <!-- Test libs -->
+ <junit_version>4.10</junit_version>
+ <easymock_version>3.0</easymock_version>
+ <jmockit_version>0.999.8</jmockit_version>
+ <!-- Build args -->
+ <argline>-Xms512m -Xmx512m</argline>
+ <skip_maven_deploy>false</skip_maven_deploy>
+ <project.build.sourceEncoding>${file_encoding}</project.build.sourceEncoding>
+ <!-- SCM urls -->
+ <project_url>http://code.alibabatech.com/wiki/display/dubbo/Home</project_url>
+ <project_scm_url>http://code.alibabatech.com/svn/dubbo/trunk</project_scm_url>
+ <project_scm_connection>scm:svn:http://code.alibabatech.com/svn/dubbo/trunk</project_scm_connection>
+ <project_issue_url>http://code.alibabatech.com/jira/browse/DUBBO</project_issue_url>
+ <project_site_url>http://code.alibabatech.com/wiki/display/dubbo/Home</project_site_url>
+ </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>jfree</groupId>
+ <artifactId>jfreechart</artifactId>
+ <version>${jfreechart_version}</version>
+ </dependency>
+ <dependency>
+ <groupId>com.caucho</groupId>
+ <artifactId>hessian</artifactId>
+ <version>${hessian_version}</version>
+ </dependency>
+ <dependency>
+ <groupId>javax.servlet</groupId>
+ <artifactId>servlet-api</artifactId>
+ <version>${servlet_version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.mortbay.jetty</groupId>
+ <artifactId>jetty</artifactId>
+ <version>${jetty_version}</version>
+ </dependency>
+ <!-- Log libs -->
+ <dependency>
+ <groupId>log4j</groupId>
+ <artifactId>log4j</artifactId>
+ <version>${log4j_version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ <version>${slf4j_version}</version>
+ </dependency>
+ <!-- Test libs -->
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <version>${junit_version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.easymock</groupId>
+ <artifactId>easymock</artifactId>
+ <version>${easymock_version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>com.googlecode.jmockit</groupId>
+ <artifactId>jmockit</artifactId>
+ <version>${jmockit_version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.easymock</groupId>
+ <artifactId>easymockclassextension</artifactId>
+ <version>${easymock_version}</version>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+ </dependencyManagement>
+ <dependencies>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.easymock</groupId>
+ <artifactId>easymock</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.easymock</groupId>
+ <artifactId>easymockclassextension</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>com.googlecode.jmockit</groupId>
+ <artifactId>jmockit</artifactId>
+ </dependency>
+ </dependencies>
+ <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>
+ <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>
+ <manifestEntries>
+ <Implementation-Build>${timestamp}</Implementation-Build>
+ </manifestEntries>
+ </archive>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <configuration>
+ <useSystemClassLoader>true</useSystemClassLoader>
+ <testFailureIgnore>true</testFailureIgnore>
+ <forkMode>pertest</forkMode>
+ <argLine>${argline}</argLine>
+ <systemProperties>
+ <!-- common shared -->
+ <property>
+ <name>transporter</name>
+ <value>${transporter}</value>
+ </property>
+ <property>
+ <name>serialization</name>
+ <value>${serialization}</value>
+ </property>
+ <!-- server side -->
+ <property>
+ <name>port</name>
+ <value>${port}</value>
+ </property>
+ <property>
+ <name>threadpool</name>
+ <value>${threadpool}</value>
+ </property>
+ <property>
+ <name>threads</name>
+ <value>${threads}</value>
+ </property>
+ <property>
+ <name>iothreads</name>
+ <value>${iothreads}</value>
+ </property>
+ <!-- client side -->
+ <property>
+ <name>server</name>
+ <value>${server}</value>
+ </property>
+ <property>
+ <name>timeout</name>
+ <value>${timeout}</value>
+ </property>
+ <property>
+ <name>length</name>
+ <value>${length}</value>
+ </property>
+ <property>
+ <name>connections</name>
+ <value>${connections}</value>
+ </property>
+ <property>
+ <name>base</name>
+ <value>${base}</value>
+ </property>
+ <property>
+ <name>concurrent</name>
+ <value>${concurrent}</value>
+ </property>
+ <property>
+ <name>runs</name>
+ <value>${runs}</value>
+ </property>
+ <property>
+ <name>onerror</name>
+ <value>${onerror}</value>
+ </property>
+ </systemProperties>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-deploy-plugin</artifactId>
+ <configuration>
+ <skip>${skip_maven_deploy}</skip>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+ <scm>
+ <connection>${project_scm_connection}</connection>
+ <developerConnection>${project_scm_connection}</developerConnection>
+ <url>${project_scm_url}</url>
+ </scm>
+ <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>
+ </developers>
+</project>
\ No newline at end of file