diff --git a/motan-core/pom.xml b/motan-core/pom.xml index 9adf21c71..f472ddd05 100644 --- a/motan-core/pom.xml +++ b/motan-core/pom.xml @@ -27,7 +27,6 @@ motan-core - commons-pool commons-pool @@ -48,5 +47,25 @@ com.google.guava guava + + com.squareup + javapoet + 1.8.0 + + + + + org.apache.maven.plugins + maven-compiler-plugin + 2.3.2 + + ${java.source.jdk} + ${java.source.jdk} + ${project.build.sourceEncoding} + -proc:none + + + + diff --git a/motan-core/src/main/java/com/weibo/api/motan/common/MotanConstants.java b/motan-core/src/main/java/com/weibo/api/motan/common/MotanConstants.java index f3b2d9f6b..9fa71052f 100644 --- a/motan-core/src/main/java/com/weibo/api/motan/common/MotanConstants.java +++ b/motan-core/src/main/java/com/weibo/api/motan/common/MotanConstants.java @@ -67,6 +67,8 @@ public class MotanConstants { public static final String DEFAULT_CHARACTER = "utf-8"; public static final int SLOW_COST = 50; // 50ms public static final int STATISTIC_PEROID = 30; // 30 seconds + public static final String ASYNC_SUFFIX = "Async";// suffix for async call. + /** * netty channel constants start **/ diff --git a/motan-core/src/main/java/com/weibo/api/motan/proxy/RefererInvocationHandler.java b/motan-core/src/main/java/com/weibo/api/motan/proxy/RefererInvocationHandler.java index 29ada9950..49b37ad92 100644 --- a/motan-core/src/main/java/com/weibo/api/motan/proxy/RefererInvocationHandler.java +++ b/motan-core/src/main/java/com/weibo/api/motan/proxy/RefererInvocationHandler.java @@ -33,6 +33,8 @@ import com.weibo.api.motan.rpc.DefaultRequest; import com.weibo.api.motan.rpc.Referer; import com.weibo.api.motan.rpc.Response; +import com.weibo.api.motan.rpc.ResponseFuture; +import com.weibo.api.motan.rpc.RpcContext; import com.weibo.api.motan.switcher.Switcher; import com.weibo.api.motan.switcher.SwitcherService; import com.weibo.api.motan.util.ExceptionUtil; @@ -76,19 +78,25 @@ private void init() { } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { - if(isLocalMethod(method)){ - if("toString".equals(method.getName())){ + if (isLocalMethod(method)) { + if ("toString".equals(method.getName())) { return clustersToString(); } throw new MotanServiceException("can not invoke local method:" + method.getName()); } DefaultRequest request = new DefaultRequest(); - request.setRequestId(RequestIdGenerator.getRequestId()); request.setArguments(args); - request.setMethodName(method.getName()); + String methodName = method.getName(); + boolean async = false; + if (methodName.endsWith(MotanConstants.ASYNC_SUFFIX) && method.getReturnType().equals(ResponseFuture.class)) { + methodName = MotanFrameworkUtil.removeAsyncSuffix(methodName); + async = true; + } + RpcContext.getContext().putAttribute(MotanConstants.ASYNC_SUFFIX, async); + request.setMethodName(methodName); request.setParamtersDesc(ReflectUtil.getMethodParamDesc(method)); - request.setInterfaceName(clz.getName()); + request.setInterfaceName(MotanFrameworkUtil.removeAsyncSuffix(clz.getName())); request.setAttachment(URLParamType.requestIdFromClient.getName(), String.valueOf(RequestIdGenerator.getRequestIdFromClient())); // 当 referer配置多个protocol的时候,比如A,B,C, @@ -113,7 +121,11 @@ public Object invoke(Object proxy, Method method, Object[] args) throws Throwabl URLParamType.throwException.getValue())); try { response = cluster.call(request); - return response.getValue(); + if (async && response instanceof ResponseFuture) { + return response; + } else { + return response.getValue(); + } } catch (RuntimeException e) { if (ExceptionUtil.isBizException(e)) { Throwable t = e.getCause(); diff --git a/motan-core/src/main/java/com/weibo/api/motan/rpc/ResponseFuture.java b/motan-core/src/main/java/com/weibo/api/motan/rpc/ResponseFuture.java new file mode 100644 index 000000000..56589f85a --- /dev/null +++ b/motan-core/src/main/java/com/weibo/api/motan/rpc/ResponseFuture.java @@ -0,0 +1,24 @@ +/* + * Copyright 2009-2016 Weibo, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.weibo.api.motan.rpc; + +public interface ResponseFuture extends Future, Response{ + public void onSuccess(Response response); + + public void onFailure(Response response) ; + + public long getCreateTime(); +} diff --git a/motan-core/src/main/java/com/weibo/api/motan/rpc/URL.java b/motan-core/src/main/java/com/weibo/api/motan/rpc/URL.java index b4a1f6fd4..f696b8fac 100644 --- a/motan-core/src/main/java/com/weibo/api/motan/rpc/URL.java +++ b/motan-core/src/main/java/com/weibo/api/motan/rpc/URL.java @@ -26,6 +26,7 @@ import com.weibo.api.motan.common.MotanConstants; import com.weibo.api.motan.common.URLParamType; import com.weibo.api.motan.exception.MotanServiceException; +import com.weibo.api.motan.util.MotanFrameworkUtil; /** *
@@ -63,7 +64,7 @@ public URL(String protocol, String host, int port, String path, Map getNumbers() {
         }
         return numbers;
     }
+    
+    /**
+     * because async call in client path with Async suffix,we need
+     * remove Async suffix in path for subscribe.
+     * @param path
+     * @return
+     */
+    private String removeAsyncPath(String path){
+        return MotanFrameworkUtil.removeAsyncSuffix(path);
+    }
 
 }
diff --git a/motan-core/src/main/java/com/weibo/api/motan/transport/async/MotanAsync.java b/motan-core/src/main/java/com/weibo/api/motan/transport/async/MotanAsync.java
new file mode 100644
index 000000000..b0a2376dc
--- /dev/null
+++ b/motan-core/src/main/java/com/weibo/api/motan/transport/async/MotanAsync.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2009-2016 Weibo, Inc.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.weibo.api.motan.transport.async;
+
+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;
+
+/**
+ * 
+ * @Description async request annotation
+ * @author zhanglei
+ * @date Feb 21, 2017
+ *
+ */
+@Documented
+@Target({ElementType.TYPE})
+@Retention(RetentionPolicy.SOURCE)
+public @interface MotanAsync {
+
+}
diff --git a/motan-core/src/main/java/com/weibo/api/motan/transport/async/MotanAsyncProcessor.java b/motan-core/src/main/java/com/weibo/api/motan/transport/async/MotanAsyncProcessor.java
new file mode 100644
index 000000000..5d8be97a0
--- /dev/null
+++ b/motan-core/src/main/java/com/weibo/api/motan/transport/async/MotanAsyncProcessor.java
@@ -0,0 +1,168 @@
+/*
+ * Copyright 2009-2016 Weibo, Inc.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.weibo.api.motan.transport.async;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
+import javax.annotation.processing.AbstractProcessor;
+import javax.annotation.processing.ProcessingEnvironment;
+import javax.annotation.processing.RoundEnvironment;
+import javax.annotation.processing.SupportedAnnotationTypes;
+import javax.annotation.processing.SupportedSourceVersion;
+import javax.lang.model.SourceVersion;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ElementKind;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.Modifier;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.element.TypeParameterElement;
+import javax.lang.model.element.VariableElement;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.TypeKind;
+import javax.lang.model.type.TypeMirror;
+import javax.tools.Diagnostic;
+
+import com.squareup.javapoet.JavaFile;
+import com.squareup.javapoet.MethodSpec;
+import com.squareup.javapoet.ParameterSpec;
+import com.squareup.javapoet.TypeName;
+import com.squareup.javapoet.TypeSpec;
+import com.squareup.javapoet.TypeVariableName;
+import com.weibo.api.motan.common.MotanConstants;
+import com.weibo.api.motan.rpc.ResponseFuture;
+
+/**
+ * 
+ * @Description create a {interfaceName}Async class with all method add a 'Async' suffix. TODO
+ *              support methods inherited from superinterfaces
+ * @author zhanglei
+ * @date Feb 24, 2017
+ *
+ */
+@SupportedSourceVersion(SourceVersion.RELEASE_6)
+@SupportedAnnotationTypes({"com.weibo.api.motan.transport.async.MotanAsync"})
+public class MotanAsyncProcessor extends AbstractProcessor {
+    protected static String ASYNC = MotanConstants.ASYNC_SUFFIX;
+    protected static String SRC_DIR = "src/main/java/";
+    protected static String TARGET_DIR = "target/generated-sources/annotations/";
+
+    public synchronized void init(ProcessingEnvironment processingEnv) {
+        super.init(processingEnv);
+    }
+
+    @Override
+    public boolean process(Set annotations, RoundEnvironment roundEnv) {
+        if (roundEnv.processingOver()) {
+            return true;
+        }
+        for (Element elem : roundEnv.getElementsAnnotatedWith(MotanAsync.class)) {
+            processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE, "MotanAsyncProcessor will process " + elem.toString());
+            try {
+                writeAsyncClass(elem);
+                processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE, "MotanAsyncProcessor done for " + elem.toString());
+            } catch (Exception e) {
+                processingEnv.getMessager().printMessage(Diagnostic.Kind.WARNING,
+                        "MotanAsyncProcessor process " + elem.toString() + " fail. exception:" + e.getMessage());
+                e.printStackTrace();
+            }
+        }
+        return true;
+    }
+
+    private void writeAsyncClass(Element elem) throws ClassNotFoundException, IOException, Exception {
+
+        if (elem.getKind().isInterface()) {
+            TypeElement interfaceClazz = (TypeElement) elem;
+            String className = interfaceClazz.getSimpleName().toString();
+            TypeSpec.Builder classBuilder =
+                    TypeSpec.interfaceBuilder(className + ASYNC).addModifiers(Modifier.PUBLIC)
+                            .addSuperinterface(TypeName.get(elem.asType()));
+
+            // add class generic type
+            classBuilder.addTypeVariables(getTypeNames(interfaceClazz.getTypeParameters()));
+
+            // add direct method
+            addMethods(interfaceClazz, classBuilder);
+
+            // add method form superinterface
+            addSuperInterfaceMethods(interfaceClazz.getInterfaces(), classBuilder);
+
+            // write class
+            JavaFile javaFile =
+                    JavaFile.builder(processingEnv.getElementUtils().getPackageOf(interfaceClazz).getQualifiedName().toString(),
+                            classBuilder.build()).build();
+
+            javaFile.writeTo(new File(System.getProperty("basedir"), TARGET_DIR));
+
+        } else {
+            processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE,
+                    "MotanAsyncProcessor not process, because " + elem.toString() + " not a interface.");
+        }
+    }
+
+    private void addMethods(TypeElement interfaceClazz, TypeSpec.Builder classBuilder) {
+        List elements = interfaceClazz.getEnclosedElements();
+        if (elements != null && !elements.isEmpty()) {
+            for (Element e : elements) {
+                if (ElementKind.METHOD.equals(e.getKind())) {
+                    ExecutableElement method = (ExecutableElement) e;
+                    MethodSpec.Builder methodBuilder =
+                            MethodSpec.methodBuilder(method.getSimpleName().toString() + ASYNC)
+                                    .addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT).returns(ResponseFuture.class)
+                                    .addTypeVariables(getTypeNames(method.getTypeParameters()));
+                    // add method params
+                    List vars = method.getParameters();
+                    for (VariableElement var : vars) {
+                        methodBuilder.addParameter(ParameterSpec.builder(TypeName.get(var.asType()), var.getSimpleName().toString())
+                                .build());
+                    }
+                    classBuilder.addMethod(methodBuilder.build());
+                }
+            }
+        }
+    }
+
+    private List getTypeNames(List types) {
+        List result = new ArrayList();
+        if (types != null && !types.isEmpty()) {
+            for (TypeParameterElement type : types) {
+                result.add(TypeVariableName.get(type));
+            }
+        }
+        return result;
+    }
+
+    private void addSuperInterfaceMethods(List superInterfaces, TypeSpec.Builder classBuilder) {
+        if (superInterfaces != null && !superInterfaces.isEmpty()) {
+            for (TypeMirror tm : superInterfaces) {
+                try {
+                    if (tm.getKind().equals(TypeKind.DECLARED)) {
+                        TypeElement de = (TypeElement) ((DeclaredType) tm).asElement();
+                        addMethods(de, classBuilder);
+                        addSuperInterfaceMethods(de.getInterfaces(), classBuilder);
+                    }
+                } catch (Exception e) {
+                    processingEnv.getMessager().printMessage(Diagnostic.Kind.WARNING,
+                            "MotanAsyncProcessor process superinterface " + tm.toString() + " fail. exception:" + e.getMessage());
+                    e.printStackTrace();
+                }
+            }
+        }
+    }
+
+}
diff --git a/motan-core/src/main/java/com/weibo/api/motan/util/MotanFrameworkUtil.java b/motan-core/src/main/java/com/weibo/api/motan/util/MotanFrameworkUtil.java
index 4671730db..447788be3 100644
--- a/motan-core/src/main/java/com/weibo/api/motan/util/MotanFrameworkUtil.java
+++ b/motan-core/src/main/java/com/weibo/api/motan/util/MotanFrameworkUtil.java
@@ -256,5 +256,12 @@ public static RegistryConfig getDefaultRegistryConfig(){
         return local;
     }
     
+    public static String removeAsyncSuffix(String path){
+        if(path != null && path.endsWith(MotanConstants.ASYNC_SUFFIX)){
+            return path.substring(0, path.length() - MotanConstants.ASYNC_SUFFIX.length());
+        }
+        return path;
+    }
+    
     
 }
diff --git a/motan-core/src/main/resources/META-INF/services/javax.annotation.processing.Processor b/motan-core/src/main/resources/META-INF/services/javax.annotation.processing.Processor
new file mode 100644
index 000000000..cdb00f444
--- /dev/null
+++ b/motan-core/src/main/resources/META-INF/services/javax.annotation.processing.Processor
@@ -0,0 +1 @@
+com.weibo.api.motan.transport.async.MotanAsyncProcessor
diff --git a/motan-core/src/test/java/com/weibo/api/motan/proxy/RefererInvocationHandlerTest.java b/motan-core/src/test/java/com/weibo/api/motan/proxy/RefererInvocationHandlerTest.java
index 07322c376..956a270e9 100644
--- a/motan-core/src/test/java/com/weibo/api/motan/proxy/RefererInvocationHandlerTest.java
+++ b/motan-core/src/test/java/com/weibo/api/motan/proxy/RefererInvocationHandlerTest.java
@@ -31,6 +31,8 @@
 import com.weibo.api.motan.exception.MotanServiceException;
 import com.weibo.api.motan.rpc.Referer;
 import com.weibo.api.motan.rpc.Request;
+import com.weibo.api.motan.rpc.ResponseFuture;
+import com.weibo.api.motan.rpc.RpcContext;
 import com.weibo.api.motan.rpc.URL;
 
 public class RefererInvocationHandlerTest extends BaseTestCase {
@@ -106,19 +108,58 @@ public void testLocalMehtod() throws Exception{
         
         
         
+    }
+    
+    @Test
+    @SuppressWarnings({"rawtypes", "unchecked"})
+    public void testAsync() {
+        final Cluster cluster = mockery.mock(Cluster.class);
+        final URL u = new URL("motan", "local", 80, "test");
+        u.addParameter(URLParamType.nodeType.getName(), MotanConstants.NODE_TYPE_REFERER);
+        final ResponseFuture response = mockery.mock(ResponseFuture.class);
+        mockery.checking(new Expectations() {
+            {
+                one(cluster).call(with(any(Request.class)));
+                will(returnValue(response));
+                allowing(cluster).getUrl();
+                will(returnValue(u));
+            }
+        });
+
+        List clus = new ArrayList();
+        clus.add(cluster);
+        RefererInvocationHandler handler = new RefererInvocationHandler(String.class, clus);
+        Method method;
+        try {
+            method = TestService.class.getMethod("helloAsync", new Class[] {});
+            ResponseFuture res = (ResponseFuture) handler.invoke(null, method, null);
+            assertEquals(response, res);
+            assertTrue((Boolean) RpcContext.getContext().getAttribute(MotanConstants.ASYNC_SUFFIX));
+        } catch (Throwable e) {
+            assertTrue(false);
+        }
+
     }
 
-    interface TestService{
+    interface TestService {
         String hello();
+
+        ResponseFuture helloAsync();
+
         boolean equals(Object o);
     }
-    
-    class TestServiceImpl implements TestService{
+
+    class TestServiceImpl implements TestService {
         @Override
         public String hello() {
             return "hello";
         }
-        
+
+        @Override
+        public ResponseFuture helloAsync() {
+            return null;
+        }
+
     }
 
 }
diff --git a/motan-demo/motan-demo-api/pom.xml b/motan-demo/motan-demo-api/pom.xml
index 094c55119..d0f7c4bb5 100644
--- a/motan-demo/motan-demo-api/pom.xml
+++ b/motan-demo/motan-demo-api/pom.xml
@@ -29,12 +29,49 @@
     
         UTF-8
     
-    
-        
-            junit
-            junit
-            3.8.1
-            test
-        
-    
+    
+    
+        
+            
+                org.codehaus.mojo
+                build-helper-maven-plugin
+                1.10
+                
+                    
+                        generate-sources
+                        
+                            add-source
+                        
+                        
+                            
+                                ${project.build.directory}/generated-sources/annotations
+                            
+                        
+                    
+                
+            
+            
+                org.apache.maven.plugins
+                maven-compiler-plugin
+                2.3.2
+                 
+                    ${java.source.jdk}
+                    ${java.source.jdk}
+                    ${project.build.sourceEncoding}
+                
+                
+                    
+                        process-annotations
+                        generate-sources
+                        
+                            compile
+                        
+                        
+                            true 
+                        
+                    
+                
+            
+        
+    
 
diff --git a/motan-demo/motan-demo-api/src/main/java/com/weibo/motan/demo/service/MotanDemoService.java b/motan-demo/motan-demo-api/src/main/java/com/weibo/motan/demo/service/MotanDemoService.java
index db08523c9..fba32b50a 100644
--- a/motan-demo/motan-demo-api/src/main/java/com/weibo/motan/demo/service/MotanDemoService.java
+++ b/motan-demo/motan-demo-api/src/main/java/com/weibo/motan/demo/service/MotanDemoService.java
@@ -16,6 +16,9 @@
 
 package com.weibo.motan.demo.service;
 
+import com.weibo.api.motan.transport.async.MotanAsync;
+
+@MotanAsync
 public interface MotanDemoService {
 	public String hello(String name);
 
diff --git a/motan-demo/motan-demo-api/src/main/java/com/weibo/motan/demo/service/TestInterface.java b/motan-demo/motan-demo-api/src/main/java/com/weibo/motan/demo/service/TestInterface.java
new file mode 100644
index 000000000..e42806b0f
--- /dev/null
+++ b/motan-demo/motan-demo-api/src/main/java/com/weibo/motan/demo/service/TestInterface.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2009-2016 Weibo, Inc.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.weibo.motan.demo.service;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import com.weibo.api.motan.transport.async.MotanAsync;
+
+@MotanAsync
+public interface TestInterface extends TestSuperInterface {
+    ArrayList xxx(ConcurrentHashMap map);
+
+    List methodRaw();
+
+    Map methodRaw(List list);
+
+    List methodType();
+
+    List methodWildcard();
+
+    List methodBoundedWildcard();
+
+    > Map methodTypeLiteral();
+
+    > void method(String p1, T p2, List p3, List p4);
+}
diff --git a/motan-demo/motan-demo-api/src/main/java/com/weibo/motan/demo/service/TestSuperInterface.java b/motan-demo/motan-demo-api/src/main/java/com/weibo/motan/demo/service/TestSuperInterface.java
new file mode 100644
index 000000000..a365f6518
--- /dev/null
+++ b/motan-demo/motan-demo-api/src/main/java/com/weibo/motan/demo/service/TestSuperInterface.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2009-2016 Weibo, Inc.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.weibo.motan.demo.service;
+
+import java.util.List;
+import java.util.Map;
+
+public interface TestSuperInterface {
+    String extendsMethod(Map map);
+
+    List extendsMethod2();
+
+}
diff --git a/motan-demo/motan-demo-api/src/test/java/com/weibo/motan/demo/test/TestMotanAsync.java b/motan-demo/motan-demo-api/src/test/java/com/weibo/motan/demo/test/TestMotanAsync.java
new file mode 100644
index 000000000..4a270e43c
--- /dev/null
+++ b/motan-demo/motan-demo-api/src/test/java/com/weibo/motan/demo/test/TestMotanAsync.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2009-2016 Weibo, Inc.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.weibo.motan.demo.test;
+
+import java.lang.reflect.Method;
+
+import org.junit.Test;
+
+import com.weibo.api.motan.common.MotanConstants;
+import com.weibo.api.motan.rpc.ResponseFuture;
+import com.weibo.motan.demo.service.TestInterface;
+import com.weibo.motan.demo.service.TestInterfaceAsync;
+import com.weibo.motan.demo.service.TestSuperInterface;
+
+import static org.junit.Assert.*;
+
+public class TestMotanAsync {
+    @Test
+    public void testAsyncMethodGenerate() throws NoSuchMethodException, SecurityException {
+        // direct methods and origin methods
+        validateMethods(TestInterface.class.getDeclaredMethods(), TestInterfaceAsync.class);
+
+        // methods from superinterface
+        validateMethods(TestSuperInterface.class.getDeclaredMethods(), TestInterfaceAsync.class);
+
+    }
+
+    private void validateMethods(Method[] methods, Class targetClazz) throws NoSuchMethodException, SecurityException {
+        for (Method m : methods) {
+            assertNotNull(targetClazz.getMethod(m.getName(), m.getParameterTypes()));
+            Method asyncMethod = targetClazz.getMethod(m.getName() + MotanConstants.ASYNC_SUFFIX, m.getParameterTypes());
+            assertNotNull(asyncMethod);
+            assertEquals(ResponseFuture.class, asyncMethod.getReturnType());
+        }
+    }
+
+}
diff --git a/motan-demo/motan-demo-client/src/main/java/com/weibo/motan/demo/client/DemoRpcAsyncClient.java b/motan-demo/motan-demo-client/src/main/java/com/weibo/motan/demo/client/DemoRpcAsyncClient.java
new file mode 100644
index 000000000..1e13877ea
--- /dev/null
+++ b/motan-demo/motan-demo-client/src/main/java/com/weibo/motan/demo/client/DemoRpcAsyncClient.java
@@ -0,0 +1,56 @@
+/*
+ *  Copyright 2009-2016 Weibo, Inc.
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License");
+ *    you may not use this file except in compliance with the License.
+ *    You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ */
+package com.weibo.motan.demo.client;
+
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.support.ClassPathXmlApplicationContext;
+
+import com.weibo.api.motan.rpc.ResponseFuture;
+import com.weibo.motan.demo.service.MotanDemoServiceAsync;
+
+public class DemoRpcAsyncClient {
+
+    /**
+     * 使用motan异步调用方法:
+     * 1、在声明的service上增加@MotanAsync注解。  如MotanDemoService
+     * 2、在项目pom.xml中增加build-helper-maven-plugin,用来把自动生成类的目录设置为source path。 参见motan-demo-api模块的pom声明。 
+     * 也可以不使用plugin,手动将target/generated-sources/annotations目录设置为source path。
+     * 3、在client配置的motan:referer标签中配置interface为自动生成的以Async为后缀的对应service类。 参见本模块的motan_demo_async_client.xml
+     * 
+     */
+    public static void main(String[] args) {
+        ApplicationContext ctx = new ClassPathXmlApplicationContext(new String[]{"classpath:motan_demo_async_client.xml"});
+
+        MotanDemoServiceAsync service = (MotanDemoServiceAsync) ctx.getBean("motanDemoReferer");
+        for (int i = 0; i < Integer.MAX_VALUE; i++) {
+            //async call
+            ResponseFuture future = service.helloAsync("motan sync " + i);
+            System.out.println(future.getValue());
+            // sync call
+            System.out.println(service.hello("motan" + i));
+            
+            try {
+                Thread.sleep(1000);
+            } catch (InterruptedException e) {
+                e.printStackTrace();
+            }
+        }
+        System.out.println("motan demo is finish.");
+        System.exit(0);
+
+    }
+
+}
diff --git a/motan-demo/motan-demo-client/src/main/resources/motan_demo_async_client.xml b/motan-demo/motan-demo-client/src/main/resources/motan_demo_async_client.xml
new file mode 100755
index 000000000..e1a144f69
--- /dev/null
+++ b/motan-demo/motan-demo-client/src/main/resources/motan_demo_async_client.xml
@@ -0,0 +1,43 @@
+
+
+
+
+
+    
+    
+    
+    
+
+    
+    
+
+    
+    
+
+    
+    
+
+
\ No newline at end of file
diff --git a/motan-demo/motan-demo-server/src/main/resources/motan_demo_server.xml b/motan-demo/motan-demo-server/src/main/resources/motan_demo_server.xml
index 442b0129b..b9bcba936 100755
--- a/motan-demo/motan-demo-server/src/main/resources/motan_demo_server.xml
+++ b/motan-demo/motan-demo-server/src/main/resources/motan_demo_server.xml
@@ -25,9 +25,9 @@
     
 
     
-    
+    
     
-    
+