diff --git a/dubbo-all/pom.xml b/dubbo-all/pom.xml index e0e6e11ccc9d..585332512e00 100644 --- a/dubbo-all/pom.xml +++ b/dubbo-all/pom.xml @@ -143,6 +143,13 @@ compile true + + org.apache.dubbo + dubbo-rpc-jsonrpc + ${project.version} + compile + true + org.apache.dubbo dubbo-rpc-rmi @@ -491,6 +498,7 @@ org.apache.dubbo:dubbo-rpc-api org.apache.dubbo:dubbo-rpc-dubbo org.apache.dubbo:dubbo-rpc-injvm + org.apache.dubbo:dubbo-rpc-jsonrpc org.apache.dubbo:dubbo-rpc-rmi org.apache.dubbo:dubbo-rpc-hessian org.apache.dubbo:dubbo-rpc-http diff --git a/dubbo-bom/pom.xml b/dubbo-bom/pom.xml index 85fcbae576db..b7e9f4ed460e 100644 --- a/dubbo-bom/pom.xml +++ b/dubbo-bom/pom.xml @@ -158,6 +158,11 @@ dubbo-rpc-injvm ${project.version} + + org.apache.dubbo + dubbo-rpc-jsonrpc + ${project.version} + org.apache.dubbo dubbo-rpc-rmi diff --git a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/ApplicationConfig.java b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/ApplicationConfig.java index d73e3e209e15..984733f293f5 100644 --- a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/ApplicationConfig.java +++ b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/ApplicationConfig.java @@ -124,6 +124,9 @@ public class ApplicationConfig extends AbstractConfig { private String shutwait; + private Boolean preferPublicIp; + + public ApplicationConfig() { } @@ -328,4 +331,11 @@ public boolean isValid() { return !StringUtils.isEmpty(name); } + public Boolean getPreferPublicIp() { + return preferPublicIp; + } + + public void setPreferPublicIp(Boolean preferPublicIp) { + this.preferPublicIp = preferPublicIp; + } } \ No newline at end of file diff --git a/dubbo-dependencies-bom/pom.xml b/dubbo-dependencies-bom/pom.xml index d2e604dad244..33fe9ee1ecbb 100644 --- a/dubbo-dependencies-bom/pom.xml +++ b/dubbo-dependencies-bom/pom.xml @@ -143,6 +143,8 @@ 2.0.1 2.8.5 + 1.2.0 + 2.0 @@ -528,12 +530,21 @@ ${spring_test_version} test - com.google.code.gson gson ${gson_version} + + com.github.briandilley.jsonrpc4j + jsonrpc4j + ${jsonrpc_version} + + + javax.portlet + portlet-api + ${portlet_version} + diff --git a/dubbo-distribution/pom.xml b/dubbo-distribution/pom.xml index d60548d3b179..126ec47618ca 100644 --- a/dubbo-distribution/pom.xml +++ b/dubbo-distribution/pom.xml @@ -110,6 +110,11 @@ dubbo-rpc-injvm ${project.version} + + org.apache.dubbo + dubbo-rpc-jsonrpc + ${project.version} + org.apache.dubbo dubbo-rpc-rmi diff --git a/dubbo-rpc/dubbo-rpc-jsonrpc/pom.xml b/dubbo-rpc/dubbo-rpc-jsonrpc/pom.xml new file mode 100644 index 000000000000..4a367328fe84 --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-jsonrpc/pom.xml @@ -0,0 +1,61 @@ + + + + dubbo-rpc + org.apache.dubbo + 2.7.2-SNAPSHOT + + 4.0.0 + + dubbo-rpc-jsonrpc + + The JSON-RPC module of dubbo project + + + false + + + + + org.apache.dubbo + dubbo-rpc-api + ${project.parent.version} + + + org.apache.dubbo + dubbo-remoting-http + ${project.parent.version} + + + org.springframework + spring-context + + + com.github.briandilley.jsonrpc4j + jsonrpc4j + + + javax.portlet + portlet-api + + + + + \ No newline at end of file diff --git a/dubbo-rpc/dubbo-rpc-jsonrpc/src/main/java/org/apache/dubbo/rpc/protocol/jsonrpc/JsonRpcProtocol.java b/dubbo-rpc/dubbo-rpc-jsonrpc/src/main/java/org/apache/dubbo/rpc/protocol/jsonrpc/JsonRpcProtocol.java new file mode 100644 index 000000000000..aaba802cd4ec --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-jsonrpc/src/main/java/org/apache/dubbo/rpc/protocol/jsonrpc/JsonRpcProtocol.java @@ -0,0 +1,163 @@ +/* + * 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.jsonrpc; + +import org.apache.dubbo.common.URL; +import org.apache.dubbo.remoting.http.HttpBinder; +import org.apache.dubbo.remoting.http.HttpHandler; +import org.apache.dubbo.remoting.http.HttpServer; +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.springframework.remoting.RemoteAccessException; + +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; + +public class JsonRpcProtocol 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 serverMap = new ConcurrentHashMap<>(); + + private final Map skeletonMap = new ConcurrentHashMap<>(); + + private HttpBinder httpBinder; + + public JsonRpcProtocol() { + 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 Runnable doExport(T impl, Class type, URL url) throws RpcException { + String addr = url.getIp() + ":" + url.getPort(); + HttpServer server = serverMap.get(addr); + if (server == null) { + server = httpBinder.bind(url, new InternalHandler(url.getParameter("cors", false))); + serverMap.put(addr, server); + } + final String path = url.getAbsolutePath(); + JsonRpcServer skeleton = new JsonRpcServer(impl, type); + skeletonMap.put(path, skeleton); + return () -> skeletonMap.remove(path); + } + + @SuppressWarnings("unchecked") + @Override + protected T doRefer(final Class serviceType, URL url) throws RpcException { + JsonProxyFactoryBean jsonProxyFactoryBean = new JsonProxyFactoryBean(); + jsonProxyFactoryBean.setServiceUrl(url.setProtocol("http").toIdentityString()); + jsonProxyFactoryBean.setServiceInterface(serviceType); + + jsonProxyFactoryBean.afterPropertiesSet(); + return (T) jsonProxyFactoryBean.getObject(); + } + + @Override + 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; + } + } + return super.getErrorCode(e); + } + + @Override + public void destroy() { + super.destroy(); + for (String key : new ArrayList<>(serverMap.keySet())) { + HttpServer 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); + } + } + } + } + +} \ No newline at end of file diff --git a/dubbo-rpc/dubbo-rpc-jsonrpc/src/main/resources/META-INF/dubbo/internal/com.alibaba.dubbo.rpc.Protocol b/dubbo-rpc/dubbo-rpc-jsonrpc/src/main/resources/META-INF/dubbo/internal/com.alibaba.dubbo.rpc.Protocol new file mode 100644 index 000000000000..0ce276fc4bdb --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-jsonrpc/src/main/resources/META-INF/dubbo/internal/com.alibaba.dubbo.rpc.Protocol @@ -0,0 +1 @@ +jsonrpc=org.apache.dubbo.rpc.protocol.jsonrpc.JsonRpcProtocol \ No newline at end of file diff --git a/dubbo-rpc/dubbo-rpc-jsonrpc/src/test/java/org/apache/dubbo/rpc/protocol/jsonrpc/JsonRpcProtocolTest.java b/dubbo-rpc/dubbo-rpc-jsonrpc/src/test/java/org/apache/dubbo/rpc/protocol/jsonrpc/JsonRpcProtocolTest.java new file mode 100644 index 000000000000..5f6e791e9b1a --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-jsonrpc/src/test/java/org/apache/dubbo/rpc/protocol/jsonrpc/JsonRpcProtocolTest.java @@ -0,0 +1,69 @@ +/* + * 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.jsonrpc; + + +import org.apache.dubbo.common.URL; +import org.apache.dubbo.common.extension.ExtensionLoader; +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.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 JsonRpcProtocolTest { + + @Test + public void testJsonrpcProtocol() { + JsonRpcServiceImpl server = new JsonRpcServiceImpl(); + assertFalse(server.isCalled()); + ProxyFactory proxyFactory = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension(); + Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension(); + URL url = URL.valueOf("jsonrpc://127.0.0.1:5342/" + JsonRpcService.class.getName() + "?version=1.0.0"); + Exporter exporter = protocol.export(proxyFactory.getInvoker(server, JsonRpcService.class, url)); + Invoker invoker = protocol.refer(JsonRpcService.class, url); + JsonRpcService client = proxyFactory.getProxy(invoker); + String result = client.sayHello("haha"); + assertTrue(server.isCalled()); + assertEquals("Hello, haha", result); + invoker.destroy(); + exporter.unexport(); + } + + @Test + public void testJsonrpcProtocolForServerJetty9() { + JsonRpcServiceImpl server = new JsonRpcServiceImpl(); + assertFalse(server.isCalled()); + ProxyFactory proxyFactory = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension(); + Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension(); + URL url = URL.valueOf("jsonrpc://127.0.0.1:5342/" + JsonRpcService.class.getName() + "?version=1.0.0&server=jetty9"); + Exporter exporter = protocol.export(proxyFactory.getInvoker(server, JsonRpcService.class, url)); + Invoker invoker = protocol.refer(JsonRpcService.class, url); + JsonRpcService client = proxyFactory.getProxy(invoker); + String result = client.sayHello("haha"); + assertTrue(server.isCalled()); + assertEquals("Hello, haha", result); + invoker.destroy(); + exporter.unexport(); + } + +} \ No newline at end of file diff --git a/dubbo-rpc/dubbo-rpc-jsonrpc/src/test/java/org/apache/dubbo/rpc/protocol/jsonrpc/JsonRpcService.java b/dubbo-rpc/dubbo-rpc-jsonrpc/src/test/java/org/apache/dubbo/rpc/protocol/jsonrpc/JsonRpcService.java new file mode 100644 index 000000000000..0bcbc8d1ade1 --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-jsonrpc/src/test/java/org/apache/dubbo/rpc/protocol/jsonrpc/JsonRpcService.java @@ -0,0 +1,25 @@ +/* + * 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.jsonrpc; + +public interface JsonRpcService { + String sayHello(String name); + + void timeOut(int millis); + + String customException(); +} diff --git a/dubbo-rpc/dubbo-rpc-jsonrpc/src/test/java/org/apache/dubbo/rpc/protocol/jsonrpc/JsonRpcServiceImpl.java b/dubbo-rpc/dubbo-rpc-jsonrpc/src/test/java/org/apache/dubbo/rpc/protocol/jsonrpc/JsonRpcServiceImpl.java new file mode 100644 index 000000000000..01f32d28abcf --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-jsonrpc/src/test/java/org/apache/dubbo/rpc/protocol/jsonrpc/JsonRpcServiceImpl.java @@ -0,0 +1,51 @@ +/* + * 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.jsonrpc; + +public class JsonRpcServiceImpl implements JsonRpcService { + 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); + } + } +} diff --git a/dubbo-rpc/pom.xml b/dubbo-rpc/pom.xml index dfd9be38c881..342c193fe9d4 100644 --- a/dubbo-rpc/pom.xml +++ b/dubbo-rpc/pom.xml @@ -32,6 +32,7 @@ dubbo-rpc-api dubbo-rpc-dubbo dubbo-rpc-injvm + dubbo-rpc-jsonrpc dubbo-rpc-rmi dubbo-rpc-hessian dubbo-rpc-http diff --git a/dubbo-test/pom.xml b/dubbo-test/pom.xml index 0cdf2a68fab5..51e97b6963c0 100644 --- a/dubbo-test/pom.xml +++ b/dubbo-test/pom.xml @@ -94,6 +94,10 @@ org.apache.dubbo dubbo-remoting-http + + org.apache.dubbo + dubbo-rpc-jsonrpc + org.apache.dubbo dubbo-rpc-dubbo