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 * @author Vladimir Dzhuvinov 087 */ 088public class JSONRPC2Request extends JSONRPC2Message { 089 090 091 /** 092 * The method name. 093 */ 094 private String method; 095 096 097 /** 098 * The positional parameters, {@code null} if none. 099 */ 100 private List<Object> positionalParams; 101 102 103 /** 104 * The named parameters, {@code null} if none. 105 */ 106 private Map<String,Object> namedParams; 107 108 109 /** 110 * The request identifier. 111 */ 112 private Object id; 113 114 115 /** 116 * Parses a JSON-RPC 2.0 request string. This method is thread-safe. 117 * 118 * @param jsonString The JSON-RPC 2.0 request string, UTF-8 encoded. 119 * Must not be {@code null}. 120 * 121 * @return The corresponding JSON-RPC 2.0 request object. 122 * 123 * @throws JSONRPC2ParseException With detailed message if parsing 124 * failed. 125 */ 126 public static JSONRPC2Request parse(final String jsonString) 127 throws JSONRPC2ParseException { 128 129 return parse(jsonString, false, false, false); 130 } 131 132 133 /** 134 * Parses a JSON-RPC 2.0 request string. This method is thread-safe. 135 * 136 * @param jsonString The JSON-RPC 2.0 request string, UTF-8 encoded. 137 * Must not be {@code null}. 138 * @param preserveOrder {@code true} to preserve the order of JSON 139 * object members in parameters. 140 * 141 * @return The corresponding JSON-RPC 2.0 request object. 142 * 143 * @throws JSONRPC2ParseException With detailed message if parsing 144 * failed. 145 */ 146 public static JSONRPC2Request parse(final String jsonString, 147 final boolean preserveOrder) 148 throws JSONRPC2ParseException { 149 150 return parse(jsonString, preserveOrder, false, false); 151 } 152 153 154 /** 155 * Parses a JSON-RPC 2.0 request string. This method is thread-safe. 156 * 157 * @param jsonString The JSON-RPC 2.0 request string, UTF-8 encoded. 158 * Must not be {@code null}. 159 * @param preserveOrder {@code true} to preserve the order of JSON 160 * object members in parameters. 161 * @param ignoreVersion {@code true} to skip a check of the 162 * {@code "jsonrpc":"2.0"} version attribute in the 163 * JSON-RPC 2.0 message. 164 * 165 * @return The corresponding JSON-RPC 2.0 request object. 166 * 167 * @throws JSONRPC2ParseException With detailed message if parsing 168 * failed. 169 */ 170 public static JSONRPC2Request parse(final String jsonString, 171 final boolean preserveOrder, 172 final boolean ignoreVersion) 173 throws JSONRPC2ParseException { 174 175 return parse(jsonString, preserveOrder, ignoreVersion, false); 176 } 177 178 179 /** 180 * Parses a JSON-RPC 2.0 request string. This method is thread-safe. 181 * 182 * @param jsonString The JSON-RPC 2.0 request string, UTF-8 183 * encoded. Must not be {@code null}. 184 * @param preserveOrder {@code true} to preserve the order of 185 * JSON object members in parameters. 186 * @param ignoreVersion {@code true} to skip a check of the 187 * {@code "jsonrpc":"2.0"} version 188 * attribute in the JSON-RPC 2.0 message. 189 * @param parseNonStdAttributes {@code true} to parse non-standard 190 * attributes found in the JSON-RPC 2.0 191 * message. 192 * 193 * @return The corresponding JSON-RPC 2.0 request object. 194 * 195 * @throws JSONRPC2ParseException With detailed message if parsing 196 * failed. 197 */ 198 public static JSONRPC2Request parse(final String jsonString, 199 final boolean preserveOrder, 200 final boolean ignoreVersion, 201 final boolean parseNonStdAttributes) 202 throws JSONRPC2ParseException { 203 204 JSONRPC2Parser parser = new JSONRPC2Parser(preserveOrder, 205 ignoreVersion, 206 parseNonStdAttributes); 207 208 return parser.parseJSONRPC2Request(jsonString); 209 } 210 211 212 /** 213 * Constructs a new JSON-RPC 2.0 request with no parameters. 214 * 215 * @param method The name of the requested method. Must not be 216 * {@code null}. 217 * @param id The request identifier echoed back to the caller. 218 * The value must <a href="#map">map</a> to a JSON 219 * scalar ({@code null} and fractions, however, should 220 * be avoided). 221 */ 222 public JSONRPC2Request(final String method, final Object id) { 223 224 setMethod(method); 225 setID(id); 226 } 227 228 229 /** 230 * Constructs a new JSON-RPC 2.0 request with positional (JSON array) 231 * parameters. 232 * 233 * @param method The name of the requested method. Must not 234 * be {@code null}. 235 * @param positionalParams The positional (JSON array) parameters, 236 * {@code null} if none. 237 * @param id The request identifier echoed back to the 238 * caller. The value must <a href="#map">map</a> 239 * to a JSON scalar ({@code null} and 240 * fractions, however, should be avoided). 241 */ 242 public JSONRPC2Request(final String method, 243 final List<Object> positionalParams, 244 final Object id) { 245 246 setMethod(method); 247 setPositionalParams(positionalParams); 248 setID(id); 249 } 250 251 252 /** 253 * Constructs a new JSON-RPC 2.0 request with named (JSON object) 254 * parameters. 255 * 256 * @param method The name of the requested method. 257 * @param namedParams The named (JSON object) parameters, {@code null} 258 * if none. 259 * @param id The request identifier echoed back to the caller. 260 * The value must <a href="#map">map</a> to a JSON 261 * scalar ({@code null} and fractions, however, 262 * should be avoided). 263 */ 264 public JSONRPC2Request(final String method, 265 final Map <String,Object> namedParams, 266 final Object id) { 267 268 setMethod(method); 269 setNamedParams(namedParams); 270 setID(id); 271 } 272 273 274 /** 275 * Gets the name of the requested method. 276 * 277 * @return The method name. 278 */ 279 public String getMethod() { 280 281 return method; 282 } 283 284 285 /** 286 * Sets the name of the requested method. 287 * 288 * @param method The method name. Must not be {@code null}. 289 */ 290 public void setMethod(final String method) { 291 292 // The method name is mandatory 293 if (method == null) 294 throw new IllegalArgumentException("The method name must not be null"); 295 296 this.method = method; 297 } 298 299 300 /** 301 * Gets the parameters type ({@link JSONRPC2ParamsType#ARRAY positional}, 302 * {@link JSONRPC2ParamsType#OBJECT named} or 303 * {@link JSONRPC2ParamsType#NO_PARAMS none}). 304 * 305 * @return The parameters type. 306 */ 307 public JSONRPC2ParamsType getParamsType() { 308 309 if (positionalParams == null && namedParams == null) 310 return JSONRPC2ParamsType.NO_PARAMS; 311 312 if (positionalParams != null) 313 return JSONRPC2ParamsType.ARRAY; 314 315 if (namedParams != null) 316 return JSONRPC2ParamsType.OBJECT; 317 318 else 319 return JSONRPC2ParamsType.NO_PARAMS; 320 } 321 322 323 /** 324 * Gets the request parameters. 325 * 326 * <p>This method was deprecated in version 1.30. Use 327 * {@link #getPositionalParams} or {@link #getNamedParams} instead. 328 * 329 * @return The parameters as {@code List<Object>} for positional 330 * (JSON array), {@code Map<String,Object>} for named 331 * (JSON object), or {@code null} if none. 332 */ 333 @Deprecated 334 public Object getParams() { 335 336 switch (getParamsType()) { 337 338 case ARRAY: 339 return positionalParams; 340 341 case OBJECT: 342 return namedParams; 343 344 default: 345 return null; 346 } 347 } 348 349 350 /** 351 * Gets the positional (JSON array) parameters. 352 * 353 * @since 1.30 354 * 355 * @return The positional (JSON array) parameters, {@code null} if none 356 * or named. 357 */ 358 public List<Object> getPositionalParams() { 359 360 return positionalParams; 361 } 362 363 364 /** 365 * Gets the named parameters. 366 * 367 * @since 1.30 368 * 369 * @return The named (JSON object) parameters, {@code null} if none or 370 * positional. 371 */ 372 public Map<String,Object> getNamedParams() { 373 374 return namedParams; 375 } 376 377 378 /** 379 * Sets the request parameters. 380 * 381 * <p>This method was deprecated in version 1.30. Use 382 * {@link #setPositionalParams} or {@link #setNamedParams} instead. 383 * 384 * @param params The parameters. For positional (JSON array) pass a 385 * {@code List<Object>}. For named (JSON object) 386 * pass a {@code Map<String,Object>}. If there are 387 * no parameters pass {@code null}. 388 */ 389 @Deprecated 390 @SuppressWarnings("unchecked") 391 public void setParams(final Object params) { 392 393 if (params == null) { 394 positionalParams = null; 395 namedParams = null; 396 } else if (params instanceof List) { 397 positionalParams = (List<Object>) params; 398 } else if (params instanceof Map) { 399 namedParams = (Map<String, Object>) params; 400 } else { 401 throw new IllegalArgumentException("The request parameters must be of type List, Map or null"); 402 } 403 } 404 405 406 /** 407 * Sets the positional (JSON array) request parameters. 408 * 409 * @since 1.30 410 * 411 * @param positionalParams The positional (JSON array) request 412 * parameters, {@code null} if none. 413 */ 414 public void setPositionalParams(final List<Object> positionalParams) { 415 416 if (positionalParams == null) 417 return; 418 419 this.positionalParams = positionalParams; 420 } 421 422 423 /** 424 * Sets the named (JSON object) request parameters. 425 * 426 * @since 1.30 427 * 428 * @param namedParams The named (JSON object) request parameters, 429 * {@code null} if none. 430 */ 431 public void setNamedParams(final Map<String,Object> namedParams) { 432 433 if (namedParams == null) 434 return; 435 436 this.namedParams = namedParams; 437 } 438 439 440 /** 441 * Gets the request identifier. 442 * 443 * @return The request identifier ({@code Number}, {@code Boolean}, 444 * {@code String}) or {@code null}. 445 */ 446 public Object getID() { 447 448 return id; 449 } 450 451 452 /** 453 * Sets the request identifier (ID). 454 * 455 * @param id The request identifier echoed back to the caller. 456 * The value must <a href="#map">map</a> to a JSON 457 * scalar ({@code null} and fractions, however, should 458 * be avoided). 459 */ 460 public void setID(final Object id) { 461 462 if (id == null || 463 id instanceof Boolean || 464 id instanceof Number || 465 id instanceof String 466 ) { 467 this.id = id; 468 } else { 469 this.id = id.toString(); 470 } 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}