├── .gitignore
├── .travis.yml
├── LICENSE.txt
├── README.md
├── jsonrpc-java
├── build
│ ├── bump-version.sh
│ ├── merge.sh
│ └── release.sh
├── pom.xml
└── src
│ ├── main
│ ├── assembly
│ │ └── assembly-descriptor.xml
│ └── java
│ │ └── org
│ │ └── json
│ │ └── rpc
│ │ ├── client
│ │ ├── HttpJsonRpcClientTransport.java
│ │ ├── JsonRpcClientTransport.java
│ │ └── JsonRpcInvoker.java
│ │ ├── commons
│ │ ├── AllowAllTypeChecker.java
│ │ ├── GsonTypeChecker.java
│ │ ├── JsonRpcClientException.java
│ │ ├── JsonRpcErrorCodes.java
│ │ ├── JsonRpcException.java
│ │ ├── JsonRpcRemoteException.java
│ │ ├── RpcIntroSpection.java
│ │ └── TypeChecker.java
│ │ └── server
│ │ ├── HandleEntry.java
│ │ ├── JsonRpcExecutor.java
│ │ ├── JsonRpcServerTransport.java
│ │ └── JsonRpcServletTransport.java
│ └── test
│ ├── java
│ └── org
│ │ └── json
│ │ └── rpc
│ │ ├── JsonRpcTest.java
│ │ ├── client
│ │ └── JsonRpcInvokerTest.java
│ │ ├── commons
│ │ └── GsonTypeCheckerTest.java
│ │ └── server
│ │ ├── CyclicReferenceBugImpl.java
│ │ └── JsonRpcExecutorTest.java
│ └── resources
│ └── log4j.properties
└── jsonrpc-js
└── jsonrpc.js
/.gitignore:
--------------------------------------------------------------------------------
1 | # Project Files #
2 | #################
3 | .idea
4 | *.ipr
5 | *.iml
6 | .settings
7 | .project
8 | .classpath
9 |
10 | # Maven Files #
11 | ###############
12 | pom.xml.*
13 | release.properties
14 | target
15 |
16 | # Compiled source #
17 | ###################
18 | *.com
19 | *.class
20 | *.dll
21 | *.exe
22 | *.o
23 | *.so
24 |
25 | # Packages #
26 | ############
27 | # it's better to unpack these files and commit the raw source
28 | # git has its own built in compression methods
29 | *.7z
30 | *.dmg
31 | *.gz
32 | *.iso
33 | *.jar
34 | *.rar
35 | *.tar
36 | *.zip
37 |
38 | # Logs and databases #
39 | ######################
40 | *.log
41 | *.sqlite
42 |
43 | # OS generated files #
44 | ######################
45 | .DS_Store
46 | .DS_Store?
47 | ._*
48 | .Spotlight-V100
49 | .Trashes
50 | Icon?
51 | ehthumbs.db
52 | Thumbs.db
53 |
54 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: java
2 | jdk:
3 | - openjdk6
4 |
5 | branches:
6 | only:
7 | - master
8 | - develop
9 | - /^release-.*$/
10 | - /^hotfix-.*$/
11 |
12 | script: "cd jsonrpc-java/ && mvn clean install"
13 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | Copyright 2012 ritwik.net
2 |
3 | Licensed under the Apache License, Version 2.0 (the "License"); you may not
4 | use this file except in compliance with the License. You may obtain a copy of
5 | the License at
6 |
7 | http://www.apache.org/licenses/LICENSE-2.0
8 |
9 | Unless required by applicable law or agreed to in writing, software
10 | distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 | WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 | License for the specific language governing permissions and limitations under
13 | the License.
14 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # JsonRpc
2 |
3 | [](https://travis-ci.org/RitwikSaikia/jsonrpc)
4 |
5 | JSON-RPC is a Java library implementing a very light weight client/server functionality of JSON-RPC protocol.
6 | Server/Client API is designed in such a way that, you don't have to worry about the details of the protocol.
7 | Just register the implementation classes in the server side, and create remote proxy objects at the client sides on the fly.
8 |
9 | The API works well in **Android/Google App Engine/Javascript** applications.
10 |
11 |
12 | # Usage
13 |
14 | ## Defining Interfaces
15 |
16 | Lets pickup an example of a Calculator service.
17 |
18 | The client side interface will be
19 | ```java
20 | public interface Calculator {
21 |
22 | double add(double x, double y);
23 |
24 | double multiply(double x, double y);
25 |
26 | }
27 | ```
28 |
29 | The server side implementation will be
30 | ```java
31 | public class SimpleCalculatorImpl implements Calculator {
32 |
33 | public double add(double x, double y) {
34 | return x + y;
35 | }
36 |
37 | public double multiply(double x, double y) {
38 | return x * y;
39 | }
40 |
41 | }
42 | ```
43 |
44 | ## Hosting the service
45 |
46 | ### Binding Service Implementation
47 |
48 | Once the service is ready, it needs to be bound to the JSON-RPC Server to make it available.
49 | ```java
50 | private JsonRpcExecutor bind() {
51 | JsonRpcExecutor executor = new JsonRpcExecutor();
52 |
53 | Calculator calcImpl = new SimpleCalculatorImpl();
54 | executor.addHandler("calc", calcImpl, Calculator.class);
55 |
56 | // add more services here
57 |
58 | return executor;
59 | }
60 | ```
61 |
62 | ### Hosting with a Servlet
63 | ```
64 | public class JsonRpcServlet extends HttpServlet {
65 |
66 | private final JsonRpcExecutor executor;
67 |
68 | public JsonRpcServlet() {
69 | executor = bind();
70 | }
71 |
72 | private JsonRpcExecutor bind() {
73 | JsonRpcExecutor executor = new JsonRpcExecutor();
74 |
75 | Calculator calcImpl = new SimpleCalculatorImpl();
76 | executor.addHandler("calc", calcImpl, Calculator.class);
77 | // add more services here
78 |
79 | return executor;
80 | }
81 |
82 | protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
83 | executor.execute(new JsonRpcServletTransport(req, resp));
84 | }
85 |
86 | }
87 | ```
88 |
89 | ## Calling the service
90 |
91 | ### Call from Java Client
92 |
93 | ```java
94 | // where the servlet is hosted
95 | String url = "http://127.0.0.1:8080/jsonrpc";
96 |
97 | HttpJsonRpcClientTransport transport = new HttpJsonRpcClientTransport(new URL(url));
98 |
99 | JsonRpcInvoker invoker = new JsonRpcInvoker();
100 | Calculator calc = invoker.get(transport, "calc", Calculator.class);
101 |
102 | double result = calc.add(1.2, 7.5);
103 | ```
104 |
105 | **Exception Handling**
106 | * In case of remote exception it throws a JsonRpcRemoteException
107 | * invalid request
108 | * unable to parse json response
109 | * unknown method
110 | * any custom exception thrown by the application (SimpleCalculator) will appear with a full stack trace at the client side.
111 | * In case of local exception it throws a JsonRpcClientException
112 | * unable to reach service end point
113 | * unable to parse json response
114 |
115 | ### Call from JavaScript Client
116 |
117 | ```javascript
118 |
119 |
147 | ```
148 |
149 | **Exception Handling**
150 | In case of remote exception it throws an _Error_
151 |
152 | ## Dependencies
153 | * [Gson 1.4](http://code.google.com/p/google-gson/)
154 | * [SLF4J 1.5.8](http://www.slf4j.org/)
155 |
156 | ### Maven
157 | ```xml
158 |
159 |
160 | org.json.rpc
161 | jsonrpc
162 | 1.1
163 |
164 |
165 |
166 |
167 |
168 | ritwik-mvn-repo
169 | http://ritwik.net/mvn/releases/
170 |
171 |
172 | ```
173 | Note: classifier = server/client can be used to include only specific implementations.
174 |
175 | ## Logging
176 | JSON-RPC uses SLF4J for logging, so you will have to include specific implementation based on your requirement
177 |
178 | ### No Logging
179 | ```xml
180 |
181 | org.slf4j
182 | slf4j-nop
183 | 1.5.8
184 |
185 | ```
186 |
187 | ### Log4j
188 | ```xml
189 |
190 | org.slf4j
191 | slf4j-log4j12
192 | 1.5.8
193 |
194 |
195 |
196 | log4j
197 | log4j
198 | 1.2.16
199 |
200 | ```
201 |
202 | ### Android
203 | ```xml
204 |
205 | org.json.rpc
206 | jsonrpc
207 | 1.0
208 | client
209 |
210 |
211 | org.slf4j
212 | slf4j-api
213 |
214 |
215 |
216 |
217 |
218 | org.slf4j
219 | slf4j-android
220 | 1.6.1-RC1
221 |
222 | ```
223 |
--------------------------------------------------------------------------------
/jsonrpc-java/build/bump-version.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python
2 |
3 | import subprocess
4 | import sys
5 | import os
6 |
7 | def main(args):
8 | os.chdir("../")
9 |
10 | plugin="org.apache.maven.plugins:maven-release-plugin:2.3.2"
11 |
12 | mvn = "mvn"
13 | update = plugin + ":update-versions"
14 |
15 | cmd = [mvn, update, "-DautoVersionSubmodules=true", "-B"]
16 | if len(args) > 1:
17 | if len(args) > 2:
18 | usage()
19 | return
20 | else:
21 | cmd.append("-DdevelopmentVersion=%s" % args[1])
22 |
23 | subprocess.Popen(cmd).communicate()
24 |
25 | def usage():
26 | print "Usage bump-version.sh []"
27 | sys.exit()
28 |
29 |
30 | main(sys.argv)
31 |
32 |
--------------------------------------------------------------------------------
/jsonrpc-java/build/merge.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python
2 |
3 | import subprocess
4 | import sys
5 | import os
6 | import re
7 |
8 | def exec_cmd(cmd):
9 | p = subprocess.Popen(cmd)
10 | p.communicate()
11 | return p.wait()
12 |
13 | def main(args):
14 | os.chdir("../")
15 |
16 | git = "git"
17 |
18 | cmd = [git, "rev-parse", "--abbrev-ref", "HEAD"]
19 | p = subprocess.Popen(cmd, stdout=subprocess.PIPE)
20 | branch = p.stdout.readline().strip()
21 | code = p.wait()
22 | if code != 0:
23 | print "unable to detect git branch"
24 | sys.exit(1)
25 |
26 | if not re.search("^(release|hotfix)-", branch):
27 | print "not on release or hotfix branch"
28 | sys.exit(1)
29 |
30 | cmd = [git, "checkout", "develop"]
31 | code = exec_cmd(cmd)
32 | if code != 0:
33 | print "unable to checkout 'develop'"
34 | sys.exit(1)
35 |
36 | cmd = [git, "merge", "--no-ff", branch]
37 | code = exec_cmd(cmd)
38 | if code != 0:
39 | print "unable to merge '" + branch + "' to 'develop'"
40 | sys.exit(1)
41 |
42 | cmd = [git, "checkout", "master"]
43 | code = exec_cmd(cmd)
44 | if code != 0:
45 | print "unable to checkout 'master'"
46 | sys.exit(1)
47 |
48 | cmd = [git, "merge", "--no-ff", branch + "~1"]
49 | code = exec_cmd(cmd)
50 | if code != 0:
51 | print "unable to merge '" + branch + "~1' to 'master'"
52 | sys.exit(1)
53 |
54 | cmd = [git, "checkout", branch]
55 | code = exec_cmd(cmd)
56 | if code != 0:
57 | print "unable to checkout '" + branch + "'"
58 | sys.exit(1)
59 |
60 |
61 |
62 | main(sys.argv)
63 |
64 |
--------------------------------------------------------------------------------
/jsonrpc-java/build/release.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python
2 |
3 | import subprocess
4 | import sys
5 | import os
6 | import re
7 |
8 | def exec_cmd(cmd):
9 | p = subprocess.Popen(cmd)
10 | p.communicate()
11 | return p.wait()
12 |
13 | def main(args):
14 | os.chdir("../")
15 |
16 | plugin="org.apache.maven.plugins:maven-release-plugin:2.3.2"
17 | mvn = "mvn"
18 | git = "git"
19 |
20 | prepare = plugin + ":prepare"
21 | perform = plugin + ":perform"
22 |
23 | cmd = [git, "rev-parse", "--abbrev-ref", "HEAD"]
24 | p = subprocess.Popen(cmd, stdout=subprocess.PIPE)
25 | branch = p.stdout.readline().strip()
26 | code = p.wait()
27 | if code != 0:
28 | print "unable to detect git branch"
29 | sys.exit(1)
30 |
31 | if not re.search("^(release|hotfix)-", branch):
32 | print "not on release or hotfix branch"
33 | sys.exit(1)
34 |
35 | cmd = [mvn, prepare, "-DdryRun=true", "-B"]
36 | code = exec_cmd(cmd)
37 | if code != 0:
38 | print "unable to run release dryrun"
39 | sys.exit(1)
40 |
41 | cmd = [mvn, prepare, "-B"]
42 | code = exec_cmd(cmd)
43 | if code != 0:
44 | print "unable to release"
45 | sys.exit(1)
46 |
47 |
48 | cmd = [mvn, perform, "-B"]
49 | code = exec_cmd(cmd)
50 | if code != 0:
51 | print "unable to perform"
52 | sys.exit(1)
53 |
54 | main(sys.argv)
55 |
56 |
--------------------------------------------------------------------------------
/jsonrpc-java/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 4.0.0
4 | org.json.rpc
5 | jsonrpc
6 | JsonRpc
7 | 1.1
8 |
9 |
10 | 1.5
11 | UTF-8
12 | UTF-8
13 |
14 |
15 |
16 |
17 | com.google.code.gson
18 | gson
19 | 2.2.2
20 |
21 |
22 |
23 | javax
24 | javaee-api
25 | 6.0
26 | provided
27 |
28 |
29 |
30 |
31 | org.slf4j
32 | slf4j-api
33 | 1.5.8
34 |
35 |
36 |
37 | org.slf4j
38 | slf4j-nop
39 | 1.5.8
40 | provided
41 |
42 |
43 |
44 |
45 | org.slf4j
46 | slf4j-log4j12
47 | 1.5.8
48 | test
49 |
50 |
51 | org.testng
52 | testng
53 | 5.8
54 | test
55 | jdk15
56 |
57 |
58 | org.easymock
59 | easymock
60 | 2.4
61 | test
62 |
63 |
64 | org.easymock
65 | easymockclassextension
66 | 2.4
67 | test
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 | org.apache.maven.plugins
76 | maven-compiler-plugin
77 | 2.0
78 |
79 | ${java.version}
80 | ${java.version}
81 |
82 |
83 |
84 |
85 | org.apache.maven.plugins
86 | maven-jar-plugin
87 | 2.3.1
88 |
89 |
90 | all-jar
91 | package
92 |
93 | jar
94 |
95 |
96 |
97 | client-jar
98 | package
99 |
100 | jar
101 |
102 |
103 | client
104 |
105 | true
106 |
107 |
108 | org/json/rpc/commons/**
109 | org/json/rpc/client/**
110 |
111 |
112 |
113 |
114 | server-jar
115 | package
116 |
117 | jar
118 |
119 |
120 | server
121 |
122 | true
123 |
124 |
125 | org/json/rpc/commons/**
126 | org/json/rpc/server/**
127 |
128 |
129 |
130 |
131 |
132 |
133 | true
134 |
135 |
136 |
137 |
138 |
139 | org.apache.maven.plugins
140 | maven-source-plugin
141 | 2.1.2
142 |
143 |
144 | package
145 |
146 | jar
147 |
148 |
149 |
150 |
151 |
152 | org.apache.maven.plugins
153 | maven-release-plugin
154 | 2.3.2
155 |
156 | jsonrpc-@{project.version}
157 | true
158 | false
159 | false
160 | false
161 | install
162 |
163 |
164 |
165 |
166 | org.apache.maven.plugins
167 | maven-javadoc-plugin
168 | 2.7
169 |
170 |
171 | package
172 |
173 | jar
174 |
175 |
176 |
177 |
178 |
179 | http://download.oracle.com/javase/1.5.0/docs/api/
180 |
181 | true
182 | public
183 |
184 |
185 |
186 | maven-assembly-plugin
187 | 2.2
188 |
189 |
190 | package
191 |
192 | single
193 |
194 |
195 |
196 |
197 | src/main/assembly/assembly-descriptor.xml
198 | ${project.artifactId}-${project.version}
199 | false
200 |
201 |
202 |
203 | org.codehaus.mojo
204 | cobertura-maven-plugin
205 | 2.5.2
206 |
207 |
208 | false
209 | 60
210 | 75
211 | 60
212 | 75
213 | 75
214 | 60
215 |
216 |
217 |
218 | **/*Exception.class
219 | **/*Constants.class
220 |
221 |
222 |
223 |
224 |
225 | package
226 |
227 | check
228 |
229 |
230 |
231 |
232 |
233 |
234 |
235 |
236 |
237 |
238 | scm:git:https://github.com/RitwikSaikia/jsonrpc.git
239 | scm:git:git@github.com:RitwikSaikia/jsonrpc.git
240 | https://github.com/RitwikSaikia/jsonrpc
241 | HEAD
242 |
243 |
244 |
245 |
246 | ritwik-mvn-repo
247 | http://ritwik.net/mvn/releases/
248 |
249 | true
250 | always
251 | warn
252 |
253 |
254 | false
255 | never
256 | fail
257 |
258 |
259 |
260 |
261 |
--------------------------------------------------------------------------------
/jsonrpc-java/src/main/assembly/assembly-descriptor.xml:
--------------------------------------------------------------------------------
1 |
2 | release
3 |
4 | zip
5 |
6 |
7 |
8 | ../
9 |
10 |
11 | LICENSE.txt
12 |
13 |
14 |
15 | target
16 |
17 |
18 | jsonrpc-*.jar
19 |
20 |
21 |
22 | target/apidocs
23 | docs/javadocs
24 |
25 | **
26 |
27 |
28 |
29 |
30 |
31 | lib
32 |
33 | org.json.rpc:jsonrpc
34 |
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/jsonrpc-java/src/main/java/org/json/rpc/client/HttpJsonRpcClientTransport.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2011 ritwik.net
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package org.json.rpc.client;
18 |
19 | import org.json.rpc.commons.JsonRpcClientException;
20 |
21 | import java.io.BufferedInputStream;
22 | import java.io.ByteArrayOutputStream;
23 | import java.io.IOException;
24 | import java.io.InputStream;
25 | import java.io.OutputStream;
26 | import java.net.HttpURLConnection;
27 | import java.net.URL;
28 | import java.util.HashMap;
29 | import java.util.Map;
30 | import java.util.zip.GZIPInputStream;
31 |
32 | public class HttpJsonRpcClientTransport implements JsonRpcClientTransport {
33 |
34 | private URL url;
35 | private final Map headers;
36 |
37 | public HttpJsonRpcClientTransport(URL url) {
38 | this.url = url;
39 | this.headers = new HashMap();
40 | }
41 |
42 | public final void setHeader(String key, String value) {
43 | this.headers.put(key, value);
44 | }
45 |
46 | public final String call(String requestData) throws Exception {
47 | String responseData = post(url, headers, requestData);
48 | return responseData;
49 | }
50 |
51 | private String post(URL url, Map headers, String data)
52 | throws IOException {
53 |
54 | HttpURLConnection connection = (HttpURLConnection) url.openConnection();
55 |
56 | if (headers != null) {
57 | for (Map.Entry entry : headers.entrySet()) {
58 | connection.addRequestProperty(entry.getKey(), entry.getValue());
59 | }
60 | }
61 |
62 | connection.addRequestProperty("Accept-Encoding", "gzip");
63 |
64 | connection.setRequestMethod("POST");
65 | connection.setDoOutput(true);
66 | connection.connect();
67 |
68 | OutputStream out = null;
69 |
70 | try {
71 | out = connection.getOutputStream();
72 |
73 | out.write(data.getBytes());
74 | out.flush();
75 | out.close();
76 |
77 | int statusCode = connection.getResponseCode();
78 | if (statusCode != HttpURLConnection.HTTP_OK) {
79 | throw new JsonRpcClientException("unexpected status code returned : " + statusCode);
80 | }
81 | } finally {
82 | if (out != null) {
83 | out.close();
84 | }
85 | }
86 |
87 | String responseEncoding = connection.getHeaderField("Content-Encoding");
88 | responseEncoding = (responseEncoding == null ? "" : responseEncoding.trim());
89 |
90 | ByteArrayOutputStream bos = new ByteArrayOutputStream();
91 |
92 | InputStream in = connection.getInputStream();
93 | try {
94 | in = connection.getInputStream();
95 | if ("gzip".equalsIgnoreCase(responseEncoding)) {
96 | in = new GZIPInputStream(in);
97 | }
98 | in = new BufferedInputStream(in);
99 |
100 | byte[] buff = new byte[1024];
101 | int n;
102 | while ((n = in.read(buff)) > 0) {
103 | bos.write(buff, 0, n);
104 | }
105 | bos.flush();
106 | bos.close();
107 | } finally {
108 | if (in != null) {
109 | in.close();
110 | }
111 | }
112 |
113 | return bos.toString();
114 | }
115 | }
116 |
--------------------------------------------------------------------------------
/jsonrpc-java/src/main/java/org/json/rpc/client/JsonRpcClientTransport.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2011 ritwik.net
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package org.json.rpc.client;
18 |
19 | public interface JsonRpcClientTransport {
20 |
21 | String call(String requestData) throws Exception;
22 |
23 | }
24 |
--------------------------------------------------------------------------------
/jsonrpc-java/src/main/java/org/json/rpc/client/JsonRpcInvoker.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2011 ritwik.net
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package org.json.rpc.client;
18 |
19 | import com.google.gson.Gson;
20 | import com.google.gson.JsonArray;
21 | import com.google.gson.JsonElement;
22 | import com.google.gson.JsonObject;
23 | import com.google.gson.JsonParser;
24 | import org.json.rpc.commons.GsonTypeChecker;
25 | import org.json.rpc.commons.JsonRpcClientException;
26 | import org.json.rpc.commons.JsonRpcRemoteException;
27 | import org.json.rpc.commons.TypeChecker;
28 | import org.slf4j.Logger;
29 | import org.slf4j.LoggerFactory;
30 |
31 | import java.io.StringReader;
32 | import java.lang.reflect.InvocationHandler;
33 | import java.lang.reflect.Method;
34 | import java.lang.reflect.Proxy;
35 | import java.util.Random;
36 |
37 | public final class JsonRpcInvoker {
38 |
39 | private static final Logger LOG = LoggerFactory.getLogger(JsonRpcInvoker.class);
40 |
41 | private final Random rand = new Random();
42 |
43 | private final TypeChecker typeChecker;
44 |
45 | private final Gson gson;
46 |
47 | public JsonRpcInvoker() {
48 | this(new GsonTypeChecker(), new Gson());
49 | }
50 |
51 | public JsonRpcInvoker(Gson gson) {
52 | this(new GsonTypeChecker(), gson);
53 | }
54 |
55 | public JsonRpcInvoker(TypeChecker typeChecker) {
56 | this(typeChecker, new Gson());
57 | }
58 |
59 | public JsonRpcInvoker(TypeChecker typeChecker, Gson gson) {
60 | this.typeChecker = typeChecker;
61 | this.gson = gson;
62 | }
63 |
64 | public T get(final JsonRpcClientTransport transport, final String handle, final Class... classes) {
65 | for (Class clazz : classes) {
66 | typeChecker.isValidInterface(clazz);
67 | }
68 | return (T) Proxy.newProxyInstance(JsonRpcInvoker.class.getClassLoader(), classes, new InvocationHandler() {
69 | public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
70 | return JsonRpcInvoker.this.invoke(handle, transport, method, args);
71 | }
72 | });
73 | }
74 |
75 | private Object invoke(String handleName,
76 | JsonRpcClientTransport transport, Method method,
77 | Object[] args) throws Throwable {
78 | int id = rand.nextInt(Integer.MAX_VALUE);
79 | String methodName = handleName + "." + method.getName();
80 |
81 | JsonObject req = new JsonObject();
82 | req.addProperty("id", id);
83 | req.addProperty("method", methodName);
84 |
85 | JsonArray params = new JsonArray();
86 | if (args != null) {
87 | for (Object o : args) {
88 | params.add(gson.toJsonTree(o));
89 | }
90 | }
91 | req.add("params", params);
92 |
93 | String requestData = req.toString();
94 | LOG.debug("JSON-RPC >> {}", requestData);
95 | String responseData;
96 | try {
97 | responseData = transport.call(requestData);
98 | } catch (Exception e) {
99 | throw new JsonRpcClientException("unable to get data from transport", e);
100 | }
101 | LOG.debug("JSON-RPC << {}", responseData);
102 |
103 | JsonParser parser = new JsonParser();
104 | JsonObject resp = (JsonObject) parser.parse(new StringReader(responseData));
105 |
106 | JsonElement result = resp.get("result");
107 | JsonElement error = resp.get("error");
108 |
109 | if (error != null && !error.isJsonNull()) {
110 | if (error.isJsonPrimitive()) {
111 | throw new JsonRpcRemoteException(error.getAsString());
112 | } else if (error.isJsonObject()) {
113 | JsonObject o = error.getAsJsonObject();
114 | Integer code = (o.has("code") ? o.get("code").getAsInt() : null);
115 | String message = (o.has("message") ? o.get("message").getAsString() : null);
116 | String data = (o.has("data") ? (o.get("data") instanceof JsonObject ? o.get("data").toString() : o.get("data").getAsString()) : null);
117 | throw new JsonRpcRemoteException(code, message, data);
118 | } else {
119 | throw new JsonRpcRemoteException("unknown error, data = " + error.toString());
120 | }
121 | }
122 |
123 | if (method.getReturnType() == void.class) {
124 | return null;
125 | }
126 |
127 | return gson.fromJson(result.toString(), method.getReturnType());
128 | }
129 | }
130 |
--------------------------------------------------------------------------------
/jsonrpc-java/src/main/java/org/json/rpc/commons/AllowAllTypeChecker.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2011 ritwik.net
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package org.json.rpc.commons;
18 |
19 | import java.lang.reflect.Method;
20 |
21 | public class AllowAllTypeChecker extends TypeChecker {
22 |
23 | public boolean isValidType(Class> clazz) {
24 | return true;
25 | }
26 |
27 | public boolean isValidType(Class> clazz, boolean throwException) {
28 | return true;
29 | }
30 |
31 | public String getTypeName(Class> clazz) {
32 | return clazz.getName();
33 | }
34 |
35 | public boolean isValidMethod(Method method) {
36 | return true;
37 | }
38 |
39 | public boolean isValidInterface(Class> clazz) {
40 | return true;
41 | }
42 |
43 | public boolean isValidMethod(Method method, boolean throwException) {
44 | return true;
45 | }
46 |
47 | public boolean isValidInterface(Class> clazz, boolean throwException) {
48 | return true;
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/jsonrpc-java/src/main/java/org/json/rpc/commons/GsonTypeChecker.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2011 ritwik.net
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package org.json.rpc.commons;
18 |
19 | import java.lang.reflect.Constructor;
20 | import java.lang.reflect.Field;
21 | import java.lang.reflect.Modifier;
22 | import java.util.Date;
23 | import java.util.HashSet;
24 | import java.util.Set;
25 |
26 | public class GsonTypeChecker extends TypeChecker {
27 |
28 | @Override
29 | public boolean isValidType(Class> clazz, boolean throwException) {
30 | return isValidType(clazz, throwException, null);
31 | }
32 |
33 | private boolean isValidType(Class> clazz, boolean throwException, Set> visited) {
34 | if (clazz.isPrimitive()) {
35 | return true;
36 | }
37 |
38 | if (Boolean.class == clazz) {
39 | return true;
40 | }
41 |
42 | if (Number.class.isAssignableFrom(clazz)) {
43 | return true;
44 | }
45 |
46 | if (String.class == clazz) {
47 | return true;
48 | }
49 |
50 | if (Character.class == clazz) {
51 | return true;
52 | }
53 |
54 | if (Date.class == clazz) {
55 | return true;
56 | }
57 |
58 | if (clazz.isArray()) {
59 | return this.isValidType(clazz.getComponentType(), throwException, visited);
60 | }
61 |
62 | /**
63 | * False cases
64 | */
65 |
66 | if (clazz.isAnonymousClass()) {
67 | if (throwException) {
68 | throw new IllegalArgumentException("anonymous class not allowed : " + clazz);
69 | }
70 | return false;
71 | }
72 |
73 | if (Modifier.isInterface(clazz.getModifiers()) || Modifier.isAbstract(clazz.getModifiers())) {
74 | if (throwException) {
75 | throw new IllegalArgumentException("abstract class or interface not allowed : " + clazz);
76 | }
77 | return false;
78 | }
79 |
80 | if (clazz.getTypeParameters().length > 0) {
81 | if (throwException) {
82 | throw new IllegalArgumentException("parametrized classes not allowed : " + clazz);
83 | }
84 | return false;
85 | }
86 |
87 | boolean zeroArgConstructor = (clazz.getConstructors().length == 0);
88 | for (Constructor c : clazz.getConstructors()) {
89 | if (c.getParameterTypes().length == 0) {
90 | zeroArgConstructor = true;
91 | break;
92 | }
93 | }
94 |
95 | if (!zeroArgConstructor) {
96 | if (throwException) {
97 | throw new IllegalArgumentException("no zero-arg constructor found : " + clazz);
98 | }
99 | return false;
100 | }
101 |
102 | // avoid cyclic references
103 | // Issue #6: Be more lenient and allow more types,
104 | // let the developer handle StackOverFlowError
105 | // in case of cycles
106 | visited = (visited == null ? new HashSet>() : visited);
107 | if (visited.contains(clazz)) {
108 | return true;
109 | }
110 | visited.add(clazz);
111 |
112 | // Check for fields because Gson uses fields
113 | for (Field f : clazz.getDeclaredFields()) {
114 | int m = f.getModifiers();
115 | if (Modifier.isStatic(m) || Modifier.isTransient(m)) {
116 | continue;
117 | }
118 |
119 | if (Modifier.isFinal(m)) {
120 | if (throwException) {
121 | throw new IllegalArgumentException("final field found : " + f);
122 | }
123 | return false;
124 | }
125 |
126 | boolean result = false;
127 | try {
128 | result = isValidType(f.getType(), throwException, visited);
129 | if (!result) {
130 | if (throwException) {
131 | throw new IllegalArgumentException("invalid field found : " + f);
132 | }
133 | return false;
134 | }
135 | } catch (RuntimeException e) {
136 | if (!result) {
137 | if (throwException) {
138 | throw new IllegalArgumentException("invalid field found : " + f, e);
139 | }
140 | return false;
141 | }
142 | }
143 | }
144 |
145 |
146 | return true;
147 | }
148 |
149 | @Override
150 | public String getTypeName(Class> clazz) {
151 | if (clazz == void.class || clazz == Void.class) {
152 | return void.class.getName();
153 | }
154 |
155 | if (clazz == boolean.class || Boolean.class == clazz) {
156 | return boolean.class.getName();
157 | }
158 |
159 | if (clazz == double.class || clazz == float.class
160 | || Double.class == clazz || Float.class == clazz) {
161 | return double.class.getName();
162 | }
163 |
164 | if (clazz == byte.class || clazz == char.class || clazz == int.class || clazz == short.class
165 | || clazz == long.class || clazz == Character.class || Number.class.isAssignableFrom(clazz)) {
166 | return int.class.getName();
167 | }
168 |
169 | if (clazz == String.class) {
170 | return "string";
171 | }
172 |
173 | if (clazz.isArray()) {
174 | return "array";
175 | }
176 |
177 | return "struct";
178 | }
179 | }
180 |
--------------------------------------------------------------------------------
/jsonrpc-java/src/main/java/org/json/rpc/commons/JsonRpcClientException.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2011 ritwik.net
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package org.json.rpc.commons;
18 |
19 | public class JsonRpcClientException extends JsonRpcException {
20 | public JsonRpcClientException(String message) {
21 | super(message);
22 | }
23 |
24 | public JsonRpcClientException(String message, Throwable cause) {
25 | super(message, cause);
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/jsonrpc-java/src/main/java/org/json/rpc/commons/JsonRpcErrorCodes.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2011 ritwik.net
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package org.json.rpc.commons;
18 |
19 | public final class JsonRpcErrorCodes {
20 |
21 | public static final int PARSE_ERROR_CODE = -32700;
22 | public static final int INVALID_REQUEST_ERROR_CODE = -32600;
23 | public static final int METHOD_NOT_FOUND_ERROR_CODE = -32601;
24 | public static final int INVALID_PARAMS_ERROR_CODE = -32602;
25 | public static final int INTERNAL_ERROR_CODE = -32603;
26 |
27 | private static final int SERVER_ERROR_START = -32000;
28 |
29 |
30 | /**
31 | * Server error range : (-32099..-32000)
32 | */
33 |
34 | public static int getServerError(int n) {
35 | return SERVER_ERROR_START - n;
36 | }
37 |
38 | private JsonRpcErrorCodes() {
39 | throw new AssertionError();
40 | }
41 |
42 | }
43 |
--------------------------------------------------------------------------------
/jsonrpc-java/src/main/java/org/json/rpc/commons/JsonRpcException.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2011 ritwik.net
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package org.json.rpc.commons;
18 |
19 | public class JsonRpcException extends RuntimeException {
20 |
21 | public JsonRpcException(String message) {
22 | super(message);
23 | }
24 |
25 | public JsonRpcException(String message, Throwable cause) {
26 | super(message, cause);
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/jsonrpc-java/src/main/java/org/json/rpc/commons/JsonRpcRemoteException.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2011 ritwik.net
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package org.json.rpc.commons;
18 |
19 | public final class JsonRpcRemoteException extends JsonRpcException {
20 |
21 | private final Integer code;
22 | private final String msg;
23 | private final String data;
24 |
25 | public JsonRpcRemoteException(String msg) {
26 | super(msg);
27 | this.code = null;
28 | this.msg = msg;
29 | this.data = null;
30 | }
31 |
32 | public JsonRpcRemoteException(Integer code, String msg, String data) {
33 | super(format(code, msg, data));
34 | this.code = code;
35 | this.msg = msg;
36 | this.data = data;
37 | }
38 |
39 | public Integer getCode() {
40 | return code;
41 | }
42 |
43 | public String getMsg() {
44 | return msg;
45 | }
46 |
47 | public String getData() {
48 | return data;
49 | }
50 |
51 | private static String format(Integer code, String message, String data) {
52 | StringBuilder str = new StringBuilder();
53 | str.append("jsonrpc error");
54 | if (code != null) {
55 | str.append("[").append(code).append("]");
56 | }
57 | str.append(" : ");
58 | if (message != null) {
59 | str.append(message);
60 | }
61 | if (data != null) {
62 | str.append("\n");
63 | str.append("Caused by " + data);
64 | }
65 | return str.toString();
66 | }
67 |
68 | }
69 |
--------------------------------------------------------------------------------
/jsonrpc-java/src/main/java/org/json/rpc/commons/RpcIntroSpection.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2011 ritwik.net
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package org.json.rpc.commons;
18 |
19 | public interface RpcIntroSpection {
20 |
21 | String[] listMethods();
22 |
23 | String[] methodSignature(String method);
24 |
25 | }
26 |
--------------------------------------------------------------------------------
/jsonrpc-java/src/main/java/org/json/rpc/commons/TypeChecker.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2011 ritwik.net
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package org.json.rpc.commons;
18 |
19 | import java.lang.reflect.Method;
20 | import java.lang.reflect.Modifier;
21 |
22 | public abstract class TypeChecker {
23 |
24 | public boolean isValidType(Class> clazz) {
25 | return isValidType(clazz, false);
26 | }
27 |
28 | public abstract boolean isValidType(Class> clazz, boolean throwException);
29 |
30 | public abstract String getTypeName(Class> clazz);
31 |
32 | public boolean isValidMethod(Method method) {
33 | return isValidMethod(method, false);
34 | }
35 |
36 | public boolean isValidInterface(Class> clazz) {
37 | return isValidInterface(clazz, false);
38 | }
39 |
40 | public boolean isValidMethod(Method method, boolean throwException) {
41 | Class> returnType = method.getReturnType();
42 | boolean result = false;
43 | try {
44 | result = isValidType(returnType, throwException);
45 | if (!result) {
46 | if (throwException) {
47 | throw new IllegalArgumentException("invalid return type : " + returnType);
48 | }
49 | return false;
50 | }
51 | } catch (RuntimeException e) {
52 | if (!result) {
53 | if (throwException) {
54 | throw new IllegalArgumentException("invalid return type : " + returnType, e);
55 | }
56 | return false;
57 | }
58 | }
59 |
60 | for (Class> paramType : method.getParameterTypes()) {
61 | result = false;
62 | try {
63 | result = isValidType(paramType, throwException);
64 | if (!result) {
65 | if (throwException) {
66 | throw new IllegalArgumentException("invalid parameter type : " + paramType);
67 | }
68 | return false;
69 | }
70 | } catch (RuntimeException e) {
71 | if (!result) {
72 | if (throwException) {
73 | throw new IllegalArgumentException("invalid parameter type : " + paramType, e);
74 | }
75 | return false;
76 | }
77 | }
78 | }
79 |
80 | return true;
81 | }
82 |
83 | public boolean isValidInterface(Class> clazz, boolean throwException) {
84 | if (!clazz.isInterface()) {
85 | if (throwException) {
86 | throw new IllegalArgumentException("not an interface : " + clazz);
87 | }
88 | return false;
89 | }
90 |
91 | for (Method method : clazz.getDeclaredMethods()) {
92 | int m = method.getModifiers();
93 | if (Modifier.isStatic(m)) {
94 | continue;
95 | }
96 |
97 | boolean result = false;
98 | try {
99 | result = isValidMethod(method, throwException);
100 | if (!result) {
101 | if (throwException) {
102 | throw new IllegalArgumentException("invalid method : " + method);
103 | }
104 | return false;
105 | }
106 | } catch (RuntimeException e) {
107 | if (!result) {
108 | if (throwException) {
109 | throw new IllegalArgumentException("invalid method : " + method, e);
110 | }
111 | return false;
112 | }
113 | }
114 | }
115 |
116 | return true;
117 | }
118 |
119 | }
120 |
--------------------------------------------------------------------------------
/jsonrpc-java/src/main/java/org/json/rpc/server/HandleEntry.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2011 ritwik.net
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package org.json.rpc.server;
18 |
19 | import org.json.rpc.commons.TypeChecker;
20 |
21 | import java.lang.reflect.Method;
22 | import java.util.ArrayList;
23 | import java.util.Collections;
24 | import java.util.HashMap;
25 | import java.util.HashSet;
26 | import java.util.List;
27 | import java.util.Map;
28 | import java.util.Set;
29 | import java.util.TreeMap;
30 |
31 | class HandleEntry {
32 |
33 | private final T handler;
34 | private final Map signatures;
35 | private final Set methods;
36 |
37 | public HandleEntry(TypeChecker typeChecker, T handler, Class... classes) {
38 | if (handler == null) {
39 | throw new NullPointerException("handler");
40 | }
41 |
42 | if (classes.length == 0) {
43 | throw new IllegalArgumentException(
44 | "at least one interface has to be mentioned");
45 | }
46 |
47 | this.handler = handler;
48 |
49 | Map> map = new HashMap>();
50 | Set set = new HashSet();
51 |
52 | for (Class> clazz : classes) {
53 | typeChecker.isValidInterface(clazz, true);
54 |
55 | if (!clazz.isInterface()) {
56 | throw new IllegalArgumentException(
57 | "class should be an interface : " + clazz);
58 | }
59 |
60 | for (Method m : clazz.getMethods()) {
61 | set.add(m);
62 | Class>[] params = m.getParameterTypes();
63 |
64 | List list = map.get(m.getName());
65 | if (list == null) {
66 | list = new ArrayList();
67 | }
68 | StringBuffer buff = new StringBuffer(typeChecker.getTypeName(m
69 | .getReturnType()));
70 | for (int i = 0; i < params.length; i++) {
71 | buff.append(",").append(typeChecker.getTypeName(params[i]));
72 | }
73 | list.add(buff.toString());
74 | map.put(m.getName(), list);
75 | }
76 |
77 | }
78 |
79 | Map signs = new TreeMap();
80 | for (Map.Entry> e : map.entrySet()) {
81 | String[] arr = new String[e.getValue().size()];
82 | signs.put(e.getKey(), e.getValue().toArray(arr));
83 | }
84 |
85 | this.methods = Collections.unmodifiableSet(set);
86 | this.signatures = Collections.unmodifiableMap(signs);
87 | }
88 |
89 | public T getHandler() {
90 | return handler;
91 | }
92 |
93 | public java.util.Map getSignatures() {
94 | return signatures;
95 | }
96 |
97 | public java.util.Set getMethods() {
98 | return methods;
99 | }
100 | }
101 |
--------------------------------------------------------------------------------
/jsonrpc-java/src/main/java/org/json/rpc/server/JsonRpcExecutor.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2011 ritwik.net
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package org.json.rpc.server;
18 |
19 | import com.google.gson.Gson;
20 | import com.google.gson.JsonArray;
21 | import com.google.gson.JsonElement;
22 | import com.google.gson.JsonObject;
23 | import com.google.gson.JsonParser;
24 | import org.json.rpc.commons.GsonTypeChecker;
25 | import org.json.rpc.commons.JsonRpcErrorCodes;
26 | import org.json.rpc.commons.JsonRpcException;
27 | import org.json.rpc.commons.JsonRpcRemoteException;
28 | import org.json.rpc.commons.RpcIntroSpection;
29 | import org.json.rpc.commons.TypeChecker;
30 | import org.slf4j.Logger;
31 | import org.slf4j.LoggerFactory;
32 |
33 | import java.io.PrintWriter;
34 | import java.io.StringReader;
35 | import java.io.StringWriter;
36 | import java.lang.reflect.InvocationTargetException;
37 | import java.lang.reflect.Method;
38 | import java.util.ArrayList;
39 | import java.util.HashMap;
40 | import java.util.List;
41 | import java.util.Map;
42 | import java.util.Set;
43 | import java.util.TreeSet;
44 | import java.util.regex.Matcher;
45 | import java.util.regex.Pattern;
46 |
47 |
48 | public final class JsonRpcExecutor implements RpcIntroSpection {
49 |
50 | private static final Logger LOG = LoggerFactory.getLogger(JsonRpcExecutor.class);
51 |
52 | private static final Pattern METHOD_PATTERN = Pattern
53 | .compile("([_a-zA-Z][_a-zA-Z0-9]*)\\.([_a-zA-Z][_a-zA-Z0-9]*)");
54 |
55 | private final Map> handlers;
56 |
57 | private final TypeChecker typeChecker;
58 | private volatile boolean locked;
59 |
60 | private final Gson gson;
61 |
62 | public JsonRpcExecutor() {
63 | this(new GsonTypeChecker(), new Gson());
64 | }
65 |
66 | public JsonRpcExecutor(Gson gson) {
67 | this(new GsonTypeChecker(), gson);
68 | }
69 |
70 | public JsonRpcExecutor(TypeChecker typeChecker) {
71 | this(typeChecker, new Gson());
72 | }
73 |
74 | @SuppressWarnings("unchecked")
75 | public JsonRpcExecutor(TypeChecker typeChecker, Gson gson) {
76 | this.typeChecker = typeChecker;
77 | this.gson = gson;
78 | this.handlers = new HashMap>();
79 | addHandler("system", this, RpcIntroSpection.class);
80 | }
81 |
82 | public boolean isLocked() {
83 | return locked;
84 | }
85 |
86 | public void addHandler(String name, T handler, Class... classes) {
87 | if (locked) {
88 | throw new JsonRpcException("executor has been locked, can't add more handlers");
89 | }
90 |
91 | synchronized (handlers) {
92 | HandleEntry handleEntry = new HandleEntry(typeChecker, handler, classes);
93 | if (this.handlers.containsKey(name)) {
94 | throw new IllegalArgumentException("handler already exists");
95 | }
96 | this.handlers.put(name, handleEntry);
97 | }
98 | }
99 |
100 | public void execute(JsonRpcServerTransport transport) {
101 | if (!locked) {
102 | synchronized (handlers) {
103 | locked = true;
104 | }
105 | LOG.info("locking executor to avoid modification");
106 | }
107 |
108 | String methodName = null;
109 | JsonArray params = null;
110 |
111 | JsonObject resp = new JsonObject();
112 | resp.addProperty("jsonrpc", "2.0");
113 |
114 | String errorMessage = null;
115 | Integer errorCode = null;
116 | String errorData = null;
117 |
118 | JsonObject req = null;
119 | try {
120 | String requestData = transport.readRequest();
121 | LOG.debug("JSON-RPC >> {}", requestData);
122 | JsonParser parser = new JsonParser();
123 | req = (JsonObject) parser.parse(new StringReader(requestData));
124 | } catch (Throwable t) {
125 | errorCode = JsonRpcErrorCodes.PARSE_ERROR_CODE;
126 | errorMessage = "unable to parse json-rpc request";
127 | errorData = getStackTrace(t);
128 |
129 | LOG.warn(errorMessage, t);
130 |
131 | sendError(transport, resp, errorCode, errorMessage, errorData);
132 | return;
133 | }
134 |
135 |
136 | try {
137 | assert req != null;
138 | resp.add("id", req.get("id"));
139 |
140 | methodName = req.getAsJsonPrimitive("method").getAsString();
141 | params = (JsonArray) req.get("params");
142 | if (params == null) {
143 | params = new JsonArray();
144 | }
145 | } catch (Throwable t) {
146 | errorCode = JsonRpcErrorCodes.INVALID_REQUEST_ERROR_CODE;
147 | errorMessage = "unable to read request";
148 | errorData = getStackTrace(t);
149 |
150 |
151 | LOG.warn(errorMessage, t);
152 | sendError(transport, resp, errorCode, errorMessage, errorData);
153 | return;
154 | }
155 |
156 | try {
157 | JsonElement result = executeMethod(methodName, params);
158 | resp.add("result", result);
159 | } catch (Throwable t) {
160 | LOG.warn("exception occured while executing : " + methodName, t);
161 | if (t instanceof JsonRpcRemoteException) {
162 | sendError(transport, resp, (JsonRpcRemoteException) t);
163 | return;
164 | }
165 | errorCode = JsonRpcErrorCodes.getServerError(1);
166 | errorMessage = t.getMessage();
167 | errorData = getStackTrace(t);
168 | sendError(transport, resp, errorCode, errorMessage, errorData);
169 | return;
170 | }
171 |
172 | try {
173 | String responseData = resp.toString();
174 | LOG.debug("JSON-RPC result << {}", responseData);
175 | transport.writeResponse(responseData);
176 | } catch (Exception e) {
177 | LOG.warn("unable to write response : " + resp, e);
178 | }
179 | }
180 |
181 | private void sendError(JsonRpcServerTransport transport, JsonObject resp, JsonRpcRemoteException e) {
182 | sendError(transport, resp, e.getCode(), e.getMessage(), e.getData());
183 | }
184 |
185 | private void sendError(JsonRpcServerTransport transport, JsonObject resp, Integer code, String message, String data) {
186 | JsonObject error = new JsonObject();
187 | if (code != null) {
188 | error.addProperty("code", code);
189 | }
190 |
191 | if (message != null) {
192 | error.addProperty("message", message);
193 | }
194 |
195 | if (data != null) {
196 | error.addProperty("data", data);
197 | }
198 |
199 | resp.add("error", error);
200 | resp.remove("result");
201 | String responseData = resp.toString();
202 |
203 | LOG.debug("JSON-RPC error << {}", responseData);
204 | try {
205 | transport.writeResponse(responseData);
206 | } catch (Exception e) {
207 | LOG.error("unable to write error response : " + responseData, e);
208 | }
209 | }
210 |
211 | private String getStackTrace(Throwable t) {
212 | StringWriter str = new StringWriter();
213 | PrintWriter w = new PrintWriter(str);
214 | t.printStackTrace(w);
215 | w.close();
216 | return str.toString();
217 | }
218 |
219 | private JsonElement executeMethod(String methodName, JsonArray params) throws Throwable {
220 | try {
221 | Matcher mat = METHOD_PATTERN.matcher(methodName);
222 | if (!mat.find()) {
223 | throw new JsonRpcRemoteException(JsonRpcErrorCodes.INVALID_REQUEST_ERROR_CODE, "invalid method name", null);
224 | }
225 |
226 | String handleName = mat.group(1);
227 | methodName = mat.group(2);
228 |
229 | HandleEntry> handleEntry = handlers.get(handleName);
230 | if (handleEntry == null) {
231 | throw new JsonRpcRemoteException(JsonRpcErrorCodes.METHOD_NOT_FOUND_ERROR_CODE, "no such method exists", null);
232 | }
233 |
234 | Method executableMethod = null;
235 | for (Method m : handleEntry.getMethods()) {
236 | if (!m.getName().equals(methodName)) {
237 | continue;
238 | }
239 |
240 | if (canExecute(m, params)) {
241 | executableMethod = m;
242 | break;
243 | }
244 | }
245 |
246 | if (executableMethod == null) {
247 | throw new JsonRpcRemoteException(JsonRpcErrorCodes.METHOD_NOT_FOUND_ERROR_CODE, "no such method exists", null);
248 | }
249 |
250 | Object result = executableMethod.invoke(
251 | handleEntry.getHandler(), getParameters(executableMethod, params));
252 |
253 | return gson.toJsonTree(result);
254 | } catch (Throwable t) {
255 | if (t instanceof InvocationTargetException) {
256 | t = ((InvocationTargetException) t).getTargetException();
257 | }
258 | if (t instanceof JsonRpcRemoteException) {
259 | throw (JsonRpcRemoteException) t;
260 | }
261 | throw new JsonRpcRemoteException(JsonRpcErrorCodes.getServerError(0), t.getMessage(), getStackTrace(t));
262 | }
263 | }
264 |
265 | public boolean canExecute(Method method, JsonArray params) {
266 | if (method.getParameterTypes().length != params.size()) {
267 | return false;
268 | }
269 |
270 | return true;
271 | }
272 |
273 | public Object[] getParameters(Method method, JsonArray params) {
274 | List