Extend rpc http service (#37)
* http 测试
* 测试异常 Not found method $invoke 解决
* 修改pom文件
* fix cli build error
* delete log4j properties
diff --git a/dubbo-rpc-extensions/dubbo-rpc-http/pom.xml b/dubbo-rpc-extensions/dubbo-rpc-http/pom.xml
new file mode 100644
index 0000000..fecae4f
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-http/pom.xml
@@ -0,0 +1,62 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT 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/xsd/maven-4.0.0.xsd">
+ <parent>
+ <artifactId>dubbo-rpc-extensions</artifactId>
+ <groupId>org.apache.dubbo.extensions</groupId>
+ <version>${revision}</version>
+ <relativePath>../pom.xml</relativePath>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+
+ <artifactId>dubbo-rpc-http</artifactId>
+
+ <description>The JSON-RPC module of dubbo project</description>
+
+ <properties>
+ <skip_maven_deploy>false</skip_maven_deploy>
+ </properties>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.dubbo</groupId>
+ <artifactId>dubbo-rpc-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.dubbo</groupId>
+ <artifactId>dubbo-remoting-http</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-context</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.dubbo</groupId>
+ <artifactId>dubbo-cluster</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>com.github.briandilley.jsonrpc4j</groupId>
+ <artifactId>jsonrpc4j</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>javax.portlet</groupId>
+ <artifactId>portlet-api</artifactId>
+ </dependency>
+ </dependencies>
+</project>
diff --git a/dubbo-rpc-extensions/dubbo-rpc-http/src/main/java/org/apache/dubbo/rpc/protocol/http/HttpProtocol.java b/dubbo-rpc-extensions/dubbo-rpc-http/src/main/java/org/apache/dubbo/rpc/protocol/http/HttpProtocol.java
new file mode 100644
index 0000000..22d4434
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-http/src/main/java/org/apache/dubbo/rpc/protocol/http/HttpProtocol.java
@@ -0,0 +1,195 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.protocol.http;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.remoting.RemotingServer;
+import org.apache.dubbo.remoting.http.HttpBinder;
+import org.apache.dubbo.remoting.http.HttpHandler;
+import org.apache.dubbo.rpc.ProtocolServer;
+import org.apache.dubbo.rpc.RpcContext;
+import org.apache.dubbo.rpc.RpcException;
+import org.apache.dubbo.rpc.protocol.AbstractProxyProtocol;
+
+import com.googlecode.jsonrpc4j.HttpException;
+import com.googlecode.jsonrpc4j.JsonRpcClientException;
+import com.googlecode.jsonrpc4j.JsonRpcServer;
+import com.googlecode.jsonrpc4j.spring.JsonProxyFactoryBean;
+import org.apache.dubbo.rpc.service.GenericService;
+import org.apache.dubbo.rpc.support.ProtocolUtils;
+import com.fasterxml.jackson.databind.DeserializationFeature;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.springframework.remoting.RemoteAccessException;
+import org.springframework.remoting.support.RemoteInvocation;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.net.SocketTimeoutException;
+import java.util.ArrayList;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import static org.apache.dubbo.rpc.Constants.GENERIC_KEY;
+
+public class HttpProtocol extends AbstractProxyProtocol {
+ public static final String ACCESS_CONTROL_ALLOW_ORIGIN_HEADER = "Access-Control-Allow-Origin";
+ public static final String ACCESS_CONTROL_ALLOW_METHODS_HEADER = "Access-Control-Allow-Methods";
+ public static final String ACCESS_CONTROL_ALLOW_HEADERS_HEADER = "Access-Control-Allow-Headers";
+
+ private final Map<String, JsonRpcServer> skeletonMap = new ConcurrentHashMap<>();
+
+ private HttpBinder httpBinder;
+
+ public HttpProtocol() {
+ super(HttpException.class, JsonRpcClientException.class);
+ }
+
+ public void setHttpBinder(HttpBinder httpBinder) {
+ this.httpBinder = httpBinder;
+ }
+
+ @Override
+ public int getDefaultPort() {
+ return 80;
+ }
+
+ private class InternalHandler implements HttpHandler {
+
+ private boolean cors;
+
+ public InternalHandler(boolean cors) {
+ this.cors = cors;
+ }
+
+ @Override
+ public void handle(HttpServletRequest request, HttpServletResponse response)
+ throws ServletException {
+ String uri = request.getRequestURI();
+ JsonRpcServer skeleton = skeletonMap.get(uri);
+ if (cors) {
+ response.setHeader(ACCESS_CONTROL_ALLOW_ORIGIN_HEADER, "*");
+ response.setHeader(ACCESS_CONTROL_ALLOW_METHODS_HEADER, "POST");
+ response.setHeader(ACCESS_CONTROL_ALLOW_HEADERS_HEADER, "*");
+ }
+ if (request.getMethod().equalsIgnoreCase("OPTIONS")) {
+ response.setStatus(200);
+ } else if (request.getMethod().equalsIgnoreCase("POST")) {
+
+ RpcContext.getContext().setRemoteAddress(request.getRemoteAddr(), request.getRemotePort());
+ try {
+ skeleton.handle(request.getInputStream(), response.getOutputStream());
+ } catch (Throwable e) {
+ throw new ServletException(e);
+ }
+ } else {
+ response.setStatus(500);
+ }
+ }
+
+ }
+
+ @Override
+ protected <T> Runnable doExport(final T impl, Class<T> type, URL url) throws RpcException {
+ String addr = getAddr(url);
+ ProtocolServer protocolServer = serverMap.get(addr);
+ if (protocolServer == null) {
+ RemotingServer remotingServer = httpBinder.bind(url, new InternalHandler(url.getParameter("cors", false)));
+ serverMap.put(addr, new ProxyProtocolServer(remotingServer));
+ }
+ final String path = url.getAbsolutePath();
+ final String genericPath = path + "/" + GENERIC_KEY;
+ ObjectMapper mapper = new ObjectMapper();
+ mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
+ JsonRpcServer skeleton = new JsonRpcServer(mapper, impl, type);
+ JsonRpcServer genericServer = new JsonRpcServer(mapper, impl, GenericService.class);
+ skeletonMap.put(path, skeleton);
+ skeletonMap.put(genericPath, genericServer);
+ return () -> {
+ skeletonMap.remove(path);
+ skeletonMap.remove(genericPath);
+ };
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ protected <T> T doRefer(final Class<T> serviceType, URL url) throws RpcException {
+ final String generic = url.getParameter(GENERIC_KEY);
+ final boolean isGeneric = ProtocolUtils.isGeneric(generic) || serviceType.equals(GenericService.class);
+ JsonProxyFactoryBean jsonProxyFactoryBean = new JsonProxyFactoryBean();
+ JsonRpcProxyFactoryBean jsonRpcProxyFactoryBean = new JsonRpcProxyFactoryBean(jsonProxyFactoryBean);
+ jsonRpcProxyFactoryBean.setRemoteInvocationFactory((methodInvocation) -> {
+ RemoteInvocation invocation = new JsonRemoteInvocation(methodInvocation);
+ if (isGeneric) {
+ invocation.addAttribute(GENERIC_KEY, generic);
+ }
+ return invocation;
+ });
+ String key = url.setProtocol("http").toIdentityString();
+ if (isGeneric) {
+ key = key + "/" + GENERIC_KEY;
+ }
+
+ jsonRpcProxyFactoryBean.setServiceUrl(key);
+ jsonRpcProxyFactoryBean.setServiceInterface(serviceType);
+
+ jsonProxyFactoryBean.afterPropertiesSet();
+ return (T) jsonProxyFactoryBean.getObject();
+ }
+
+ protected int getErrorCode(Throwable e) {
+ if (e instanceof RemoteAccessException) {
+ e = e.getCause();
+ }
+ if (e != null) {
+ Class<?> cls = e.getClass();
+ if (SocketTimeoutException.class.equals(cls)) {
+ return RpcException.TIMEOUT_EXCEPTION;
+ } else if (IOException.class.isAssignableFrom(cls)) {
+ return RpcException.NETWORK_EXCEPTION;
+ } else if (ClassNotFoundException.class.isAssignableFrom(cls)) {
+ return RpcException.SERIALIZATION_EXCEPTION;
+ }
+
+ if (e instanceof HttpProtocolErrorCode) {
+ return ((HttpProtocolErrorCode) e).getErrorCode();
+ }
+ }
+ return super.getErrorCode(e);
+ }
+
+ @Override
+ public void destroy() {
+ super.destroy();
+ for (String key : new ArrayList<>(serverMap.keySet())) {
+ ProtocolServer server = serverMap.remove(key);
+ if (server != null) {
+ try {
+ if (logger.isInfoEnabled()) {
+ logger.info("Close jsonrpc server " + server.getUrl());
+ }
+ server.close();
+ } catch (Throwable t) {
+ logger.warn(t.getMessage(), t);
+ }
+ }
+ }
+ }
+
+
+}
diff --git a/dubbo-rpc-extensions/dubbo-rpc-http/src/main/java/org/apache/dubbo/rpc/protocol/http/HttpProtocolErrorCode.java b/dubbo-rpc-extensions/dubbo-rpc-http/src/main/java/org/apache/dubbo/rpc/protocol/http/HttpProtocolErrorCode.java
new file mode 100644
index 0000000..75b4103
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-http/src/main/java/org/apache/dubbo/rpc/protocol/http/HttpProtocolErrorCode.java
@@ -0,0 +1,29 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.protocol.http;
+
+/**
+ * Custom exception error code for http protocol.
+ */
+public interface HttpProtocolErrorCode {
+
+ /**
+ * @return custom error code of exception
+ */
+ int getErrorCode();
+
+}
diff --git a/dubbo-rpc-extensions/dubbo-rpc-http/src/main/java/org/apache/dubbo/rpc/protocol/http/JsonRemoteInvocation.java b/dubbo-rpc-extensions/dubbo-rpc-http/src/main/java/org/apache/dubbo/rpc/protocol/http/JsonRemoteInvocation.java
new file mode 100644
index 0000000..b5437f6
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-http/src/main/java/org/apache/dubbo/rpc/protocol/http/JsonRemoteInvocation.java
@@ -0,0 +1,61 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.dubbo.rpc.protocol.http;
+
+import org.apache.dubbo.common.utils.StringUtils;
+import org.apache.dubbo.rpc.RpcContext;
+
+import org.aopalliance.intercept.MethodInvocation;
+import org.springframework.remoting.support.RemoteInvocation;
+
+import java.lang.reflect.InvocationTargetException;
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.apache.dubbo.rpc.Constants.GENERIC_KEY;
+
+/**
+ * JsonRemoteInvocation
+ */
+public class JsonRemoteInvocation extends RemoteInvocation {
+ private static final long serialVersionUID = 1L;
+ private static final String DUBBO_ATTACHMENTS_ATTR_NAME = "dubbo.attachments";
+
+ public JsonRemoteInvocation(MethodInvocation methodInvocation) {
+ super(methodInvocation);
+ addAttribute(DUBBO_ATTACHMENTS_ATTR_NAME, new HashMap<>(RpcContext.getContext().getAttachments()));
+ }
+
+ @Override
+ public Object invoke(Object targetObject) throws NoSuchMethodException, IllegalAccessException,
+ InvocationTargetException {
+ RpcContext context = RpcContext.getContext();
+ context.setAttachments((Map<String, String>) getAttribute(DUBBO_ATTACHMENTS_ATTR_NAME));
+
+ String generic = (String) getAttribute(GENERIC_KEY);
+ if (StringUtils.isNotEmpty(generic)) {
+ context.setAttachment(GENERIC_KEY, generic);
+ }
+ try {
+ return super.invoke(targetObject);
+ } finally {
+ context.setAttachments(null);
+
+ }
+ }
+}
diff --git a/dubbo-rpc-extensions/dubbo-rpc-http/src/main/java/org/apache/dubbo/rpc/protocol/http/JsonRpcProxyFactoryBean.java b/dubbo-rpc-extensions/dubbo-rpc-http/src/main/java/org/apache/dubbo/rpc/protocol/http/JsonRpcProxyFactoryBean.java
new file mode 100644
index 0000000..aff81d2
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-http/src/main/java/org/apache/dubbo/rpc/protocol/http/JsonRpcProxyFactoryBean.java
@@ -0,0 +1,86 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.dubbo.rpc.protocol.http;
+
+import com.googlecode.jsonrpc4j.spring.JsonProxyFactoryBean;
+import org.aopalliance.intercept.MethodInterceptor;
+import org.aopalliance.intercept.MethodInvocation;
+import org.springframework.beans.factory.FactoryBean;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
+import org.springframework.remoting.support.RemoteInvocationBasedAccessor;
+
+/**
+ * JsonRpcProxyFactoryBean
+ */
+public class JsonRpcProxyFactoryBean extends RemoteInvocationBasedAccessor
+ implements MethodInterceptor,
+ InitializingBean,
+ FactoryBean<Object>,
+ ApplicationContextAware {
+ private final JsonProxyFactoryBean jsonProxyFactoryBean;
+
+ public JsonRpcProxyFactoryBean(JsonProxyFactoryBean factoryBean) {
+ this.jsonProxyFactoryBean = factoryBean;
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public void afterPropertiesSet() {
+ jsonProxyFactoryBean.afterPropertiesSet();
+ }
+
+ @Override
+ public Object invoke(MethodInvocation invocation)
+ throws Throwable {
+
+ return jsonProxyFactoryBean.invoke(invocation);
+ }
+
+ @Override
+ public Object getObject() {
+ return jsonProxyFactoryBean.getObject();
+ }
+
+ @Override
+ public Class<?> getObjectType() {
+ return jsonProxyFactoryBean.getObjectType();
+ }
+
+ @Override
+ public boolean isSingleton() {
+ return jsonProxyFactoryBean.isSingleton();
+ }
+
+ @Override
+ public void setApplicationContext(ApplicationContext applicationContext) {
+ jsonProxyFactoryBean.setApplicationContext(applicationContext);
+ }
+
+ @Override
+ public void setServiceUrl(String serviceUrl) {
+ jsonProxyFactoryBean.setServiceUrl(serviceUrl);
+ }
+
+ @Override
+ public void setServiceInterface(Class<?> serviceInterface) {
+ jsonProxyFactoryBean.setServiceInterface(serviceInterface);
+ }
+
+}
diff --git a/dubbo-rpc-extensions/dubbo-rpc-http/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.Protocol b/dubbo-rpc-extensions/dubbo-rpc-http/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.Protocol
new file mode 100644
index 0000000..46f47ba
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-http/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.Protocol
@@ -0,0 +1 @@
+http=org.apache.dubbo.rpc.protocol.http.HttpProtocol
\ No newline at end of file
diff --git a/dubbo-rpc-extensions/dubbo-rpc-http/src/test/java/org/apache/dubbo/rpc/protocol/http/HttpProtocolTest.java b/dubbo-rpc-extensions/dubbo-rpc-http/src/test/java/org/apache/dubbo/rpc/protocol/http/HttpProtocolTest.java
new file mode 100644
index 0000000..8b23d1e
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-http/src/test/java/org/apache/dubbo/rpc/protocol/http/HttpProtocolTest.java
@@ -0,0 +1,92 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.protocol.http;
+
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.extension.ExtensionLoader;
+import org.apache.dubbo.common.utils.NetUtils;
+import org.apache.dubbo.rpc.Exporter;
+import org.apache.dubbo.rpc.Invoker;
+import org.apache.dubbo.rpc.Protocol;
+import org.apache.dubbo.rpc.ProxyFactory;
+
+import org.apache.dubbo.rpc.service.GenericService;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public class HttpProtocolTest {
+
+ @Test
+ public void testJsonrpcProtocol() {
+ HttpServiceImpl server = new HttpServiceImpl();
+ assertFalse(server.isCalled());
+ ProxyFactory proxyFactory = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();
+ Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
+ int port = NetUtils.getAvailablePort();
+ URL url = URL.valueOf("http://127.0.0.1:" + port + "/" + HttpService.class.getName() + "?version=1.0.0");
+ Exporter<HttpService> exporter = protocol.export(proxyFactory.getInvoker(server, HttpService.class, url));
+ Invoker<HttpService> invoker = protocol.refer(HttpService.class, url);
+ HttpService client = proxyFactory.getProxy(invoker);
+ String result = client.sayHello("haha");
+ assertTrue(server.isCalled());
+ assertEquals("Hello, haha", result);
+ invoker.destroy();
+ exporter.unexport();
+ }
+
+ @Test
+ public void testJsonrpcProtocolForServerJetty() {
+ HttpServiceImpl server = new HttpServiceImpl();
+ assertFalse(server.isCalled());
+ ProxyFactory proxyFactory = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();
+ Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
+ int port = NetUtils.getAvailablePort();
+ URL url = URL.valueOf("http://127.0.0.1:" + port + "/" + HttpService.class.getName() + "?version=1.0.0&server=jetty");
+ Exporter<HttpService> exporter = protocol.export(proxyFactory.getInvoker(server, HttpService.class, url));
+ Invoker<HttpService> invoker = protocol.refer(HttpService.class, url);
+ HttpService client = proxyFactory.getProxy(invoker);
+ String result = client.sayHello("haha");
+ assertTrue(server.isCalled());
+ assertEquals("Hello, haha", result);
+ invoker.destroy();
+ exporter.unexport();
+ }
+
+ @Test
+ public void testGenericInvoke() {
+ HttpServiceImpl server = new HttpServiceImpl();
+ Assertions.assertFalse(server.isCalled());
+ ProxyFactory proxyFactory = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();
+ Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
+ int port = NetUtils.getAvailablePort();
+ URL url = URL.valueOf("http://127.0.0.1:" + port + "/" + HttpService.class.getName() + "?release=2.7.2");
+ Exporter<HttpService> exporter = protocol.export(proxyFactory.getInvoker(server, HttpService.class, url));
+ Invoker<GenericService> invoker = protocol.refer(GenericService.class, url);
+ GenericService client = proxyFactory.getProxy(invoker, true);
+ String result = (String) client.$invoke("sayHello", new String[]{"java.lang.String"}, new Object[]{"haha"});
+ Assertions.assertTrue(server.isCalled());
+ Assertions.assertEquals("Hello, haha", result);
+ invoker.destroy();
+ exporter.unexport();
+ }
+
+}
diff --git a/dubbo-rpc-extensions/dubbo-rpc-http/src/test/java/org/apache/dubbo/rpc/protocol/http/HttpService.java b/dubbo-rpc-extensions/dubbo-rpc-http/src/test/java/org/apache/dubbo/rpc/protocol/http/HttpService.java
new file mode 100644
index 0000000..f05994e
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-http/src/test/java/org/apache/dubbo/rpc/protocol/http/HttpService.java
@@ -0,0 +1,27 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.protocol.http;
+
+public interface HttpService {
+ String sayHello(String name);
+
+ void timeOut(int millis);
+
+ String customException();
+
+ String getRemoteApplicationName();
+}
diff --git a/dubbo-rpc-extensions/dubbo-rpc-http/src/test/java/org/apache/dubbo/rpc/protocol/http/HttpServiceImpl.java b/dubbo-rpc-extensions/dubbo-rpc-http/src/test/java/org/apache/dubbo/rpc/protocol/http/HttpServiceImpl.java
new file mode 100644
index 0000000..44fb4f2
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-http/src/test/java/org/apache/dubbo/rpc/protocol/http/HttpServiceImpl.java
@@ -0,0 +1,66 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.protocol.http;
+
+import org.apache.dubbo.rpc.RpcContext;
+import org.apache.dubbo.rpc.service.GenericException;
+import org.apache.dubbo.rpc.service.GenericService;
+
+public class HttpServiceImpl implements HttpService {
+ 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");
+ }
+
+// @Override
+// public Object $invoke(String method, String[] parameterTypes, Object[] args) throws GenericException {
+// called = true;
+// return "Hello, " + args[0];
+// }
+
+ static class MyException extends RuntimeException{
+
+ private static final long serialVersionUID = -3051041116483629056L;
+
+ public MyException(String message) {
+ super(message);
+ }
+ }
+
+ @Override
+ public String getRemoteApplicationName() {
+ return RpcContext.getContext().getRemoteApplicationName();
+ }
+}
diff --git a/dubbo-rpc-extensions/pom.xml b/dubbo-rpc-extensions/pom.xml
index 09fd505..9a75355 100644
--- a/dubbo-rpc-extensions/pom.xml
+++ b/dubbo-rpc-extensions/pom.xml
@@ -24,8 +24,12 @@
<version>${revision}</version>
<relativePath>../pom.xml</relativePath>
</parent>
+ <packaging>pom</packaging>
+
<modelVersion>4.0.0</modelVersion>
<artifactId>dubbo-rpc-extensions</artifactId>
-
+ <modules>
+ <module>dubbo-rpc-http</module>
+ </modules>
</project>