1+ /*
2+ * Licensed to the Apache Software Foundation (ASF) under one or more
3+ * contributor license agreements. See the NOTICE file distributed with
4+ * this work for additional information regarding copyright ownership.
5+ * The ASF licenses this file to You under the Apache License, Version 2.0
6+ * (the "License"); you may not use this file except in compliance with
7+ * the License. You may obtain a copy of the License at
8+ *
9+ * http://www.apache.org/licenses/LICENSE-2.0
10+ *
11+ * Unless required by applicable law or agreed to in writing, software
12+ * distributed under the License is distributed on an "AS IS" BASIS,
13+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+ * See the License for the specific language governing permissions and
15+ * limitations under the License.
16+ */
17+ package org .apache .dubbo .rpc .protocol .jsonrpc ;
18+
19+ import org .apache .dubbo .common .URL ;
20+ import org .apache .dubbo .remoting .http .HttpBinder ;
21+ import org .apache .dubbo .remoting .http .HttpHandler ;
22+ import org .apache .dubbo .remoting .http .HttpServer ;
23+ import org .apache .dubbo .rpc .RpcContext ;
24+ import org .apache .dubbo .rpc .RpcException ;
25+ import org .apache .dubbo .rpc .protocol .AbstractProxyProtocol ;
26+
27+ import com .googlecode .jsonrpc4j .HttpException ;
28+ import com .googlecode .jsonrpc4j .JsonRpcClientException ;
29+ import com .googlecode .jsonrpc4j .JsonRpcServer ;
30+ import com .googlecode .jsonrpc4j .spring .JsonProxyFactoryBean ;
31+ import org .springframework .remoting .RemoteAccessException ;
32+
33+ import javax .servlet .ServletException ;
34+ import javax .servlet .http .HttpServletRequest ;
35+ import javax .servlet .http .HttpServletResponse ;
36+ import java .io .IOException ;
37+ import java .net .SocketTimeoutException ;
38+ import java .util .ArrayList ;
39+ import java .util .Map ;
40+ import java .util .concurrent .ConcurrentHashMap ;
41+
42+ public class JsonRpcProtocol extends AbstractProxyProtocol {
43+
44+ public static final String ACCESS_CONTROL_ALLOW_ORIGIN_HEADER = "Access-Control-Allow-Origin" ;
45+ public static final String ACCESS_CONTROL_ALLOW_METHODS_HEADER = "Access-Control-Allow-Methods" ;
46+ public static final String ACCESS_CONTROL_ALLOW_HEADERS_HEADER = "Access-Control-Allow-Headers" ;
47+
48+ private final Map <String , HttpServer > serverMap = new ConcurrentHashMap <>();
49+
50+ private final Map <String , JsonRpcServer > skeletonMap = new ConcurrentHashMap <>();
51+
52+ private HttpBinder httpBinder ;
53+
54+ public JsonRpcProtocol () {
55+ super (HttpException .class , JsonRpcClientException .class );
56+ }
57+
58+ public void setHttpBinder (HttpBinder httpBinder ) {
59+ this .httpBinder = httpBinder ;
60+ }
61+
62+ @ Override
63+ public int getDefaultPort () {
64+ return 80 ;
65+ }
66+
67+ private class InternalHandler implements HttpHandler {
68+
69+ private boolean cors ;
70+
71+ public InternalHandler (boolean cors ) {
72+ this .cors = cors ;
73+ }
74+
75+ @ Override
76+ public void handle (HttpServletRequest request , HttpServletResponse response )
77+ throws ServletException {
78+ String uri = request .getRequestURI ();
79+ JsonRpcServer skeleton = skeletonMap .get (uri );
80+ if (cors ) {
81+ response .setHeader (ACCESS_CONTROL_ALLOW_ORIGIN_HEADER , "*" );
82+ response .setHeader (ACCESS_CONTROL_ALLOW_METHODS_HEADER , "POST" );
83+ response .setHeader (ACCESS_CONTROL_ALLOW_HEADERS_HEADER , "*" );
84+ }
85+ if (request .getMethod ().equalsIgnoreCase ("OPTIONS" )) {
86+ response .setStatus (200 );
87+ } else if (request .getMethod ().equalsIgnoreCase ("POST" )) {
88+
89+ RpcContext .getContext ().setRemoteAddress (request .getRemoteAddr (), request .getRemotePort ());
90+ try {
91+ skeleton .handle (request .getInputStream (), response .getOutputStream ());
92+ } catch (Throwable e ) {
93+ throw new ServletException (e );
94+ }
95+ } else {
96+ response .setStatus (500 );
97+ }
98+ }
99+
100+ }
101+
102+ @ Override
103+ protected <T > Runnable doExport (T impl , Class <T > type , URL url ) throws RpcException {
104+ String addr = url .getIp () + ":" + url .getPort ();
105+ HttpServer server = serverMap .get (addr );
106+ if (server == null ) {
107+ server = httpBinder .bind (url , new InternalHandler (url .getParameter ("cors" , false )));
108+ serverMap .put (addr , server );
109+ }
110+ final String path = url .getAbsolutePath ();
111+ JsonRpcServer skeleton = new JsonRpcServer (impl , type );
112+ skeletonMap .put (path , skeleton );
113+ return () -> skeletonMap .remove (path );
114+ }
115+
116+ @ SuppressWarnings ("unchecked" )
117+ @ Override
118+ protected <T > T doRefer (final Class <T > serviceType , URL url ) throws RpcException {
119+ JsonProxyFactoryBean jsonProxyFactoryBean = new JsonProxyFactoryBean ();
120+ jsonProxyFactoryBean .setServiceUrl (url .setProtocol ("http" ).toIdentityString ());
121+ jsonProxyFactoryBean .setServiceInterface (serviceType );
122+
123+ jsonProxyFactoryBean .afterPropertiesSet ();
124+ return (T ) jsonProxyFactoryBean .getObject ();
125+ }
126+
127+ @ Override
128+ protected int getErrorCode (Throwable e ) {
129+ if (e instanceof RemoteAccessException ) {
130+ e = e .getCause ();
131+ }
132+ if (e != null ) {
133+ Class <?> cls = e .getClass ();
134+ if (SocketTimeoutException .class .equals (cls )) {
135+ return RpcException .TIMEOUT_EXCEPTION ;
136+ } else if (IOException .class .isAssignableFrom (cls )) {
137+ return RpcException .NETWORK_EXCEPTION ;
138+ } else if (ClassNotFoundException .class .isAssignableFrom (cls )) {
139+ return RpcException .SERIALIZATION_EXCEPTION ;
140+ }
141+ }
142+ return super .getErrorCode (e );
143+ }
144+
145+ @ Override
146+ public void destroy () {
147+ super .destroy ();
148+ for (String key : new ArrayList <>(serverMap .keySet ())) {
149+ HttpServer server = serverMap .remove (key );
150+ if (server != null ) {
151+ try {
152+ if (logger .isInfoEnabled ()) {
153+ logger .info ("Close jsonrpc server " + server .getUrl ());
154+ }
155+ server .close ();
156+ } catch (Throwable t ) {
157+ logger .warn (t .getMessage (), t );
158+ }
159+ }
160+ }
161+ }
162+
163+ }
0 commit comments