001package com.thetransactioncompany.jsonrpc2; 002 003 004import java.util.List; 005import java.util.Map; 006 007import net.minidev.json.JSONObject; 008 009 010/** 011 * Represents a JSON-RPC 2.0 request. 012 * 013 * <p>A request carries four pieces of data: 014 * <ul> 015 * <li>{@code method} The name of the remote method to call. 016 * <li>{@code params} The required method parameters (if any), which can 017 * be packed into a JSON array or object. 018 * <li>{@code id} An identifier which is echoed back to the client with 019 * the response. 020 * <li>{@code jsonrpc} A string indicating the JSON-RPC protocol version 021 * set to "2.0". 022 * </ul> 023 * 024 * <p>Here is a sample JSON-RPC 2.0 request string: 025 * 026 * <pre> 027 * { 028 * "method" : "makePayment", 029 * "params" : { "recipient" : "Penny Adams", "amount":175.05 }, 030 * "id" : "0001", 031 * "jsonrpc" : "2.0" 032 * } 033 * </pre> 034 * 035 * <p>This class provides two methods to obtain a request object: 036 * <ul> 037 * <li>Pass a JSON-RPC 2.0 request string to the static 038 * {@link #parse} method, or 039 * <li>Invoke one of the constructors with the appropriate arguments. 040 * </ul> 041 * 042 * <p>Example 1: Parsing a request string: 043 * 044 * <pre> 045 * String jsonString = "{\"method\":\"makePayment\"," + 046 * "\"params\":{\"recipient\":\"Penny Adams\",\"amount\":175.05}," + 047 * "\"id\":\"0001\","+ 048 * "\"jsonrpc\":\"2.0\"}"; 049 * 050 * JSONRPC2Request req = null; 051 * 052 * try { 053 * req = JSONRPC2Request.parse(jsonString); 054 * 055 * } catch (JSONRPC2ParseException e) { 056 * // handle exception 057 * } 058 * </pre> 059 * 060 * <p>Example 2: Recreating the above request: 061 * 062 * <pre> 063 * String method = "makePayment"; 064 * Map<String,Object> params = new HashMap<String,Object>(); 065 * params.put("recipient", "Penny Adams"); 066 * params.put("amount", 175.05); 067 * String id = "0001"; 068 * 069 * JSONRPC2Request req = new JSONRPC2Request(method, params, id); 070 * 071 * System.out.println(req); 072 * </pre> 073 * 074 * <p id="map">The mapping between JSON and Java entities (as defined by the 075 * underlying JSON Smart library): 076 * 077 * <pre> 078 * true|false <---> java.lang.Boolean 079 * number <---> java.lang.Number 080 * string <---> java.lang.String 081 * array <---> java.util.List 082 * object <---> java.util.Map 083 * null <---> null 084 * </pre> 085 * 086 * <p>The JSON-RPC 2.0 specification and user group forum can be found 087 * <a href="http://groups.google.com/group/json-rpc">here</a>. 088 * 089 * @author Vladimir Dzhuvinov 090 */ 091public class JSONRPC2Request extends JSONRPC2Message { 092 093 094 /** 095 * The method name. 096 */ 097 private String method; 098 099 100 /** 101 * The positional parameters, {@code null} if none. 102 */ 103 private List<Object> positionalParams; 104 105 106 /** 107 * The named parameters, {@code null} if none. 108 */ 109 private Map<String,Object> namedParams; 110 111 112 /** 113 * The request identifier. 114 */ 115 private Object id; 116 117 118 /** 119 * Parses a JSON-RPC 2.0 request string. This method is thread-safe. 120 * 121 * @param jsonString The JSON-RPC 2.0 request string, UTF-8 encoded. 122 * Must not be {@code null}. 123 * 124 * @return The corresponding JSON-RPC 2.0 request object. 125 * 126 * @throws JSONRPC2ParseException With detailed message if parsing 127 * failed. 128 */ 129 public static JSONRPC2Request parse(final String jsonString) 130 throws JSONRPC2ParseException { 131 132 return parse(jsonString, false, false, false); 133 } 134 135 136 /** 137 * Parses a JSON-RPC 2.0 request string. This method is thread-safe. 138 * 139 * @param jsonString The JSON-RPC 2.0 request string, UTF-8 encoded. 140 * Must not be {@code null}. 141 * @param preserveOrder {@code true} to preserve the order of JSON 142 * object members in parameters. 143 * 144 * @return The corresponding JSON-RPC 2.0 request object. 145 * 146 * @throws JSONRPC2ParseException With detailed message if parsing 147 * failed. 148 */ 149 public static JSONRPC2Request parse(final String jsonString, 150 final boolean preserveOrder) 151 throws JSONRPC2ParseException { 152 153 return parse(jsonString, preserveOrder, false, false); 154 } 155 156 157 /** 158 * Parses a JSON-RPC 2.0 request string. This method is thread-safe. 159 * 160 * @param jsonString The JSON-RPC 2.0 request string, UTF-8 encoded. 161 * Must not be {@code null}. 162 * @param preserveOrder {@code true} to preserve the order of JSON 163 * object members in parameters. 164 * @param ignoreVersion {@code true} to skip a check of the 165 * {@code "jsonrpc":"2.0"} version attribute in the 166 * JSON-RPC 2.0 message. 167 * 168 * @return The corresponding JSON-RPC 2.0 request object. 169 * 170 * @throws JSONRPC2ParseException With detailed message if parsing 171 * failed. 172 */ 173 public static JSONRPC2Request parse(final String jsonString, 174 final boolean preserveOrder, 175 final boolean ignoreVersion) 176 throws JSONRPC2ParseException { 177 178 return parse(jsonString, preserveOrder, ignoreVersion, false); 179 } 180 181 182 /** 183 * Parses a JSON-RPC 2.0 request string. This method is thread-safe. 184 * 185 * @param jsonString The JSON-RPC 2.0 request string, UTF-8 186 * encoded. Must not be {@code null}. 187 * @param preserveOrder {@code true} to preserve the order of 188 * JSON object members in parameters. 189 * @param ignoreVersion {@code true} to skip a check of the 190 * {@code "jsonrpc":"2.0"} version 191 * attribute in the JSON-RPC 2.0 message. 192 * @param parseNonStdAttributes {@code true} to parse non-standard 193 * attributes found in the JSON-RPC 2.0 194 * message. 195 * 196 * @return The corresponding JSON-RPC 2.0 request object. 197 * 198 * @throws JSONRPC2ParseException With detailed message if parsing 199 * failed. 200 */ 201 public static JSONRPC2Request parse(final String jsonString, 202 final boolean preserveOrder, 203 final boolean ignoreVersion, 204 final boolean parseNonStdAttributes) 205 throws JSONRPC2ParseException { 206 207 JSONRPC2Parser parser = new JSONRPC2Parser(preserveOrder, 208 ignoreVersion, 209 parseNonStdAttributes); 210 211 return parser.parseJSONRPC2Request(jsonString); 212 } 213 214 215 /** 216 * Constructs a new JSON-RPC 2.0 request with no parameters. 217 * 218 * @param method The name of the requested method. Must not be 219 * {@code null}. 220 * @param id The request identifier echoed back to the caller. 221 * The value must <a href="#map">map</a> to a JSON 222 * scalar ({@code null} and fractions, however, should 223 * be avoided). 224 */ 225 public JSONRPC2Request(final String method, final Object id) { 226 227 setMethod(method); 228 setID(id); 229 } 230 231 232 /** 233 * Constructs a new JSON-RPC 2.0 request with positional (JSON array) 234 * parameters. 235 * 236 * @param method The name of the requested method. Must not 237 * be {@code null}. 238 * @param positionalParams The positional (JSON array) parameters, 239 * {@code null} if none. 240 * @param id The request identifier echoed back to the 241 * caller. The value must <a href="#map">map</a> 242 * to a JSON scalar ({@code null} and 243 * fractions, however, should be avoided). 244 */ 245 public JSONRPC2Request(final String method, 246 final List<Object> positionalParams, 247 final Object id) { 248 249 setMethod(method); 250 setPositionalParams(positionalParams); 251 setID(id); 252 } 253 254 255 /** 256 * Constructs a new JSON-RPC 2.0 request with named (JSON object) 257 * parameters. 258 * 259 * @param method The name of the requested method. 260 * @param namedParams The named (JSON object) parameters, {@code null} 261 * if none. 262 * @param id The request identifier echoed back to the caller. 263 * The value must <a href="#map">map</a> to a JSON 264 * scalar ({@code null} and fractions, however, 265 * should be avoided). 266 */ 267 public JSONRPC2Request(final String method, 268 final Map <String,Object> namedParams, 269 final Object id) { 270 271 setMethod(method); 272 setNamedParams(namedParams); 273 setID(id); 274 } 275 276 277 /** 278 * Gets the name of the requested method. 279 * 280 * @return The method name. 281 */ 282 public String getMethod() { 283 284 return method; 285 } 286 287 288 /** 289 * Sets the name of the requested method. 290 * 291 * @param method The method name. Must not be {@code null}. 292 */ 293 public void setMethod(final String method) { 294 295 // The method name is mandatory 296 if (method == null) 297 throw new IllegalArgumentException("The method name must not be null"); 298 299 this.method = method; 300 } 301 302 303 /** 304 * Gets the parameters type ({@link JSONRPC2ParamsType#ARRAY positional}, 305 * {@link JSONRPC2ParamsType#OBJECT named} or 306 * {@link JSONRPC2ParamsType#NO_PARAMS none}). 307 * 308 * @return The parameters type. 309 */ 310 public JSONRPC2ParamsType getParamsType() { 311 312 if (positionalParams == null && namedParams == null) 313 return JSONRPC2ParamsType.NO_PARAMS; 314 315 if (positionalParams != null) 316 return JSONRPC2ParamsType.ARRAY; 317 318 if (namedParams != null) 319 return JSONRPC2ParamsType.OBJECT; 320 321 else 322 return JSONRPC2ParamsType.NO_PARAMS; 323 } 324 325 326 /** 327 * Gets the request parameters. 328 * 329 * <p>This method was deprecated in version 1.30. Use 330 * {@link #getPositionalParams} or {@link #getNamedParams} instead. 331 * 332 * @return The parameters as {@code List<Object>} for positional 333 * (JSON array), {@code Map<String,Object>} for named 334 * (JSON object), or {@code null} if none. 335 */ 336 @Deprecated 337 public Object getParams() { 338 339 switch (getParamsType()) { 340 341 case ARRAY: 342 return positionalParams; 343 344 case OBJECT: 345 return namedParams; 346 347 default: 348 return null; 349 } 350 } 351 352 353 /** 354 * Gets the positional (JSON array) parameters. 355 * 356 * @since 1.30 357 * 358 * @return The positional (JSON array) parameters, {@code null} if none 359 * or named. 360 */ 361 public List<Object> getPositionalParams() { 362 363 return positionalParams; 364 } 365 366 367 /** 368 * Gets the named parameters. 369 * 370 * @since 1.30 371 * 372 * @return The named (JSON object) parameters, {@code null} if none or 373 * positional. 374 */ 375 public Map<String,Object> getNamedParams() { 376 377 return namedParams; 378 } 379 380 381 /** 382 * Sets the request parameters. 383 * 384 * <p>This method was deprecated in version 1.30. Use 385 * {@link #setPositionalParams} or {@link #setNamedParams} instead. 386 * 387 * @param params The parameters. For positional (JSON array) pass a 388 * {@code List<Object>}. For named (JSON object) 389 * pass a {@code Map<String,Object>}. If there are 390 * no parameters pass {@code null}. 391 */ 392 @Deprecated 393 @SuppressWarnings("unchecked") 394 public void setParams(final Object params) { 395 396 if (params == null) { 397 positionalParams = null; 398 namedParams = null; 399 } else if (params instanceof List) { 400 positionalParams = (List<Object>) params; 401 } else if (params instanceof Map) { 402 namedParams = (Map<String, Object>) params; 403 } else { 404 throw new IllegalArgumentException("The request parameters must be of type List, Map or null"); 405 } 406 } 407 408 409 /** 410 * Sets the positional (JSON array) request parameters. 411 * 412 * @since 1.30 413 * 414 * @param positionalParams The positional (JSON array) request 415 * parameters, {@code null} if none. 416 */ 417 public void setPositionalParams(final List<Object> positionalParams) { 418 419 if (positionalParams == null) 420 return; 421 422 this.positionalParams = positionalParams; 423 } 424 425 426 /** 427 * Sets the named (JSON object) request parameters. 428 * 429 * @since 1.30 430 * 431 * @param namedParams The named (JSON object) request parameters, 432 * {@code null} if none. 433 */ 434 public void setNamedParams(final Map<String,Object> namedParams) { 435 436 if (namedParams == null) 437 return; 438 439 this.namedParams = namedParams; 440 } 441 442 443 /** 444 * Gets the request identifier. 445 * 446 * @return The request identifier ({@code Number}, {@code Boolean}, 447 * {@code String}) or {@code null}. 448 */ 449 public Object getID() { 450 451 return id; 452 } 453 454 455 /** 456 * Sets the request identifier (ID). 457 * 458 * @param id The request identifier echoed back to the caller. 459 * The value must <a href="#map">map</a> to a JSON 460 * scalar ({@code null} and fractions, however, should 461 * be avoided). 462 */ 463 public void setID(final Object id) { 464 465 if ( id != null && 466 ! (id instanceof Boolean) && 467 ! (id instanceof Number ) && 468 ! (id instanceof String ) ) 469 throw new IllegalArgumentException("The request identifier must map to a JSON scalar"); 470 this.id = id; 471 } 472 473 474 @Override 475 public JSONObject toJSONObject() { 476 477 JSONObject req = new JSONObject(); 478 479 req.put("method", method); 480 481 // The params can be omitted if none 482 switch (getParamsType()) { 483 484 case ARRAY: 485 req.put("params", positionalParams); 486 break; 487 488 case OBJECT: 489 req.put("params", namedParams); 490 break; 491 } 492 493 req.put("id", id); 494 495 req.put("jsonrpc", "2.0"); 496 497 Map <String,Object> nonStdAttributes = getNonStdAttributes(); 498 499 if (nonStdAttributes != null) { 500 501 for (final Map.Entry<String,Object> attr: nonStdAttributes.entrySet()) 502 req.put(attr.getKey(), attr.getValue()); 503 } 504 505 return req; 506 } 507}