001package com.thetransactioncompany.jsonrpc2; 002 003 004import java.util.Map; 005 006import net.minidev.json.JSONObject; 007 008 009/** 010 * Represents a JSON-RPC 2.0 response. 011 * 012 * <p>A response is returned to the caller after a JSON-RPC 2.0 request has 013 * been processed (notifications, however, don't produce a response). The 014 * response can take two different forms depending on the outcome: 015 * 016 * <ul> 017 * <li>The request was successful. The corresponding response returns 018 * a JSON object with the following information: 019 * <ul> 020 * <li>{@code result} The result, which can be of any JSON type 021 * - a number, a boolean value, a string, an array, an object 022 * or null. 023 * <li>{@code id} The request identifier which is echoed back back 024 * to the caller. 025 * <li>{@code jsonrpc} A string indicating the JSON-RPC protocol 026 * version set to "2.0". 027 * </ul> 028 * <li>The request failed. The returned JSON object contains: 029 * <ul> 030 * <li>{@code error} An object with: 031 * <ul> 032 * <li>{@code code} An integer indicating the error type. 033 * <li>{@code message} A brief error messsage. 034 * <li>{@code data} Optional error data. 035 * </ul> 036 * <li>{@code id} The request identifier. If it couldn't be 037 * determined, e.g. due to a request parse error, the ID is 038 * set to {@code null}. 039 * <li>{@code jsonrpc} A string indicating the JSON-RPC protocol 040 * version set to "2.0". 041 * </ul> 042 * </ul> 043 * 044 * <p>Here is an example JSON-RPC 2.0 response string where the request 045 * has succeeded: 046 * 047 * <pre> 048 * { 049 * "result" : true, 050 * "id" : "req-002", 051 * "jsonrpc" : "2.0" 052 * } 053 * </pre> 054 * 055 * 056 * <p>And here is an example JSON-RPC 2.0 response string indicating a failure: 057 * 058 * <pre> 059 * { 060 * "error" : { "code" : -32601, "message" : "Method not found" }, 061 * "id" : "req-003", 062 * "jsonrpc" : "2.0" 063 * } 064 * </pre> 065 * 066 * <p>A response object is obtained either by passing a valid JSON-RPC 2.0 067 * response string to the static {@link #parse} method or by invoking the 068 * appropriate constructor. 069 * 070 * <p>Here is how parsing is done: 071 * 072 * <pre> 073 * String jsonString = "{\"result\":true,\"id\":\"req-002\",\"jsonrpc\":\"2.0\"}"; 074 * 075 * JSONRPC2Response response = null; 076 * 077 * try { 078 * response = JSONRPC2Response.parse(jsonString); 079 * 080 * } catch (JSONRPC2Exception e) { 081 * // handle exception 082 * } 083 * </pre> 084 * 085 * <p>And here is how you can replicate the above example response strings: 086 * 087 * <pre> 088 * // success example 089 * JSONRPC2Response resp = new JSONRPC2Response(true, "req-002"); 090 * System.out.println(resp); 091 * 092 * // failure example 093 * JSONRPC2Error err = new JSONRPC2Error(-32601, "Method not found"); 094 * resp = new JSONRPC2Response(err, "req-003"); 095 * System.out.println(resp); 096 * 097 * </pre> 098 * 099 * <p id="map">The mapping between JSON and Java entities (as defined by the 100 * underlying JSON Smart library): 101 * 102 * <pre> 103 * true|false <---> java.lang.Boolean 104 * number <---> java.lang.Number 105 * string <---> java.lang.String 106 * array <---> java.util.List 107 * object <---> java.util.Map 108 * null <---> null 109 * </pre> 110 * 111 * @author Vladimir Dzhuvinov 112 */ 113public class JSONRPC2Response extends JSONRPC2Message { 114 115 116 /** 117 * The result. 118 */ 119 private Object result = null; 120 121 122 /** 123 * The error object. 124 */ 125 private JSONRPC2Error error = null; 126 127 128 /** 129 * The echoed request identifier. 130 */ 131 private Object id = null; 132 133 134 /** 135 * Parses a JSON-RPC 2.0 response string. This method is thread-safe. 136 * 137 * @param jsonString The JSON-RPC 2.0 response string, UTF-8 encoded. 138 * Must not be {@code null}. 139 * 140 * @return The corresponding JSON-RPC 2.0 response object. 141 * 142 * @throws JSONRPC2ParseException With detailed message if parsing 143 * failed. 144 */ 145 public static JSONRPC2Response parse(final String jsonString) 146 throws JSONRPC2ParseException { 147 148 return parse(jsonString, false, false, false); 149 } 150 151 152 /** 153 * Parses a JSON-RPC 2.0 response string. This method is thread-safe. 154 * 155 * @param jsonString The JSON-RPC 2.0 response string, UTF-8 encoded. 156 * Must not be {@code null}. 157 * @param preserveOrder {@code true} to preserve the order of JSON 158 * object members in results. 159 * 160 * @return The corresponding JSON-RPC 2.0 response object. 161 * 162 * @throws JSONRPC2ParseException With detailed message if parsing 163 * failed. 164 */ 165 public static JSONRPC2Response parse(final String jsonString, 166 final boolean preserveOrder) 167 throws JSONRPC2ParseException { 168 169 return parse(jsonString, preserveOrder, false, false); 170 } 171 172 173 /** 174 * Parses a JSON-RPC 2.0 response string. This method is thread-safe. 175 * 176 * @param jsonString The JSON-RPC 2.0 response string, UTF-8 encoded. 177 * Must not be {@code null}. 178 * @param preserveOrder {@code true} to preserve the order of JSON 179 * object members in results. 180 * @param ignoreVersion {@code true} to skip a check of the 181 * {@code "jsonrpc":"2.0"} version attribute in the 182 * JSON-RPC 2.0 message. 183 * 184 * @return The corresponding JSON-RPC 2.0 response object. 185 * 186 * @throws JSONRPC2ParseException With detailed message if the parsing 187 * failed. 188 */ 189 public static JSONRPC2Response parse(final String jsonString, 190 final boolean preserveOrder, 191 final boolean ignoreVersion) 192 throws JSONRPC2ParseException { 193 194 return parse(jsonString, preserveOrder, ignoreVersion, false); 195 } 196 197 198 /** 199 * Parses a JSON-RPC 2.0 response string. This method is thread-safe. 200 * 201 * @param jsonString The JSON-RPC 2.0 response string, UTF-8 202 * encoded. Must not be {@code null}. 203 * @param preserveOrder {@code true} to preserve the order of 204 * JSON object members in results. 205 * @param ignoreVersion {@code true} to skip a check of the 206 * {@code "jsonrpc":"2.0"} version 207 * attribute in the JSON-RPC 2.0 message. 208 * @param parseNonStdAttributes {@code true} to parse non-standard 209 * attributes found in the JSON-RPC 2.0 210 * message. 211 * 212 * @return The corresponding JSON-RPC 2.0 response object. 213 * 214 * @throws JSONRPC2ParseException With detailed message if the parsing 215 * failed. 216 */ 217 public static JSONRPC2Response parse(final String jsonString, 218 final boolean preserveOrder, 219 final boolean ignoreVersion, 220 final boolean parseNonStdAttributes) 221 throws JSONRPC2ParseException { 222 223 JSONRPC2Parser parser = new JSONRPC2Parser(preserveOrder, ignoreVersion, parseNonStdAttributes); 224 225 return parser.parseJSONRPC2Response(jsonString); 226 } 227 228 229 /** 230 * Creates a new JSON-RPC 2.0 response to a successful request. 231 * 232 * @param result The result. The value can <a href="#map">map</a> 233 * to any JSON type. May be {@code null}. 234 * @param id The request identifier echoed back to the caller. May 235 * be {@code null} though not recommended. 236 */ 237 public JSONRPC2Response(final Object result, final Object id) { 238 239 setResult(result); 240 setID(id); 241 } 242 243 244 /** 245 * Creates a new JSON-RPC 2.0 response to a successful request which 246 * result is {@code null}. 247 * 248 * @param id The request identifier echoed back to the caller. May be 249 * {@code null} though not recommended. 250 */ 251 public JSONRPC2Response(final Object id) { 252 253 setResult(null); 254 setID(id); 255 } 256 257 258 /** 259 * Creates a new JSON-RPC 2.0 response to a failed request. 260 * 261 * @param error A JSON-RPC 2.0 error instance indicating the 262 * cause of the failure. Must not be {@code null}. 263 * @param id The request identifier echoed back to the caller. 264 * Pass a {@code null} if the request identifier couldn't 265 * be determined (e.g. due to a parse error). 266 */ 267 public JSONRPC2Response(final JSONRPC2Error error, final Object id) { 268 269 setError(error); 270 setID(id); 271 } 272 273 274 /** 275 * Indicates a successful JSON-RPC 2.0 request and sets the result. 276 * Note that if the response was previously indicating failure this 277 * will turn it into a response indicating success. Any previously set 278 * error data will be invalidated. 279 * 280 * @param result The result. The value can <a href="#map">map</a> to 281 * any JSON type. May be {@code null}. 282 */ 283 public void setResult(final Object result) { 284 285 // result and error are mutually exclusive 286 this.result = result; 287 this.error = null; 288 } 289 290 291 /** 292 * Gets the result of the request. The returned value has meaning 293 * only if the request was successful. Use the 294 * {@link #getError getError} method to check this. 295 * 296 * @return The result. 297 */ 298 public Object getResult() { 299 300 return result; 301 } 302 303 304 /** 305 * Indicates a failed JSON-RPC 2.0 request and sets the error details. 306 * Note that if the response was previously indicating success this 307 * will turn it into a response indicating failure. Any previously set 308 * result data will be invalidated. 309 * 310 * @param error A JSON-RPC 2.0 error instance indicating the cause of 311 * the failure. Must not be {@code null}. 312 */ 313 public void setError(final JSONRPC2Error error) { 314 315 if (error == null) 316 throw new IllegalArgumentException("The error object cannot be null"); 317 318 // result and error are mutually exclusive 319 this.error = error; 320 this.result = null; 321 } 322 323 324 /** 325 * Gets the error object indicating the cause of the request failure. 326 * If a {@code null} is returned, the request succeeded and there was 327 * no error. 328 * 329 * @return A JSON-RPC 2.0 error object, {@code null} if the response 330 * indicates success. 331 */ 332 public JSONRPC2Error getError() { 333 334 return error; 335 } 336 337 338 /** 339 * A convinience method to check if the response indicates success or 340 * failure of the request. Alternatively, you can use the 341 * {@code #getError} method for this purpose. 342 * 343 * @return {@code true} if the request succeeded, {@code false} if 344 * there was an error. 345 */ 346 public boolean indicatesSuccess() { 347 348 return error == null; 349 } 350 351 352 /** 353 * Sets the request identifier echoed back to the caller. 354 * 355 * @param id The value must <a href="#map">map</a> to a JSON scalar. 356 * Pass a {@code null} if the request identifier couldn't 357 * be determined (e.g. due to a parse error). 358 */ 359 public void setID(final Object id) { 360 361 if (id == null || 362 id instanceof Boolean || 363 id instanceof Number || 364 id instanceof String 365 ) { 366 this.id = id; 367 } else { 368 this.id = id.toString(); 369 } 370 } 371 372 373 /** 374 * Gets the request identifier that is echoed back to the caller. 375 * 376 * @return The request identifier. If there was an error during the 377 * the request retrieval (e.g. parse error) and the identifier 378 * couldn't be determined, the value will be {@code null}. 379 */ 380 public Object getID() { 381 382 return id; 383 } 384 385 386 @Override 387 public JSONObject toJSONObject() { 388 389 JSONObject out = new JSONObject(); 390 391 // Result and error are mutually exclusive 392 if (error != null) { 393 out.put("error", error.toJSONObject()); 394 } 395 else { 396 out.put("result", result); 397 } 398 399 out.put("id", id); 400 401 out.put("jsonrpc", "2.0"); 402 403 404 Map <String,Object> nonStdAttributes = getNonStdAttributes(); 405 406 if (nonStdAttributes != null) { 407 408 for (final Map.Entry<String,Object> attr: nonStdAttributes.entrySet()) 409 out.put(attr.getKey(), attr.getValue()); 410 } 411 412 return out; 413 } 414}