forked from I2P_Developers/i2p.i2p
Bundle I2PControl 0.12, as a console webapp
Includes mods to use org.json.simple package. See licenses/LICENSE-Apache2.0.txt Includes jBCrypt: Copyright (c) 2006 Damien Miller <djm@mindrot.org> See licenses/LICENSE-jBCrypt.txt Includes jsonrpc2 libs: See licenses/LICENSE-Apache2.0.txt http://software.dzhuvinov.com/json-rpc-2.0-server.html Jars from maven central: jsonrpc2-base-1.38.1-sources.jar 22-Oct-2017 jsonrpc2-server-1.11-sources.jar 16-Mar-2015
This commit is contained in:
@@ -0,0 +1,274 @@
|
||||
package com.thetransactioncompany.jsonrpc2;
|
||||
|
||||
|
||||
import org.json.simple.JSONObject;
|
||||
|
||||
|
||||
/**
|
||||
* Represents a JSON-RPC 2.0 error that occurred during the processing of a
|
||||
* request. This class is immutable.
|
||||
*
|
||||
* <p>The protocol expects error objects to be structured like this:
|
||||
*
|
||||
* <ul>
|
||||
* <li>{@code code} An integer that indicates the error type.
|
||||
* <li>{@code message} A string providing a short description of the
|
||||
* error. The message should be limited to a concise single sentence.
|
||||
* <li>{@code data} Additional information, which may be omitted. Its
|
||||
* contents is entirely defined by the application.
|
||||
* </ul>
|
||||
*
|
||||
* <p>Note that the "Error" word in the class name was put there solely to
|
||||
* comply with the parlance of the JSON-RPC spec. This class doesn't inherit
|
||||
* from {@code java.lang.Error}. It's a regular subclass of
|
||||
* {@code java.lang.Exception} and, if thrown, it's to indicate a condition
|
||||
* that a reasonable application might want to catch.
|
||||
*
|
||||
* <p>This class also includes convenient final static instances for all
|
||||
* standard JSON-RPC 2.0 errors:
|
||||
*
|
||||
* <ul>
|
||||
* <li>{@link #PARSE_ERROR} JSON parse error (-32700)
|
||||
* <li>{@link #INVALID_REQUEST} Invalid JSON-RPC 2.0 Request (-32600)
|
||||
* <li>{@link #METHOD_NOT_FOUND} Method not found (-32601)
|
||||
* <li>{@link #INVALID_PARAMS} Invalid parameters (-32602)
|
||||
* <li>{@link #INTERNAL_ERROR} Internal error (-32603)
|
||||
* </ul>
|
||||
*
|
||||
* <p>Note that the range -32099..-32000 is reserved for additional server
|
||||
* errors.
|
||||
*
|
||||
* <p id="map">The mapping between JSON and Java entities (as defined by the
|
||||
* underlying JSON Smart library):
|
||||
* <pre>
|
||||
* true|false <---> java.lang.Boolean
|
||||
* number <---> java.lang.Number
|
||||
* string <---> java.lang.String
|
||||
* array <---> java.util.List
|
||||
* object <---> java.util.Map
|
||||
* null <---> null
|
||||
* </pre>
|
||||
*
|
||||
* @author Vladimir Dzhuvinov
|
||||
*/
|
||||
public class JSONRPC2Error extends Exception {
|
||||
|
||||
|
||||
/**
|
||||
* Serial version UID.
|
||||
*/
|
||||
private static final long serialVersionUID = 4682571044532698806L;
|
||||
|
||||
|
||||
/**
|
||||
* JSON parse error (-32700).
|
||||
*/
|
||||
public static final JSONRPC2Error PARSE_ERROR = new JSONRPC2Error(-32700, "JSON parse error");
|
||||
|
||||
|
||||
/**
|
||||
* Invalid JSON-RPC 2.0 request error (-32600).
|
||||
*/
|
||||
public static final JSONRPC2Error INVALID_REQUEST = new JSONRPC2Error(-32600, "Invalid request");
|
||||
|
||||
|
||||
/**
|
||||
* Method not found error (-32601).
|
||||
*/
|
||||
public static final JSONRPC2Error METHOD_NOT_FOUND = new JSONRPC2Error(-32601, "Method not found");
|
||||
|
||||
|
||||
/**
|
||||
* Invalid parameters error (-32602).
|
||||
*/
|
||||
public static final JSONRPC2Error INVALID_PARAMS = new JSONRPC2Error(-32602, "Invalid parameters");
|
||||
|
||||
|
||||
/**
|
||||
* Internal JSON-RPC 2.0 error (-32603).
|
||||
*/
|
||||
public static final JSONRPC2Error INTERNAL_ERROR = new JSONRPC2Error(-32603, "Internal error");
|
||||
|
||||
|
||||
/**
|
||||
* The error code.
|
||||
*/
|
||||
private final int code;
|
||||
|
||||
|
||||
/**
|
||||
* The optional error data.
|
||||
*/
|
||||
private final Object data;
|
||||
|
||||
|
||||
/**
|
||||
* Appends the specified string to the message of a JSON-RPC 2.0 error.
|
||||
*
|
||||
* @param err The JSON-RPC 2.0 error. Must not be {@code null}.
|
||||
* @param apx The string to append to the original error message.
|
||||
*
|
||||
* @return A new JSON-RPC 2.0 error with the appended message.
|
||||
*/
|
||||
@Deprecated
|
||||
public static JSONRPC2Error appendMessage(final JSONRPC2Error err, final String apx) {
|
||||
|
||||
return new JSONRPC2Error(err.getCode(), err.getMessage() + apx, err.getData());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets the specified data to a JSON-RPC 2.0 error.
|
||||
*
|
||||
* @param err The JSON-RPC 2.0 error to have its data field set. Must
|
||||
* not be {@code null}.
|
||||
* @param data Optional error data, must <a href="#map">map</a> to a
|
||||
* valid JSON type.
|
||||
*
|
||||
* @return A new JSON-RPC 2.0 error with the set data.
|
||||
*/
|
||||
@Deprecated
|
||||
public static JSONRPC2Error setData(final JSONRPC2Error err, final Object data) {
|
||||
|
||||
return new JSONRPC2Error(err.getCode(), err.getMessage(), data);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new JSON-RPC 2.0 error with the specified code and
|
||||
* message. The optional data is omitted.
|
||||
*
|
||||
* @param code The error code (standard pre-defined or
|
||||
* application-specific).
|
||||
* @param message The error message.
|
||||
*/
|
||||
public JSONRPC2Error(int code, String message) {
|
||||
|
||||
this(code, message, null);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new JSON-RPC 2.0 error with the specified code,
|
||||
* message and data.
|
||||
*
|
||||
* @param code The error code (standard pre-defined or
|
||||
* application-specific).
|
||||
* @param message The error message.
|
||||
* @param data Optional error data, must <a href="#map">map</a>
|
||||
* to a valid JSON type.
|
||||
*/
|
||||
public JSONRPC2Error(int code, String message, Object data) {
|
||||
|
||||
super(message);
|
||||
this.code = code;
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets the JSON-RPC 2.0 error code.
|
||||
*
|
||||
* @return The error code.
|
||||
*/
|
||||
public int getCode() {
|
||||
|
||||
return code;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets the JSON-RPC 2.0 error data.
|
||||
*
|
||||
* @return The error data, {@code null} if none was specified.
|
||||
*/
|
||||
public Object getData() {
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets the specified data to a JSON-RPC 2.0 error.
|
||||
*
|
||||
* @param data Optional error data, must <a href="#map">map</a> to a
|
||||
* valid JSON type.
|
||||
*
|
||||
* @return A new JSON-RPC 2.0 error with the set data.
|
||||
*/
|
||||
public JSONRPC2Error setData(final Object data) {
|
||||
|
||||
return new JSONRPC2Error(code, getMessage(), data);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Appends the specified string to the message of this JSON-RPC 2.0
|
||||
* error.
|
||||
*
|
||||
* @param apx The string to append to the original error message.
|
||||
*
|
||||
* @return A new JSON-RPC 2.0 error with the appended message.
|
||||
*/
|
||||
public JSONRPC2Error appendMessage(final String apx) {
|
||||
|
||||
return new JSONRPC2Error(code, getMessage() + apx, data);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @see #toJSONObject
|
||||
*/
|
||||
@Deprecated
|
||||
public JSONObject toJSON() {
|
||||
|
||||
return toJSONObject();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns a JSON object representation of this JSON-RPC 2.0 error.
|
||||
*
|
||||
* @return A JSON object representing this error object.
|
||||
*/
|
||||
public JSONObject toJSONObject() {
|
||||
|
||||
JSONObject out = new JSONObject();
|
||||
|
||||
out.put("code", code);
|
||||
out.put("message", super.getMessage());
|
||||
if (data != null)
|
||||
out.put("data", data);
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Serialises the error object to a JSON string.
|
||||
*
|
||||
* @return A JSON-encoded string representing this error object.
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
|
||||
return toJSON().toString();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Overrides {@code Object.equals()}.
|
||||
*
|
||||
* @param object The object to compare to.
|
||||
*
|
||||
* @return {@code true} if both objects are instances if this class and
|
||||
* their error codes are identical, {@code false} if not.
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object object) {
|
||||
|
||||
return object != null &&
|
||||
object instanceof JSONRPC2Error &&
|
||||
code == ((JSONRPC2Error)object).getCode();
|
||||
}
|
||||
}
|
@@ -0,0 +1,251 @@
|
||||
package com.thetransactioncompany.jsonrpc2;
|
||||
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.json.simple.JSONAware;
|
||||
import org.json.simple.JSONObject;
|
||||
|
||||
|
||||
/**
|
||||
* The base abstract class for JSON-RPC 2.0 requests, notifications and
|
||||
* responses. Provides common methods for parsing (from JSON string) and
|
||||
* serialisation (to JSON string) of these three message types.
|
||||
*
|
||||
* <p>Example parsing and serialisation back to JSON:
|
||||
*
|
||||
* <pre>
|
||||
* String jsonString = "{\"method\":\"progressNotify\",\"params\":[\"75%\"],\"jsonrpc\":\"2.0\"}";
|
||||
*
|
||||
* JSONRPC2Message message = null;
|
||||
*
|
||||
* // parse
|
||||
* try {
|
||||
* message = JSONRPC2Message.parse(jsonString);
|
||||
* } catch (JSONRPC2ParseException e) {
|
||||
* // handle parse exception
|
||||
* }
|
||||
*
|
||||
* if (message instanceof JSONRPC2Request)
|
||||
* System.out.println("The message is a request");
|
||||
* else if (message instanceof JSONRPC2Notification)
|
||||
* System.out.println("The message is a notification");
|
||||
* else if (message instanceof JSONRPC2Response)
|
||||
* System.out.println("The message is a response");
|
||||
*
|
||||
* // serialise back to JSON string
|
||||
* System.out.println(message);
|
||||
*
|
||||
* </pre>
|
||||
*
|
||||
* <p id="map">The mapping between JSON and Java entities (as defined by the
|
||||
* underlying JSON Smart library):
|
||||
*
|
||||
* <pre>
|
||||
* true|false <---> java.lang.Boolean
|
||||
* number <---> java.lang.Number
|
||||
* string <---> java.lang.String
|
||||
* array <---> java.util.List
|
||||
* object <---> java.util.Map
|
||||
* null <---> null
|
||||
* </pre>
|
||||
*
|
||||
* @author Vladimir Dzhuvinov
|
||||
*/
|
||||
public abstract class JSONRPC2Message implements JSONAware {
|
||||
|
||||
|
||||
/**
|
||||
* Map of non-standard JSON-RPC 2.0 message attributes, {@code null} if
|
||||
* none.
|
||||
*/
|
||||
private Map <String,Object> nonStdAttributes = null;
|
||||
|
||||
|
||||
/**
|
||||
* Provides common parsing of JSON-RPC 2.0 requests, notifications
|
||||
* and responses. Use this method if you don't know which type of
|
||||
* JSON-RPC message the input JSON string represents.
|
||||
*
|
||||
* <p>Batched requests / notifications are not supported.
|
||||
*
|
||||
* <p>This method is thread-safe.
|
||||
*
|
||||
* <p>If you are certain about the message type use the dedicated
|
||||
* {@link JSONRPC2Request#parse}, {@link JSONRPC2Notification#parse}
|
||||
* or {@link JSONRPC2Response#parse} methods. They are more efficient
|
||||
* and provide a more detailed parse error reporting.
|
||||
*
|
||||
* <p>The member order of parsed JSON objects will not be preserved
|
||||
* (for efficiency reasons) and the JSON-RPC 2.0 version field must be
|
||||
* set to "2.0". To change this behaviour check the optional {@link
|
||||
* #parse(String,boolean,boolean)} method.
|
||||
*
|
||||
* @param jsonString A JSON string representing a JSON-RPC 2.0 request,
|
||||
* notification or response, UTF-8 encoded. Must not
|
||||
* be {@code null}.
|
||||
*
|
||||
* @return An instance of {@link JSONRPC2Request},
|
||||
* {@link JSONRPC2Notification} or {@link JSONRPC2Response}.
|
||||
*
|
||||
* @throws JSONRPC2ParseException With detailed message if parsing
|
||||
* failed.
|
||||
*/
|
||||
public static JSONRPC2Message parse(final String jsonString)
|
||||
throws JSONRPC2ParseException {
|
||||
|
||||
return parse(jsonString, false, false);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Provides common parsing of JSON-RPC 2.0 requests, notifications
|
||||
* and responses. Use this method if you don't know which type of
|
||||
* JSON-RPC message the input string represents.
|
||||
*
|
||||
* <p>Batched requests / notifications are not supported.
|
||||
*
|
||||
* <p>This method is thread-safe.
|
||||
*
|
||||
* <p>If you are certain about the message type use the dedicated
|
||||
* {@link JSONRPC2Request#parse}, {@link JSONRPC2Notification#parse}
|
||||
* or {@link JSONRPC2Response#parse} methods. They are more efficient
|
||||
* and provide a more detailed parse error reporting.
|
||||
*
|
||||
* @param jsonString A JSON string representing a JSON-RPC 2.0
|
||||
* request, notification or response, UTF-8
|
||||
* encoded. Must not be {@code null}.
|
||||
* @param preserveOrder If {@code true} the member order of JSON objects
|
||||
* in parameters and results must be preserved.
|
||||
* @param ignoreVersion If {@code true} the {@code "jsonrpc":"2.0"}
|
||||
* version field in the JSON-RPC 2.0 message will
|
||||
* not be checked.
|
||||
*
|
||||
* @return An instance of {@link JSONRPC2Request},
|
||||
* {@link JSONRPC2Notification} or {@link JSONRPC2Response}.
|
||||
*
|
||||
* @throws JSONRPC2ParseException With detailed message if parsing
|
||||
* failed.
|
||||
*/
|
||||
public static JSONRPC2Message parse(final String jsonString, final boolean preserveOrder, final boolean ignoreVersion)
|
||||
throws JSONRPC2ParseException {
|
||||
|
||||
JSONRPC2Parser parser = new JSONRPC2Parser(preserveOrder, ignoreVersion);
|
||||
|
||||
return parser.parseJSONRPC2Message(jsonString);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Appends a non-standard attribute to this JSON-RPC 2.0 message. This is
|
||||
* done by adding a new member (key / value pair) to the top level JSON
|
||||
* object representing the message.
|
||||
*
|
||||
* <p>You may use this method to add meta and debugging attributes,
|
||||
* such as the request processing time, to a JSON-RPC 2.0 message.
|
||||
*
|
||||
* @param name The attribute name. Must not conflict with the existing
|
||||
* "method", "id", "params", "result", "error" and "jsonrpc"
|
||||
* attributes reserved by the JSON-RPC 2.0 protocol, else
|
||||
* an {@code IllegalArgumentException} will be thrown. Must
|
||||
* not be {@code null} either.
|
||||
* @param value The attribute value. Must be of type String, boolean,
|
||||
* number, List, Map or null, else an
|
||||
* {@code IllegalArgumentException} will be thrown.
|
||||
*/
|
||||
public void appendNonStdAttribute(final String name, final Object value) {
|
||||
|
||||
// Name check
|
||||
if (name == null ||
|
||||
name.equals("method") ||
|
||||
name.equals("id") ||
|
||||
name.equals("params") ||
|
||||
name.equals("result") ||
|
||||
name.equals("error") ||
|
||||
name.equals("jsonrpc") )
|
||||
|
||||
throw new IllegalArgumentException("Non-standard attribute name violation");
|
||||
|
||||
// Value check
|
||||
if ( value != null &&
|
||||
! (value instanceof Boolean) &&
|
||||
! (value instanceof Number) &&
|
||||
! (value instanceof String) &&
|
||||
! (value instanceof List) &&
|
||||
! (value instanceof Map) )
|
||||
|
||||
throw new IllegalArgumentException("Illegal non-standard attribute value, must map to a valid JSON type");
|
||||
|
||||
|
||||
if (nonStdAttributes == null)
|
||||
nonStdAttributes = new HashMap<String,Object>();
|
||||
|
||||
nonStdAttributes.put(name, value);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Retrieves a non-standard JSON-RPC 2.0 message attribute.
|
||||
*
|
||||
* @param name The name of the non-standard attribute to retrieve. Must
|
||||
* not be {@code null}.
|
||||
*
|
||||
* @return The value of the non-standard attribute (may also be
|
||||
* {@code null}, {@code null} if not found.
|
||||
*/
|
||||
public Object getNonStdAttribute(final String name) {
|
||||
|
||||
if (nonStdAttributes == null)
|
||||
return null;
|
||||
|
||||
return nonStdAttributes.get(name);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Retrieves the non-standard JSON-RPC 2.0 message attributes.
|
||||
*
|
||||
* @return The non-standard attributes as a map, {@code null} if none.
|
||||
*/
|
||||
public Map<String,Object> getNonStdAttributes() {
|
||||
|
||||
return nonStdAttributes;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns a JSON object representing this JSON-RPC 2.0 message.
|
||||
*
|
||||
* @return The JSON object.
|
||||
*/
|
||||
public abstract JSONObject toJSONObject();
|
||||
|
||||
|
||||
/**
|
||||
* Returns a JSON string representation of this JSON-RPC 2.0 message.
|
||||
*
|
||||
* @see #toString
|
||||
*
|
||||
* @return The JSON object string representing this JSON-RPC 2.0
|
||||
* message.
|
||||
*/
|
||||
public String toJSONString() {
|
||||
|
||||
return toString();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Serialises this JSON-RPC 2.0 message to a JSON object string.
|
||||
*
|
||||
* @return The JSON object string representing this JSON-RPC 2.0
|
||||
* message.
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
|
||||
return toJSONObject().toString();
|
||||
}
|
||||
}
|
@@ -0,0 +1,448 @@
|
||||
package com.thetransactioncompany.jsonrpc2;
|
||||
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.json.simple.JSONObject;
|
||||
|
||||
|
||||
/**
|
||||
* Represents a JSON-RPC 2.0 notification.
|
||||
*
|
||||
* <p>Notifications provide a mean for calling a remote procedure without
|
||||
* generating a response. Note that notifications are inherently unreliable
|
||||
* as no confirmation is sent back to the caller.
|
||||
*
|
||||
* <p>Notifications have the same JSON structure as requests, except that they
|
||||
* lack an identifier:
|
||||
* <ul>
|
||||
* <li>{@code method} The name of the remote method to call.
|
||||
* <li>{@code params} The required method parameters (if any), which can
|
||||
* be packed into a JSON array or object.
|
||||
* <li>{@code jsonrpc} A string indicating the JSON-RPC protocol version
|
||||
* set to "2.0".
|
||||
* </ul>
|
||||
*
|
||||
* <p>Here is a sample JSON-RPC 2.0 notification string:
|
||||
*
|
||||
* <pre>
|
||||
* {
|
||||
* "method" : "progressNotify",
|
||||
* "params" : ["75%"],
|
||||
* "jsonrpc" : "2.0"
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* <p>This class provides two methods to obtain a request object:
|
||||
* <ul>
|
||||
* <li>Pass a JSON-RPC 2.0 notification string to the static
|
||||
* {@link #parse} method, or
|
||||
* <li>Invoke one of the constructors with the appropriate arguments.
|
||||
* </ul>
|
||||
*
|
||||
* <p>Example 1: Parsing a notification string:
|
||||
*
|
||||
* <pre>
|
||||
* String jsonString = "{\"method\":\"progressNotify\",\"params\":[\"75%\"],\"jsonrpc\":\"2.0\"}";
|
||||
*
|
||||
* JSONRPC2Notification notification = null;
|
||||
*
|
||||
* try {
|
||||
* notification = JSONRPC2Notification.parse(jsonString);
|
||||
*
|
||||
* } catch (JSONRPC2ParseException e) {
|
||||
* // handle exception
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* <p>Example 2: Recreating the above request:
|
||||
*
|
||||
* <pre>
|
||||
* String method = "progressNotify";
|
||||
* List<Object> params = new Vector<Object>();
|
||||
* params.add("75%");
|
||||
*
|
||||
* JSONRPC2Notification notification = new JSONRPC2Notification(method, params);
|
||||
*
|
||||
* System.out.println(notification);
|
||||
* </pre>
|
||||
*
|
||||
* <p id="map">The mapping between JSON and Java entities (as defined by the
|
||||
* underlying JSON Smart library):
|
||||
*
|
||||
* <pre>
|
||||
* true|false <---> java.lang.Boolean
|
||||
* number <---> java.lang.Number
|
||||
* string <---> java.lang.String
|
||||
* array <---> java.util.List
|
||||
* object <---> java.util.Map
|
||||
* null <---> null
|
||||
* </pre>
|
||||
*
|
||||
* @author Vladimir Dzhuvinov
|
||||
*/
|
||||
public class JSONRPC2Notification extends JSONRPC2Message {
|
||||
|
||||
|
||||
/**
|
||||
* The requested method name.
|
||||
*/
|
||||
private String method;
|
||||
|
||||
|
||||
/**
|
||||
* The positional parameters, {@code null} if none.
|
||||
*/
|
||||
private List<Object> positionalParams;
|
||||
|
||||
|
||||
/**
|
||||
* The named parameters, {@code null} if none.
|
||||
*/
|
||||
private Map<String,Object> namedParams;
|
||||
|
||||
|
||||
/**
|
||||
* Parses a JSON-RPC 2.0 notification string. This method is
|
||||
* thread-safe.
|
||||
*
|
||||
* @param jsonString The JSON-RPC 2.0 notification string, UTF-8
|
||||
* encoded. Must not be {@code null}.
|
||||
*
|
||||
* @return The corresponding JSON-RPC 2.0 notification object.
|
||||
*
|
||||
* @throws JSONRPC2ParseException With detailed message if parsing
|
||||
* failed.
|
||||
*/
|
||||
public static JSONRPC2Notification parse(final String jsonString)
|
||||
throws JSONRPC2ParseException {
|
||||
|
||||
return parse(jsonString, false, false, false);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Parses a JSON-RPC 2.0 notification string. This method is
|
||||
* thread-safe.
|
||||
*
|
||||
* @param jsonString The JSON-RPC 2.0 notification string, UTF-8
|
||||
* encoded. Must not be {@code null}.
|
||||
* @param preserveOrder {@code true} to preserve the order of JSON
|
||||
* object members in parameters.
|
||||
*
|
||||
* @return The corresponding JSON-RPC 2.0 notification object.
|
||||
*
|
||||
* @throws JSONRPC2ParseException With detailed message if parsing
|
||||
* failed.
|
||||
*/
|
||||
public static JSONRPC2Notification parse(final String jsonString,
|
||||
final boolean preserveOrder)
|
||||
throws JSONRPC2ParseException {
|
||||
|
||||
return parse(jsonString, preserveOrder, false, false);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Parses a JSON-RPC 2.0 notification string. This method is
|
||||
* thread-safe.
|
||||
*
|
||||
* @param jsonString The JSON-RPC 2.0 notification string, UTF-8
|
||||
* encoded. Must not be {@code null}.
|
||||
* @param preserveOrder {@code true} to preserve the order of JSON
|
||||
* object members in parameters.
|
||||
* @param ignoreVersion {@code true} to skip a check of the
|
||||
* {@code "jsonrpc":"2.0"} version attribute in the
|
||||
* JSON-RPC 2.0 message.
|
||||
*
|
||||
* @return The corresponding JSON-RPC 2.0 notification object.
|
||||
*
|
||||
* @throws JSONRPC2ParseException With detailed message if parsing
|
||||
* failed.
|
||||
*/
|
||||
public static JSONRPC2Notification parse(final String jsonString,
|
||||
final boolean preserveOrder,
|
||||
final boolean ignoreVersion)
|
||||
throws JSONRPC2ParseException {
|
||||
|
||||
return parse(jsonString, preserveOrder, ignoreVersion, false);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Parses a JSON-RPC 2.0 notification string. This method is
|
||||
* thread-safe.
|
||||
*
|
||||
* @param jsonString The JSON-RPC 2.0 notification string,
|
||||
* UTF-8 encoded. Must not be {@code null}.
|
||||
* @param preserveOrder {@code true} to preserve the order of
|
||||
* JSON object members in parameters.
|
||||
* @param ignoreVersion {@code true} to skip a check of the
|
||||
* {@code "jsonrpc":"2.0"} version
|
||||
* attribute in the JSON-RPC 2.0 message.
|
||||
* @param parseNonStdAttributes {@code true} to parse non-standard
|
||||
* attributes found in the JSON-RPC 2.0
|
||||
* message.
|
||||
*
|
||||
* @return The corresponding JSON-RPC 2.0 notification object.
|
||||
*
|
||||
* @throws JSONRPC2ParseException With detailed message if parsing
|
||||
* failed.
|
||||
*/
|
||||
public static JSONRPC2Notification parse(final String jsonString,
|
||||
final boolean preserveOrder,
|
||||
final boolean ignoreVersion,
|
||||
final boolean parseNonStdAttributes)
|
||||
throws JSONRPC2ParseException {
|
||||
|
||||
JSONRPC2Parser parser = new JSONRPC2Parser(preserveOrder, ignoreVersion, parseNonStdAttributes);
|
||||
|
||||
return parser.parseJSONRPC2Notification(jsonString);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Constructs a new JSON-RPC 2.0 notification with no parameters.
|
||||
*
|
||||
* @param method The name of the requested method. Must not be
|
||||
* {@code null}.
|
||||
*/
|
||||
public JSONRPC2Notification(final String method) {
|
||||
|
||||
setMethod(method);
|
||||
setParams(null);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Constructs a new JSON-RPC 2.0 notification with positional (JSON
|
||||
* array) parameters.
|
||||
*
|
||||
* @param method The name of the requested method. Must not
|
||||
* be {@code null}.
|
||||
* @param positionalParams The positional (JSON array) parameters,
|
||||
* {@code null} if none.
|
||||
*/
|
||||
public JSONRPC2Notification(final String method,
|
||||
final List<Object> positionalParams) {
|
||||
|
||||
setMethod(method);
|
||||
setPositionalParams(positionalParams);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Constructs a new JSON-RPC 2.0 notification with named (JSON object)
|
||||
* parameters.
|
||||
*
|
||||
* @param method The name of the requested method.
|
||||
* @param namedParams The named (JSON object) parameters, {@code null}
|
||||
* if none.
|
||||
*/
|
||||
public JSONRPC2Notification(final String method,
|
||||
final Map <String,Object> namedParams) {
|
||||
|
||||
setMethod(method);
|
||||
setNamedParams(namedParams);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets the name of the requested method.
|
||||
*
|
||||
* @return The method name.
|
||||
*/
|
||||
public String getMethod() {
|
||||
|
||||
return method;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets the name of the requested method.
|
||||
*
|
||||
* @param method The method name. Must not be {@code null}.
|
||||
*/
|
||||
public void setMethod(final String method) {
|
||||
|
||||
// The method name is mandatory
|
||||
if (method == null)
|
||||
throw new IllegalArgumentException("The method name must not be null");
|
||||
|
||||
this.method = method;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets the parameters type ({@link JSONRPC2ParamsType#ARRAY positional},
|
||||
* {@link JSONRPC2ParamsType#OBJECT named} or
|
||||
* {@link JSONRPC2ParamsType#NO_PARAMS none}).
|
||||
*
|
||||
* @return The parameters type.
|
||||
*/
|
||||
public JSONRPC2ParamsType getParamsType() {
|
||||
|
||||
if (positionalParams == null && namedParams == null)
|
||||
return JSONRPC2ParamsType.NO_PARAMS;
|
||||
|
||||
if (positionalParams != null)
|
||||
return JSONRPC2ParamsType.ARRAY;
|
||||
|
||||
if (namedParams != null)
|
||||
return JSONRPC2ParamsType.OBJECT;
|
||||
|
||||
else
|
||||
return JSONRPC2ParamsType.NO_PARAMS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the notification parameters.
|
||||
*
|
||||
* <p>This method was deprecated in version 1.30. Use
|
||||
* {@link #getPositionalParams} or {@link #getNamedParams} instead.
|
||||
*
|
||||
* @return The parameters as {@code List<Object>} for positional
|
||||
* (JSON array), {@code Map<String,Object>} for named
|
||||
* (JSON object), or {@code null} if none.
|
||||
*/
|
||||
@Deprecated
|
||||
public Object getParams() {
|
||||
|
||||
switch (getParamsType()) {
|
||||
|
||||
case ARRAY:
|
||||
return positionalParams;
|
||||
|
||||
case OBJECT:
|
||||
return namedParams;
|
||||
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets the positional (JSON array) parameters.
|
||||
*
|
||||
* @since 1.30
|
||||
*
|
||||
* @return The positional (JSON array) parameters, {@code null} if none
|
||||
* or named.
|
||||
*/
|
||||
public List<Object> getPositionalParams() {
|
||||
|
||||
return positionalParams;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets the named parameters.
|
||||
*
|
||||
* @since 1.30
|
||||
*
|
||||
* @return The named (JSON object) parameters, {@code null} if none or
|
||||
* positional.
|
||||
*/
|
||||
public Map<String,Object> getNamedParams() {
|
||||
|
||||
return namedParams;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets the notification parameters.
|
||||
*
|
||||
* <p>This method was deprecated in version 1.30. Use
|
||||
* {@link #setPositionalParams} or {@link #setNamedParams} instead.
|
||||
*
|
||||
* @param params The parameters. For positional (JSON array) pass a
|
||||
* {@code List<Object>}. For named (JSON object)
|
||||
* pass a {@code Map<String,Object>}. If there are
|
||||
* no parameters pass {@code null}.
|
||||
*/
|
||||
@Deprecated
|
||||
@SuppressWarnings("unchecked")
|
||||
public void setParams(final Object params) {
|
||||
|
||||
if (params == null) {
|
||||
positionalParams = null;
|
||||
namedParams = null;
|
||||
} else if (params instanceof List) {
|
||||
positionalParams = (List<Object>) params;
|
||||
} else if (params instanceof Map) {
|
||||
namedParams = (Map<String, Object>) params;
|
||||
} else {
|
||||
throw new IllegalArgumentException("The notification parameters must be of type List, Map or null");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets the positional (JSON array) request parameters.
|
||||
*
|
||||
* @since 1.30
|
||||
*
|
||||
* @param positionalParams The positional (JSON array) request
|
||||
* parameters, {@code null} if none.
|
||||
*/
|
||||
public void setPositionalParams(final List<Object> positionalParams) {
|
||||
|
||||
if (positionalParams == null)
|
||||
return;
|
||||
|
||||
this.positionalParams = positionalParams;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets the named (JSON object) request parameters.
|
||||
*
|
||||
* @since 1.30
|
||||
*
|
||||
* @param namedParams The named (JSON object) request parameters,
|
||||
* {@code null} if none.
|
||||
*/
|
||||
public void setNamedParams(final Map<String,Object> namedParams) {
|
||||
|
||||
if (namedParams == null)
|
||||
return;
|
||||
|
||||
this.namedParams = namedParams;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public JSONObject toJSONObject() {
|
||||
|
||||
JSONObject notf = new JSONObject();
|
||||
|
||||
notf.put("method", method);
|
||||
|
||||
// The params can be omitted if none
|
||||
switch (getParamsType()) {
|
||||
|
||||
case ARRAY:
|
||||
notf.put("params", positionalParams);
|
||||
break;
|
||||
|
||||
case OBJECT:
|
||||
notf.put("params", namedParams);
|
||||
break;
|
||||
}
|
||||
|
||||
notf.put("jsonrpc", "2.0");
|
||||
|
||||
|
||||
Map <String,Object> nonStdAttributes = getNonStdAttributes();
|
||||
|
||||
if (nonStdAttributes != null) {
|
||||
|
||||
for (final Map.Entry<String,Object> attr: nonStdAttributes.entrySet())
|
||||
notf.put(attr.getKey(), attr.getValue());
|
||||
}
|
||||
|
||||
return notf;
|
||||
}
|
||||
}
|
@@ -0,0 +1,37 @@
|
||||
package com.thetransactioncompany.jsonrpc2;
|
||||
|
||||
|
||||
/**
|
||||
* Enumeration of the three parameter types in JSON-RPC 2.0 requests and
|
||||
* notifications.
|
||||
*
|
||||
* <ul>
|
||||
* <li>{@link #NO_PARAMS} The method takes no parameters.
|
||||
* <li>{@link #ARRAY} The method takes positional parameters, packed as a
|
||||
* JSON array, e.g. {@code ["val1", "val2", ...]}.
|
||||
* <li>{@link #OBJECT} The method takes named parameters, packed as a JSON
|
||||
* object, e.g. {@code {"param1":"val1", "param2":"val2", ...}}.
|
||||
* </ul>
|
||||
*
|
||||
* @author Vladimir Dzhuvinov
|
||||
*/
|
||||
public enum JSONRPC2ParamsType {
|
||||
|
||||
|
||||
/**
|
||||
* No parameters.
|
||||
*/
|
||||
NO_PARAMS,
|
||||
|
||||
|
||||
/**
|
||||
* Positional parameters, packed as a JSON array.
|
||||
*/
|
||||
ARRAY,
|
||||
|
||||
|
||||
/**
|
||||
* Named parameters, packed as a JSON object.
|
||||
*/
|
||||
OBJECT
|
||||
}
|
@@ -0,0 +1,113 @@
|
||||
package com.thetransactioncompany.jsonrpc2;
|
||||
|
||||
|
||||
/**
|
||||
* Thrown to indicate an exception during the parsing of a JSON-RPC 2.0
|
||||
* message string.
|
||||
*
|
||||
* @author Vladimir Dzhuvinov
|
||||
*/
|
||||
public class JSONRPC2ParseException extends Exception {
|
||||
|
||||
|
||||
/**
|
||||
* Serial version UID.
|
||||
*/
|
||||
private static final long serialVersionUID = 3376608778436136410L;
|
||||
|
||||
|
||||
/**
|
||||
* Indicates a parse exception caused by a JSON message not conforming
|
||||
* to the JSON-RPC 2.0 protocol.
|
||||
*/
|
||||
public static final int PROTOCOL = 0;
|
||||
|
||||
|
||||
/**
|
||||
* Indicates a parse exception caused by invalid JSON.
|
||||
*/
|
||||
public static final int JSON = 1;
|
||||
|
||||
|
||||
/**
|
||||
* The parse exception cause type. Default is {@link #PROTOCOL}.
|
||||
*/
|
||||
private int causeType = PROTOCOL;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* The string that could't be parsed.
|
||||
*/
|
||||
private String unparsableString = null;
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new parse exception with the specified message. The cause
|
||||
* type is set to {@link #PROTOCOL}.
|
||||
*
|
||||
* @param message The exception message.
|
||||
*/
|
||||
public JSONRPC2ParseException(final String message) {
|
||||
|
||||
super(message);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new parse exception with the specified message and the
|
||||
* original string that didn't parse. The cause type is set to
|
||||
* {@link #PROTOCOL}.
|
||||
*
|
||||
* @param message The exception message.
|
||||
* @param unparsableString The unparsable string.
|
||||
*/
|
||||
public JSONRPC2ParseException(final String message, final String unparsableString) {
|
||||
|
||||
super(message);
|
||||
this.unparsableString = unparsableString;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new parse exception with the specified message, cause type
|
||||
* and the original string that didn't parse.
|
||||
*
|
||||
* @param message The exception message.
|
||||
* @param causeType The exception cause type, either
|
||||
* {@link #PROTOCOL} or {@link #JSON}.
|
||||
* @param unparsableString The unparsable string.
|
||||
*/
|
||||
public JSONRPC2ParseException(final String message, final int causeType, final String unparsableString) {
|
||||
|
||||
super(message);
|
||||
|
||||
if (causeType != PROTOCOL && causeType != JSON)
|
||||
throw new IllegalArgumentException("Cause type must be either PROTOCOL or JSON");
|
||||
|
||||
this.causeType = causeType;
|
||||
this.unparsableString = unparsableString;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets the parse exception cause type.
|
||||
*
|
||||
* @return The cause type, either {@link #PROTOCOL} or {@link #JSON}.
|
||||
*/
|
||||
public int getCauseType() {
|
||||
|
||||
return causeType;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets original string that caused the parse exception (if specified).
|
||||
*
|
||||
* @return The string that didn't parse, {@code null} if none.
|
||||
*/
|
||||
public String getUnparsableString() {
|
||||
|
||||
return unparsableString;
|
||||
}
|
||||
}
|
@@ -0,0 +1,654 @@
|
||||
package com.thetransactioncompany.jsonrpc2;
|
||||
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.json.simple.parser.ContainerFactory;
|
||||
import org.json.simple.parser.JSONParser;
|
||||
import org.json.simple.parser.ParseException;
|
||||
|
||||
|
||||
/**
|
||||
* Parses JSON-RPC 2.0 request, notification and response messages.
|
||||
*
|
||||
* <p>Parsing of batched requests / notifications is not supported.
|
||||
*
|
||||
* <p>This class is not thread-safe. A parser instance should not be used by
|
||||
* more than one thread unless properly synchronised. Alternatively, you may
|
||||
* use the thread-safe {@link JSONRPC2Message#parse} and its sister methods.
|
||||
*
|
||||
* <p>Example:
|
||||
*
|
||||
* <pre>
|
||||
* String jsonString = "{\"method\":\"makePayment\"," +
|
||||
* "\"params\":{\"recipient\":\"Penny Adams\",\"amount\":175.05}," +
|
||||
* "\"id\":\"0001\","+
|
||||
* "\"jsonrpc\":\"2.0\"}";
|
||||
*
|
||||
* JSONRPC2Request req = null;
|
||||
*
|
||||
* JSONRPC2Parser parser = new JSONRPC2Parser();
|
||||
*
|
||||
* try {
|
||||
* req = parser.parseJSONRPC2Request(jsonString);
|
||||
*
|
||||
* } catch (JSONRPC2ParseException e) {
|
||||
* // handle exception
|
||||
* }
|
||||
*
|
||||
* </pre>
|
||||
*
|
||||
* <p id="map">The mapping between JSON and Java entities (as defined by the
|
||||
* underlying JSON Smart library):
|
||||
*
|
||||
* <pre>
|
||||
* true|false <---> java.lang.Boolean
|
||||
* number <---> java.lang.Number
|
||||
* string <---> java.lang.String
|
||||
* array <---> java.util.List
|
||||
* object <---> java.util.Map
|
||||
* null <---> null
|
||||
* </pre>
|
||||
*
|
||||
* @author Vladimir Dzhuvinov
|
||||
*/
|
||||
public class JSONRPC2Parser {
|
||||
|
||||
|
||||
/**
|
||||
* Reusable JSON parser. Not thread-safe!
|
||||
*/
|
||||
private final JSONParser parser;
|
||||
|
||||
|
||||
/**
|
||||
* If {@code true} the order of the parsed JSON object members must be
|
||||
* preserved.
|
||||
*/
|
||||
private boolean preserveOrder;
|
||||
|
||||
|
||||
/**
|
||||
* If {@code true} the {@code "jsonrpc":"2.0"} version attribute in the
|
||||
* JSON-RPC 2.0 message must be ignored during parsing.
|
||||
*/
|
||||
private boolean ignoreVersion;
|
||||
|
||||
|
||||
/**
|
||||
* If {@code true} non-standard JSON-RPC 2.0 message attributes must be
|
||||
* parsed too.
|
||||
*/
|
||||
private boolean parseNonStdAttributes;
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new JSON-RPC 2.0 message parser.
|
||||
*
|
||||
* <p>The member order of parsed JSON objects in parameters and results
|
||||
* will not be preserved; strict checking of the 2.0 JSON-RPC version
|
||||
* attribute will be enforced; non-standard message attributes will be
|
||||
* ignored. Check the other constructors if you want to specify
|
||||
* different behaviour.
|
||||
*/
|
||||
public JSONRPC2Parser() {
|
||||
|
||||
this(false, false, false);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new JSON-RPC 2.0 message parser.
|
||||
*
|
||||
* <p>Strict checking of the 2.0 JSON-RPC version attribute will be
|
||||
* enforced; non-standard message attributes will be ignored. Check the
|
||||
* other constructors if you want to specify different behaviour.
|
||||
*
|
||||
* @param preserveOrder If {@code true} the member order of JSON objects
|
||||
* in parameters and results will be preserved.
|
||||
*/
|
||||
public JSONRPC2Parser(final boolean preserveOrder) {
|
||||
|
||||
this(preserveOrder, false, false);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new JSON-RPC 2.0 message parser.
|
||||
*
|
||||
* <p>Non-standard message attributes will be ignored. Check the other
|
||||
* constructors if you want to specify different behaviour.
|
||||
*
|
||||
* @param preserveOrder If {@code true} the member order of JSON objects
|
||||
* in parameters and results will be preserved.
|
||||
* @param ignoreVersion If {@code true} the {@code "jsonrpc":"2.0"}
|
||||
* version attribute in the JSON-RPC 2.0 message
|
||||
* will not be checked.
|
||||
*/
|
||||
public JSONRPC2Parser(final boolean preserveOrder,
|
||||
final boolean ignoreVersion) {
|
||||
|
||||
this(preserveOrder, ignoreVersion, false);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new JSON-RPC 2.0 message parser.
|
||||
*
|
||||
* <p>This constructor allows full specification of the available
|
||||
* JSON-RPC message parsing properties.
|
||||
*
|
||||
* @param preserveOrder If {@code true} the member order of JSON
|
||||
* objects in parameters and results will
|
||||
* be preserved.
|
||||
* @param ignoreVersion If {@code true} the
|
||||
* {@code "jsonrpc":"2.0"} version
|
||||
* attribute in the JSON-RPC 2.0 message
|
||||
* will not be checked.
|
||||
* @param parseNonStdAttributes If {@code true} non-standard attributes
|
||||
* found in the JSON-RPC 2.0 messages will
|
||||
* be parsed too.
|
||||
*/
|
||||
public JSONRPC2Parser(final boolean preserveOrder,
|
||||
final boolean ignoreVersion,
|
||||
final boolean parseNonStdAttributes) {
|
||||
|
||||
// Numbers parsed as long/double, requires JSON Smart 1.0.9+
|
||||
parser = new JSONParser();
|
||||
|
||||
this.preserveOrder = preserveOrder;
|
||||
this.ignoreVersion = ignoreVersion;
|
||||
this.parseNonStdAttributes = parseNonStdAttributes;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Parses a JSON object string. Provides the initial parsing of
|
||||
* JSON-RPC 2.0 messages. The member order of JSON objects will be
|
||||
* preserved if {@link #preserveOrder} is set to {@code true}.
|
||||
*
|
||||
* @param jsonString The JSON string to parse. Must not be
|
||||
* {@code null}.
|
||||
*
|
||||
* @return The parsed JSON object.
|
||||
*
|
||||
* @throws JSONRPC2ParseException With detailed message if parsing
|
||||
* failed.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private Map<String,Object> parseJSONObject(final String jsonString)
|
||||
throws JSONRPC2ParseException {
|
||||
|
||||
if (jsonString.trim().length()==0)
|
||||
throw new JSONRPC2ParseException("Invalid JSON: Empty string",
|
||||
JSONRPC2ParseException.JSON,
|
||||
jsonString);
|
||||
|
||||
Object json;
|
||||
|
||||
// Parse the JSON string
|
||||
try {
|
||||
//if (preserveOrder)
|
||||
// json = parser.parse(jsonString, ContainerFactory.FACTORY_ORDERED);
|
||||
|
||||
//else
|
||||
json = parser.parse(jsonString);
|
||||
|
||||
} catch (ParseException e) {
|
||||
|
||||
// Terse message, do not include full parse exception message
|
||||
throw new JSONRPC2ParseException("Invalid JSON",
|
||||
JSONRPC2ParseException.JSON,
|
||||
jsonString);
|
||||
}
|
||||
|
||||
if (json instanceof List)
|
||||
throw new JSONRPC2ParseException("JSON-RPC 2.0 batch requests/notifications not supported", jsonString);
|
||||
|
||||
if (! (json instanceof Map))
|
||||
throw new JSONRPC2ParseException("Invalid JSON-RPC 2.0 message: Message must be a JSON object", jsonString);
|
||||
|
||||
return (Map<String,Object>)json;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Ensures the specified parameter is a {@code String} object set to
|
||||
* "2.0". This method is intended to check the "jsonrpc" attribute
|
||||
* during parsing of JSON-RPC messages.
|
||||
*
|
||||
* @param version The version parameter. Must not be {@code null}.
|
||||
* @param jsonString The original JSON string.
|
||||
*
|
||||
* @throws JSONRPC2ParseException If the parameter is not a string that
|
||||
* equals "2.0".
|
||||
*/
|
||||
private static void ensureVersion2(final Object version, final String jsonString)
|
||||
throws JSONRPC2ParseException {
|
||||
|
||||
if (version == null)
|
||||
throw new JSONRPC2ParseException("Invalid JSON-RPC 2.0: Version string missing", jsonString);
|
||||
|
||||
else if (! (version instanceof String))
|
||||
throw new JSONRPC2ParseException("Invalid JSON-RPC 2.0: Version not a JSON string", jsonString);
|
||||
|
||||
else if (! version.equals("2.0"))
|
||||
throw new JSONRPC2ParseException("Invalid JSON-RPC 2.0: Version must be \"2.0\"", jsonString);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Provides common parsing of JSON-RPC 2.0 requests, notifications
|
||||
* and responses. Use this method if you don't know which type of
|
||||
* JSON-RPC message the input string represents.
|
||||
*
|
||||
* <p>If a particular message type is expected use the dedicated
|
||||
* {@link #parseJSONRPC2Request}, {@link #parseJSONRPC2Notification}
|
||||
* and {@link #parseJSONRPC2Response} methods. They are more efficient
|
||||
* and would provide you with more detailed parse error reporting.
|
||||
*
|
||||
* @param jsonString A JSON string representing a JSON-RPC 2.0 request,
|
||||
* notification or response, UTF-8 encoded. Must not
|
||||
* be {@code null}.
|
||||
*
|
||||
* @return An instance of {@link JSONRPC2Request},
|
||||
* {@link JSONRPC2Notification} or {@link JSONRPC2Response}.
|
||||
*
|
||||
* @throws JSONRPC2ParseException With detailed message if the parsing
|
||||
* failed.
|
||||
*/
|
||||
public JSONRPC2Message parseJSONRPC2Message(final String jsonString)
|
||||
throws JSONRPC2ParseException {
|
||||
|
||||
// Try each of the parsers until one succeeds (or all fail)
|
||||
|
||||
try {
|
||||
return parseJSONRPC2Request(jsonString);
|
||||
|
||||
} catch (JSONRPC2ParseException e) {
|
||||
|
||||
// throw on JSON error, ignore on protocol error
|
||||
if (e.getCauseType() == JSONRPC2ParseException.JSON)
|
||||
throw e;
|
||||
}
|
||||
|
||||
try {
|
||||
return parseJSONRPC2Notification(jsonString);
|
||||
|
||||
} catch (JSONRPC2ParseException e) {
|
||||
|
||||
// throw on JSON error, ignore on protocol error
|
||||
if (e.getCauseType() == JSONRPC2ParseException.JSON)
|
||||
throw e;
|
||||
}
|
||||
|
||||
try {
|
||||
return parseJSONRPC2Response(jsonString);
|
||||
|
||||
} catch (JSONRPC2ParseException e) {
|
||||
|
||||
// throw on JSON error, ignore on protocol error
|
||||
if (e.getCauseType() == JSONRPC2ParseException.JSON)
|
||||
throw e;
|
||||
}
|
||||
|
||||
throw new JSONRPC2ParseException("Invalid JSON-RPC 2.0 message",
|
||||
JSONRPC2ParseException.PROTOCOL,
|
||||
jsonString);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Parses a JSON-RPC 2.0 request string.
|
||||
*
|
||||
* @param jsonString The JSON-RPC 2.0 request string, UTF-8 encoded.
|
||||
* Must not be {@code null}.
|
||||
*
|
||||
* @return The corresponding JSON-RPC 2.0 request object.
|
||||
*
|
||||
* @throws JSONRPC2ParseException With detailed message if parsing
|
||||
* failed.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public JSONRPC2Request parseJSONRPC2Request(final String jsonString)
|
||||
throws JSONRPC2ParseException {
|
||||
|
||||
// Initial JSON object parsing
|
||||
Map<String,Object> jsonObject = parseJSONObject(jsonString);
|
||||
|
||||
|
||||
// Check for JSON-RPC version "2.0"
|
||||
Object version = jsonObject.remove("jsonrpc");
|
||||
|
||||
if (! ignoreVersion)
|
||||
ensureVersion2(version, jsonString);
|
||||
|
||||
|
||||
// Extract method name
|
||||
Object method = jsonObject.remove("method");
|
||||
|
||||
if (method == null)
|
||||
throw new JSONRPC2ParseException("Invalid JSON-RPC 2.0 request: Method name missing", jsonString);
|
||||
|
||||
else if (! (method instanceof String))
|
||||
throw new JSONRPC2ParseException("Invalid JSON-RPC 2.0 request: Method name not a JSON string", jsonString);
|
||||
|
||||
else if (((String)method).length() == 0)
|
||||
throw new JSONRPC2ParseException("Invalid JSON-RPC 2.0 request: Method name is an empty string", jsonString);
|
||||
|
||||
|
||||
// Extract ID
|
||||
if (! jsonObject.containsKey("id"))
|
||||
throw new JSONRPC2ParseException("Invalid JSON-RPC 2.0 request: Missing identifier", jsonString);
|
||||
|
||||
Object id = jsonObject.remove("id");
|
||||
|
||||
if ( id != null &&
|
||||
!(id instanceof Number ) &&
|
||||
!(id instanceof Boolean) &&
|
||||
!(id instanceof String ) )
|
||||
throw new JSONRPC2ParseException("Invalid JSON-RPC 2.0 request: Identifier not a JSON scalar", jsonString);
|
||||
|
||||
|
||||
// Extract params
|
||||
Object params = jsonObject.remove("params");
|
||||
|
||||
|
||||
JSONRPC2Request request;
|
||||
|
||||
if (params == null)
|
||||
request = new JSONRPC2Request((String)method, id);
|
||||
|
||||
else if (params instanceof List)
|
||||
request = new JSONRPC2Request((String)method, (List<Object>)params, id);
|
||||
|
||||
else if (params instanceof Map)
|
||||
request = new JSONRPC2Request((String)method, (Map<String,Object>)params, id);
|
||||
|
||||
else
|
||||
throw new JSONRPC2ParseException("Invalid JSON-RPC 2.0 request: Method parameters have unexpected JSON type", jsonString);
|
||||
|
||||
|
||||
// Extract remaining non-std params?
|
||||
if (parseNonStdAttributes) {
|
||||
|
||||
for (Map.Entry<String,Object> entry: jsonObject.entrySet()) {
|
||||
|
||||
request.appendNonStdAttribute(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
return request;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Parses a JSON-RPC 2.0 notification string.
|
||||
*
|
||||
* @param jsonString The JSON-RPC 2.0 notification string, UTF-8
|
||||
* encoded. Must not be {@code null}.
|
||||
*
|
||||
* @return The corresponding JSON-RPC 2.0 notification object.
|
||||
*
|
||||
* @throws JSONRPC2ParseException With detailed message if parsing
|
||||
* failed.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public JSONRPC2Notification parseJSONRPC2Notification(final String jsonString)
|
||||
throws JSONRPC2ParseException {
|
||||
|
||||
// Initial JSON object parsing
|
||||
Map<String,Object> jsonObject = parseJSONObject(jsonString);
|
||||
|
||||
|
||||
// Check for JSON-RPC version "2.0"
|
||||
Object version = jsonObject.remove("jsonrpc");
|
||||
|
||||
if (! ignoreVersion)
|
||||
ensureVersion2(version, jsonString);
|
||||
|
||||
|
||||
// Extract method name
|
||||
Object method = jsonObject.remove("method");
|
||||
|
||||
if (method == null)
|
||||
throw new JSONRPC2ParseException("Invalid JSON-RPC 2.0 notification: Method name missing", jsonString);
|
||||
|
||||
else if (! (method instanceof String))
|
||||
throw new JSONRPC2ParseException("Invalid JSON-RPC 2.0 notification: Method name not a JSON string", jsonString);
|
||||
|
||||
else if (((String)method).length() == 0)
|
||||
throw new JSONRPC2ParseException("Invalid JSON-RPC 2.0 notification: Method name is an empty string", jsonString);
|
||||
|
||||
|
||||
// Extract params
|
||||
Object params = jsonObject.get("params");
|
||||
|
||||
JSONRPC2Notification notification;
|
||||
|
||||
if (params == null)
|
||||
notification = new JSONRPC2Notification((String)method);
|
||||
|
||||
else if (params instanceof List)
|
||||
notification = new JSONRPC2Notification((String)method, (List<Object>)params);
|
||||
|
||||
else if (params instanceof Map)
|
||||
notification = new JSONRPC2Notification((String)method, (Map<String,Object>)params);
|
||||
else
|
||||
throw new JSONRPC2ParseException("Invalid JSON-RPC 2.0 notification: Method parameters have unexpected JSON type", jsonString);
|
||||
|
||||
// Extract remaining non-std params?
|
||||
if (parseNonStdAttributes) {
|
||||
|
||||
for (Map.Entry<String,Object> entry: jsonObject.entrySet()) {
|
||||
|
||||
notification.appendNonStdAttribute(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
return notification;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Parses a JSON-RPC 2.0 response string.
|
||||
*
|
||||
* @param jsonString The JSON-RPC 2.0 response string, UTF-8 encoded.
|
||||
* Must not be {@code null}.
|
||||
*
|
||||
* @return The corresponding JSON-RPC 2.0 response object.
|
||||
*
|
||||
* @throws JSONRPC2ParseException With detailed message if parsing
|
||||
* failed.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public JSONRPC2Response parseJSONRPC2Response(final String jsonString)
|
||||
throws JSONRPC2ParseException {
|
||||
|
||||
// Initial JSON object parsing
|
||||
Map<String,Object> jsonObject = parseJSONObject(jsonString);
|
||||
|
||||
// Check for JSON-RPC version "2.0"
|
||||
Object version = jsonObject.remove("jsonrpc");
|
||||
|
||||
if (! ignoreVersion)
|
||||
ensureVersion2(version, jsonString);
|
||||
|
||||
|
||||
// Extract request ID
|
||||
Object id = jsonObject.remove("id");
|
||||
|
||||
if ( id != null &&
|
||||
! (id instanceof Boolean) &&
|
||||
! (id instanceof Number ) &&
|
||||
! (id instanceof String ) )
|
||||
throw new JSONRPC2ParseException("Invalid JSON-RPC 2.0 response: Identifier not a JSON scalar", jsonString);
|
||||
|
||||
|
||||
// Extract result/error and create response object
|
||||
// Note: result and error are mutually exclusive
|
||||
|
||||
JSONRPC2Response response;
|
||||
|
||||
if (jsonObject.containsKey("result") && ! jsonObject.containsKey("error")) {
|
||||
|
||||
// Success
|
||||
Object res = jsonObject.remove("result");
|
||||
|
||||
response = new JSONRPC2Response(res, id);
|
||||
|
||||
}
|
||||
else if (! jsonObject.containsKey("result") && jsonObject.containsKey("error")) {
|
||||
|
||||
// Error JSON object
|
||||
Object errorJSON = jsonObject.remove("error");
|
||||
|
||||
if (errorJSON == null)
|
||||
throw new JSONRPC2ParseException("Invalid JSON-RPC 2.0 response: Missing error object", jsonString);
|
||||
|
||||
|
||||
if (! (errorJSON instanceof Map))
|
||||
throw new JSONRPC2ParseException("Invalid JSON-RPC 2.0 response: Error object not a JSON object");
|
||||
|
||||
|
||||
Map<String,Object> error = (Map<String,Object>)errorJSON;
|
||||
|
||||
|
||||
int errorCode;
|
||||
|
||||
try {
|
||||
errorCode = ((Number)error.get("code")).intValue();
|
||||
|
||||
} catch (Exception e) {
|
||||
|
||||
throw new JSONRPC2ParseException("Invalid JSON-RPC 2.0 response: Error code missing or not an integer", jsonString);
|
||||
}
|
||||
|
||||
String errorMessage;
|
||||
|
||||
try {
|
||||
errorMessage = (String)error.get("message");
|
||||
|
||||
} catch (Exception e) {
|
||||
|
||||
throw new JSONRPC2ParseException("Invalid JSON-RPC 2.0 response: Error message missing or not a string", jsonString);
|
||||
}
|
||||
|
||||
Object errorData = error.get("data");
|
||||
|
||||
response = new JSONRPC2Response(new JSONRPC2Error(errorCode, errorMessage, errorData), id);
|
||||
|
||||
}
|
||||
else if (jsonObject.containsKey("result") && jsonObject.containsKey("error")) {
|
||||
|
||||
// Invalid response
|
||||
throw new JSONRPC2ParseException("Invalid JSON-RPC 2.0 response: You cannot have result and error at the same time", jsonString);
|
||||
}
|
||||
else if (! jsonObject.containsKey("result") && ! jsonObject.containsKey("error")){
|
||||
|
||||
// Invalid response
|
||||
throw new JSONRPC2ParseException("Invalid JSON-RPC 2.0 response: Neither result nor error specified", jsonString);
|
||||
}
|
||||
else {
|
||||
throw new AssertionError();
|
||||
}
|
||||
|
||||
|
||||
// Extract remaining non-std params?
|
||||
if (parseNonStdAttributes) {
|
||||
|
||||
for (Map.Entry<String,Object> entry: jsonObject.entrySet()) {
|
||||
|
||||
response.appendNonStdAttribute(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Controls the preservation of JSON object member order in parsed
|
||||
* JSON-RPC 2.0 messages.
|
||||
*
|
||||
* @param preserveOrder {@code true} to preserve the order of JSON
|
||||
* object members, else {@code false}.
|
||||
*/
|
||||
public void preserveOrder(final boolean preserveOrder) {
|
||||
|
||||
this.preserveOrder = preserveOrder;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns {@code true} if the order of JSON object members in parsed
|
||||
* JSON-RPC 2.0 messages is preserved, else {@code false}.
|
||||
*
|
||||
* @return {@code true} if order is preserved, else {@code false}.
|
||||
*/
|
||||
public boolean preservesOrder() {
|
||||
|
||||
return preserveOrder;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Specifies whether to ignore the {@code "jsonrpc":"2.0"} version
|
||||
* attribute during parsing of JSON-RPC 2.0 messages.
|
||||
*
|
||||
* <p>You may with to disable strict 2.0 version checking if the parsed
|
||||
* JSON-RPC 2.0 messages don't include a version attribute or if you
|
||||
* wish to achieve limited compatibility with older JSON-RPC protocol
|
||||
* versions.
|
||||
*
|
||||
* @param ignore {@code true} to skip checks of the
|
||||
* {@code "jsonrpc":"2.0"} version attribute in parsed
|
||||
* JSON-RPC 2.0 messages, else {@code false}.
|
||||
*/
|
||||
public void ignoreVersion(final boolean ignore) {
|
||||
|
||||
ignoreVersion = ignore;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns {@code true} if the {@code "jsonrpc":"2.0"} version
|
||||
* attribute in parsed JSON-RPC 2.0 messages is ignored, else
|
||||
* {@code false}.
|
||||
*
|
||||
* @return {@code true} if the {@code "jsonrpc":"2.0"} version
|
||||
* attribute in parsed JSON-RPC 2.0 messages is ignored, else
|
||||
* {@code false}.
|
||||
*/
|
||||
public boolean ignoresVersion() {
|
||||
|
||||
return ignoreVersion;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Specifies whether to parse non-standard attributes found in JSON-RPC
|
||||
* 2.0 messages.
|
||||
*
|
||||
* @param enable {@code true} to parse non-standard attributes, else
|
||||
* {@code false}.
|
||||
*/
|
||||
public void parseNonStdAttributes(final boolean enable) {
|
||||
|
||||
parseNonStdAttributes = enable;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns {@code true} if non-standard attributes in JSON-RPC 2.0
|
||||
* messages are parsed.
|
||||
*
|
||||
* @return {@code true} if non-standard attributes are parsed, else
|
||||
* {@code false}.
|
||||
*/
|
||||
public boolean parsesNonStdAttributes() {
|
||||
|
||||
return parseNonStdAttributes;
|
||||
}
|
||||
}
|
@@ -0,0 +1,507 @@
|
||||
package com.thetransactioncompany.jsonrpc2;
|
||||
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.json.simple.JSONObject;
|
||||
|
||||
|
||||
/**
|
||||
* Represents a JSON-RPC 2.0 request.
|
||||
*
|
||||
* <p>A request carries four pieces of data:
|
||||
* <ul>
|
||||
* <li>{@code method} The name of the remote method to call.
|
||||
* <li>{@code params} The required method parameters (if any), which can
|
||||
* be packed into a JSON array or object.
|
||||
* <li>{@code id} An identifier which is echoed back to the client with
|
||||
* the response.
|
||||
* <li>{@code jsonrpc} A string indicating the JSON-RPC protocol version
|
||||
* set to "2.0".
|
||||
* </ul>
|
||||
*
|
||||
* <p>Here is a sample JSON-RPC 2.0 request string:
|
||||
*
|
||||
* <pre>
|
||||
* {
|
||||
* "method" : "makePayment",
|
||||
* "params" : { "recipient" : "Penny Adams", "amount":175.05 },
|
||||
* "id" : "0001",
|
||||
* "jsonrpc" : "2.0"
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* <p>This class provides two methods to obtain a request object:
|
||||
* <ul>
|
||||
* <li>Pass a JSON-RPC 2.0 request string to the static
|
||||
* {@link #parse} method, or
|
||||
* <li>Invoke one of the constructors with the appropriate arguments.
|
||||
* </ul>
|
||||
*
|
||||
* <p>Example 1: Parsing a request string:
|
||||
*
|
||||
* <pre>
|
||||
* String jsonString = "{\"method\":\"makePayment\"," +
|
||||
* "\"params\":{\"recipient\":\"Penny Adams\",\"amount\":175.05}," +
|
||||
* "\"id\":\"0001\","+
|
||||
* "\"jsonrpc\":\"2.0\"}";
|
||||
*
|
||||
* JSONRPC2Request req = null;
|
||||
*
|
||||
* try {
|
||||
* req = JSONRPC2Request.parse(jsonString);
|
||||
*
|
||||
* } catch (JSONRPC2ParseException e) {
|
||||
* // handle exception
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* <p>Example 2: Recreating the above request:
|
||||
*
|
||||
* <pre>
|
||||
* String method = "makePayment";
|
||||
* Map<String,Object> params = new HashMap<String,Object>();
|
||||
* params.put("recipient", "Penny Adams");
|
||||
* params.put("amount", 175.05);
|
||||
* String id = "0001";
|
||||
*
|
||||
* JSONRPC2Request req = new JSONRPC2Request(method, params, id);
|
||||
*
|
||||
* System.out.println(req);
|
||||
* </pre>
|
||||
*
|
||||
* <p id="map">The mapping between JSON and Java entities (as defined by the
|
||||
* underlying JSON Smart library):
|
||||
*
|
||||
* <pre>
|
||||
* true|false <---> java.lang.Boolean
|
||||
* number <---> java.lang.Number
|
||||
* string <---> java.lang.String
|
||||
* array <---> java.util.List
|
||||
* object <---> java.util.Map
|
||||
* null <---> null
|
||||
* </pre>
|
||||
*
|
||||
* @author Vladimir Dzhuvinov
|
||||
*/
|
||||
public class JSONRPC2Request extends JSONRPC2Message {
|
||||
|
||||
|
||||
/**
|
||||
* The method name.
|
||||
*/
|
||||
private String method;
|
||||
|
||||
|
||||
/**
|
||||
* The positional parameters, {@code null} if none.
|
||||
*/
|
||||
private List<Object> positionalParams;
|
||||
|
||||
|
||||
/**
|
||||
* The named parameters, {@code null} if none.
|
||||
*/
|
||||
private Map<String,Object> namedParams;
|
||||
|
||||
|
||||
/**
|
||||
* The request identifier.
|
||||
*/
|
||||
private Object id;
|
||||
|
||||
|
||||
/**
|
||||
* Parses a JSON-RPC 2.0 request string. This method is thread-safe.
|
||||
*
|
||||
* @param jsonString The JSON-RPC 2.0 request string, UTF-8 encoded.
|
||||
* Must not be {@code null}.
|
||||
*
|
||||
* @return The corresponding JSON-RPC 2.0 request object.
|
||||
*
|
||||
* @throws JSONRPC2ParseException With detailed message if parsing
|
||||
* failed.
|
||||
*/
|
||||
public static JSONRPC2Request parse(final String jsonString)
|
||||
throws JSONRPC2ParseException {
|
||||
|
||||
return parse(jsonString, false, false, false);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Parses a JSON-RPC 2.0 request string. This method is thread-safe.
|
||||
*
|
||||
* @param jsonString The JSON-RPC 2.0 request string, UTF-8 encoded.
|
||||
* Must not be {@code null}.
|
||||
* @param preserveOrder {@code true} to preserve the order of JSON
|
||||
* object members in parameters.
|
||||
*
|
||||
* @return The corresponding JSON-RPC 2.0 request object.
|
||||
*
|
||||
* @throws JSONRPC2ParseException With detailed message if parsing
|
||||
* failed.
|
||||
*/
|
||||
public static JSONRPC2Request parse(final String jsonString,
|
||||
final boolean preserveOrder)
|
||||
throws JSONRPC2ParseException {
|
||||
|
||||
return parse(jsonString, preserveOrder, false, false);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Parses a JSON-RPC 2.0 request string. This method is thread-safe.
|
||||
*
|
||||
* @param jsonString The JSON-RPC 2.0 request string, UTF-8 encoded.
|
||||
* Must not be {@code null}.
|
||||
* @param preserveOrder {@code true} to preserve the order of JSON
|
||||
* object members in parameters.
|
||||
* @param ignoreVersion {@code true} to skip a check of the
|
||||
* {@code "jsonrpc":"2.0"} version attribute in the
|
||||
* JSON-RPC 2.0 message.
|
||||
*
|
||||
* @return The corresponding JSON-RPC 2.0 request object.
|
||||
*
|
||||
* @throws JSONRPC2ParseException With detailed message if parsing
|
||||
* failed.
|
||||
*/
|
||||
public static JSONRPC2Request parse(final String jsonString,
|
||||
final boolean preserveOrder,
|
||||
final boolean ignoreVersion)
|
||||
throws JSONRPC2ParseException {
|
||||
|
||||
return parse(jsonString, preserveOrder, ignoreVersion, false);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Parses a JSON-RPC 2.0 request string. This method is thread-safe.
|
||||
*
|
||||
* @param jsonString The JSON-RPC 2.0 request string, UTF-8
|
||||
* encoded. Must not be {@code null}.
|
||||
* @param preserveOrder {@code true} to preserve the order of
|
||||
* JSON object members in parameters.
|
||||
* @param ignoreVersion {@code true} to skip a check of the
|
||||
* {@code "jsonrpc":"2.0"} version
|
||||
* attribute in the JSON-RPC 2.0 message.
|
||||
* @param parseNonStdAttributes {@code true} to parse non-standard
|
||||
* attributes found in the JSON-RPC 2.0
|
||||
* message.
|
||||
*
|
||||
* @return The corresponding JSON-RPC 2.0 request object.
|
||||
*
|
||||
* @throws JSONRPC2ParseException With detailed message if parsing
|
||||
* failed.
|
||||
*/
|
||||
public static JSONRPC2Request parse(final String jsonString,
|
||||
final boolean preserveOrder,
|
||||
final boolean ignoreVersion,
|
||||
final boolean parseNonStdAttributes)
|
||||
throws JSONRPC2ParseException {
|
||||
|
||||
JSONRPC2Parser parser = new JSONRPC2Parser(preserveOrder,
|
||||
ignoreVersion,
|
||||
parseNonStdAttributes);
|
||||
|
||||
return parser.parseJSONRPC2Request(jsonString);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Constructs a new JSON-RPC 2.0 request with no parameters.
|
||||
*
|
||||
* @param method The name of the requested method. Must not be
|
||||
* {@code null}.
|
||||
* @param id The request identifier echoed back to the caller.
|
||||
* The value must <a href="#map">map</a> to a JSON
|
||||
* scalar ({@code null} and fractions, however, should
|
||||
* be avoided).
|
||||
*/
|
||||
public JSONRPC2Request(final String method, final Object id) {
|
||||
|
||||
setMethod(method);
|
||||
setID(id);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Constructs a new JSON-RPC 2.0 request with positional (JSON array)
|
||||
* parameters.
|
||||
*
|
||||
* @param method The name of the requested method. Must not
|
||||
* be {@code null}.
|
||||
* @param positionalParams The positional (JSON array) parameters,
|
||||
* {@code null} if none.
|
||||
* @param id The request identifier echoed back to the
|
||||
* caller. The value must <a href="#map">map</a>
|
||||
* to a JSON scalar ({@code null} and
|
||||
* fractions, however, should be avoided).
|
||||
*/
|
||||
public JSONRPC2Request(final String method,
|
||||
final List<Object> positionalParams,
|
||||
final Object id) {
|
||||
|
||||
setMethod(method);
|
||||
setPositionalParams(positionalParams);
|
||||
setID(id);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Constructs a new JSON-RPC 2.0 request with named (JSON object)
|
||||
* parameters.
|
||||
*
|
||||
* @param method The name of the requested method.
|
||||
* @param namedParams The named (JSON object) parameters, {@code null}
|
||||
* if none.
|
||||
* @param id The request identifier echoed back to the caller.
|
||||
* The value must <a href="#map">map</a> to a JSON
|
||||
* scalar ({@code null} and fractions, however,
|
||||
* should be avoided).
|
||||
*/
|
||||
public JSONRPC2Request(final String method,
|
||||
final Map <String,Object> namedParams,
|
||||
final Object id) {
|
||||
|
||||
setMethod(method);
|
||||
setNamedParams(namedParams);
|
||||
setID(id);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets the name of the requested method.
|
||||
*
|
||||
* @return The method name.
|
||||
*/
|
||||
public String getMethod() {
|
||||
|
||||
return method;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets the name of the requested method.
|
||||
*
|
||||
* @param method The method name. Must not be {@code null}.
|
||||
*/
|
||||
public void setMethod(final String method) {
|
||||
|
||||
// The method name is mandatory
|
||||
if (method == null)
|
||||
throw new IllegalArgumentException("The method name must not be null");
|
||||
|
||||
this.method = method;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets the parameters type ({@link JSONRPC2ParamsType#ARRAY positional},
|
||||
* {@link JSONRPC2ParamsType#OBJECT named} or
|
||||
* {@link JSONRPC2ParamsType#NO_PARAMS none}).
|
||||
*
|
||||
* @return The parameters type.
|
||||
*/
|
||||
public JSONRPC2ParamsType getParamsType() {
|
||||
|
||||
if (positionalParams == null && namedParams == null)
|
||||
return JSONRPC2ParamsType.NO_PARAMS;
|
||||
|
||||
if (positionalParams != null)
|
||||
return JSONRPC2ParamsType.ARRAY;
|
||||
|
||||
if (namedParams != null)
|
||||
return JSONRPC2ParamsType.OBJECT;
|
||||
|
||||
else
|
||||
return JSONRPC2ParamsType.NO_PARAMS;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets the request parameters.
|
||||
*
|
||||
* <p>This method was deprecated in version 1.30. Use
|
||||
* {@link #getPositionalParams} or {@link #getNamedParams} instead.
|
||||
*
|
||||
* @return The parameters as {@code List<Object>} for positional
|
||||
* (JSON array), {@code Map<String,Object>} for named
|
||||
* (JSON object), or {@code null} if none.
|
||||
*/
|
||||
@Deprecated
|
||||
public Object getParams() {
|
||||
|
||||
switch (getParamsType()) {
|
||||
|
||||
case ARRAY:
|
||||
return positionalParams;
|
||||
|
||||
case OBJECT:
|
||||
return namedParams;
|
||||
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets the positional (JSON array) parameters.
|
||||
*
|
||||
* @since 1.30
|
||||
*
|
||||
* @return The positional (JSON array) parameters, {@code null} if none
|
||||
* or named.
|
||||
*/
|
||||
public List<Object> getPositionalParams() {
|
||||
|
||||
return positionalParams;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets the named parameters.
|
||||
*
|
||||
* @since 1.30
|
||||
*
|
||||
* @return The named (JSON object) parameters, {@code null} if none or
|
||||
* positional.
|
||||
*/
|
||||
public Map<String,Object> getNamedParams() {
|
||||
|
||||
return namedParams;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets the request parameters.
|
||||
*
|
||||
* <p>This method was deprecated in version 1.30. Use
|
||||
* {@link #setPositionalParams} or {@link #setNamedParams} instead.
|
||||
*
|
||||
* @param params The parameters. For positional (JSON array) pass a
|
||||
* {@code List<Object>}. For named (JSON object)
|
||||
* pass a {@code Map<String,Object>}. If there are
|
||||
* no parameters pass {@code null}.
|
||||
*/
|
||||
@Deprecated
|
||||
@SuppressWarnings("unchecked")
|
||||
public void setParams(final Object params) {
|
||||
|
||||
if (params == null) {
|
||||
positionalParams = null;
|
||||
namedParams = null;
|
||||
} else if (params instanceof List) {
|
||||
positionalParams = (List<Object>) params;
|
||||
} else if (params instanceof Map) {
|
||||
namedParams = (Map<String, Object>) params;
|
||||
} else {
|
||||
throw new IllegalArgumentException("The request parameters must be of type List, Map or null");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets the positional (JSON array) request parameters.
|
||||
*
|
||||
* @since 1.30
|
||||
*
|
||||
* @param positionalParams The positional (JSON array) request
|
||||
* parameters, {@code null} if none.
|
||||
*/
|
||||
public void setPositionalParams(final List<Object> positionalParams) {
|
||||
|
||||
if (positionalParams == null)
|
||||
return;
|
||||
|
||||
this.positionalParams = positionalParams;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets the named (JSON object) request parameters.
|
||||
*
|
||||
* @since 1.30
|
||||
*
|
||||
* @param namedParams The named (JSON object) request parameters,
|
||||
* {@code null} if none.
|
||||
*/
|
||||
public void setNamedParams(final Map<String,Object> namedParams) {
|
||||
|
||||
if (namedParams == null)
|
||||
return;
|
||||
|
||||
this.namedParams = namedParams;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets the request identifier.
|
||||
*
|
||||
* @return The request identifier ({@code Number}, {@code Boolean},
|
||||
* {@code String}) or {@code null}.
|
||||
*/
|
||||
public Object getID() {
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets the request identifier (ID).
|
||||
*
|
||||
* @param id The request identifier echoed back to the caller.
|
||||
* The value must <a href="#map">map</a> to a JSON
|
||||
* scalar ({@code null} and fractions, however, should
|
||||
* be avoided).
|
||||
*/
|
||||
public void setID(final Object id) {
|
||||
|
||||
if (id == null ||
|
||||
id instanceof Boolean ||
|
||||
id instanceof Number ||
|
||||
id instanceof String
|
||||
) {
|
||||
this.id = id;
|
||||
} else {
|
||||
this.id = id.toString();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public JSONObject toJSONObject() {
|
||||
|
||||
JSONObject req = new JSONObject();
|
||||
|
||||
req.put("method", method);
|
||||
|
||||
// The params can be omitted if none
|
||||
switch (getParamsType()) {
|
||||
|
||||
case ARRAY:
|
||||
req.put("params", positionalParams);
|
||||
break;
|
||||
|
||||
case OBJECT:
|
||||
req.put("params", namedParams);
|
||||
break;
|
||||
}
|
||||
|
||||
req.put("id", id);
|
||||
|
||||
req.put("jsonrpc", "2.0");
|
||||
|
||||
Map <String,Object> nonStdAttributes = getNonStdAttributes();
|
||||
|
||||
if (nonStdAttributes != null) {
|
||||
|
||||
for (final Map.Entry<String,Object> attr: nonStdAttributes.entrySet())
|
||||
req.put(attr.getKey(), attr.getValue());
|
||||
}
|
||||
|
||||
return req;
|
||||
}
|
||||
}
|
@@ -0,0 +1,414 @@
|
||||
package com.thetransactioncompany.jsonrpc2;
|
||||
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.json.simple.JSONObject;
|
||||
|
||||
|
||||
/**
|
||||
* Represents a JSON-RPC 2.0 response.
|
||||
*
|
||||
* <p>A response is returned to the caller after a JSON-RPC 2.0 request has
|
||||
* been processed (notifications, however, don't produce a response). The
|
||||
* response can take two different forms depending on the outcome:
|
||||
*
|
||||
* <ul>
|
||||
* <li>The request was successful. The corresponding response returns
|
||||
* a JSON object with the following information:
|
||||
* <ul>
|
||||
* <li>{@code result} The result, which can be of any JSON type
|
||||
* - a number, a boolean value, a string, an array, an object
|
||||
* or null.
|
||||
* <li>{@code id} The request identifier which is echoed back back
|
||||
* to the caller.
|
||||
* <li>{@code jsonrpc} A string indicating the JSON-RPC protocol
|
||||
* version set to "2.0".
|
||||
* </ul>
|
||||
* <li>The request failed. The returned JSON object contains:
|
||||
* <ul>
|
||||
* <li>{@code error} An object with:
|
||||
* <ul>
|
||||
* <li>{@code code} An integer indicating the error type.
|
||||
* <li>{@code message} A brief error messsage.
|
||||
* <li>{@code data} Optional error data.
|
||||
* </ul>
|
||||
* <li>{@code id} The request identifier. If it couldn't be
|
||||
* determined, e.g. due to a request parse error, the ID is
|
||||
* set to {@code null}.
|
||||
* <li>{@code jsonrpc} A string indicating the JSON-RPC protocol
|
||||
* version set to "2.0".
|
||||
* </ul>
|
||||
* </ul>
|
||||
*
|
||||
* <p>Here is an example JSON-RPC 2.0 response string where the request
|
||||
* has succeeded:
|
||||
*
|
||||
* <pre>
|
||||
* {
|
||||
* "result" : true,
|
||||
* "id" : "req-002",
|
||||
* "jsonrpc" : "2.0"
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
*
|
||||
* <p>And here is an example JSON-RPC 2.0 response string indicating a failure:
|
||||
*
|
||||
* <pre>
|
||||
* {
|
||||
* "error" : { "code" : -32601, "message" : "Method not found" },
|
||||
* "id" : "req-003",
|
||||
* "jsonrpc" : "2.0"
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* <p>A response object is obtained either by passing a valid JSON-RPC 2.0
|
||||
* response string to the static {@link #parse} method or by invoking the
|
||||
* appropriate constructor.
|
||||
*
|
||||
* <p>Here is how parsing is done:
|
||||
*
|
||||
* <pre>
|
||||
* String jsonString = "{\"result\":true,\"id\":\"req-002\",\"jsonrpc\":\"2.0\"}";
|
||||
*
|
||||
* JSONRPC2Response response = null;
|
||||
*
|
||||
* try {
|
||||
* response = JSONRPC2Response.parse(jsonString);
|
||||
*
|
||||
* } catch (JSONRPC2Exception e) {
|
||||
* // handle exception
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* <p>And here is how you can replicate the above example response strings:
|
||||
*
|
||||
* <pre>
|
||||
* // success example
|
||||
* JSONRPC2Response resp = new JSONRPC2Response(true, "req-002");
|
||||
* System.out.println(resp);
|
||||
*
|
||||
* // failure example
|
||||
* JSONRPC2Error err = new JSONRPC2Error(-32601, "Method not found");
|
||||
* resp = new JSONRPC2Response(err, "req-003");
|
||||
* System.out.println(resp);
|
||||
*
|
||||
* </pre>
|
||||
*
|
||||
* <p id="map">The mapping between JSON and Java entities (as defined by the
|
||||
* underlying JSON Smart library):
|
||||
*
|
||||
* <pre>
|
||||
* true|false <---> java.lang.Boolean
|
||||
* number <---> java.lang.Number
|
||||
* string <---> java.lang.String
|
||||
* array <---> java.util.List
|
||||
* object <---> java.util.Map
|
||||
* null <---> null
|
||||
* </pre>
|
||||
*
|
||||
* @author Vladimir Dzhuvinov
|
||||
*/
|
||||
public class JSONRPC2Response extends JSONRPC2Message {
|
||||
|
||||
|
||||
/**
|
||||
* The result.
|
||||
*/
|
||||
private Object result = null;
|
||||
|
||||
|
||||
/**
|
||||
* The error object.
|
||||
*/
|
||||
private JSONRPC2Error error = null;
|
||||
|
||||
|
||||
/**
|
||||
* The echoed request identifier.
|
||||
*/
|
||||
private Object id = null;
|
||||
|
||||
|
||||
/**
|
||||
* Parses a JSON-RPC 2.0 response string. This method is thread-safe.
|
||||
*
|
||||
* @param jsonString The JSON-RPC 2.0 response string, UTF-8 encoded.
|
||||
* Must not be {@code null}.
|
||||
*
|
||||
* @return The corresponding JSON-RPC 2.0 response object.
|
||||
*
|
||||
* @throws JSONRPC2ParseException With detailed message if parsing
|
||||
* failed.
|
||||
*/
|
||||
public static JSONRPC2Response parse(final String jsonString)
|
||||
throws JSONRPC2ParseException {
|
||||
|
||||
return parse(jsonString, false, false, false);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Parses a JSON-RPC 2.0 response string. This method is thread-safe.
|
||||
*
|
||||
* @param jsonString The JSON-RPC 2.0 response string, UTF-8 encoded.
|
||||
* Must not be {@code null}.
|
||||
* @param preserveOrder {@code true} to preserve the order of JSON
|
||||
* object members in results.
|
||||
*
|
||||
* @return The corresponding JSON-RPC 2.0 response object.
|
||||
*
|
||||
* @throws JSONRPC2ParseException With detailed message if parsing
|
||||
* failed.
|
||||
*/
|
||||
public static JSONRPC2Response parse(final String jsonString,
|
||||
final boolean preserveOrder)
|
||||
throws JSONRPC2ParseException {
|
||||
|
||||
return parse(jsonString, preserveOrder, false, false);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Parses a JSON-RPC 2.0 response string. This method is thread-safe.
|
||||
*
|
||||
* @param jsonString The JSON-RPC 2.0 response string, UTF-8 encoded.
|
||||
* Must not be {@code null}.
|
||||
* @param preserveOrder {@code true} to preserve the order of JSON
|
||||
* object members in results.
|
||||
* @param ignoreVersion {@code true} to skip a check of the
|
||||
* {@code "jsonrpc":"2.0"} version attribute in the
|
||||
* JSON-RPC 2.0 message.
|
||||
*
|
||||
* @return The corresponding JSON-RPC 2.0 response object.
|
||||
*
|
||||
* @throws JSONRPC2ParseException With detailed message if the parsing
|
||||
* failed.
|
||||
*/
|
||||
public static JSONRPC2Response parse(final String jsonString,
|
||||
final boolean preserveOrder,
|
||||
final boolean ignoreVersion)
|
||||
throws JSONRPC2ParseException {
|
||||
|
||||
return parse(jsonString, preserveOrder, ignoreVersion, false);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Parses a JSON-RPC 2.0 response string. This method is thread-safe.
|
||||
*
|
||||
* @param jsonString The JSON-RPC 2.0 response string, UTF-8
|
||||
* encoded. Must not be {@code null}.
|
||||
* @param preserveOrder {@code true} to preserve the order of
|
||||
* JSON object members in results.
|
||||
* @param ignoreVersion {@code true} to skip a check of the
|
||||
* {@code "jsonrpc":"2.0"} version
|
||||
* attribute in the JSON-RPC 2.0 message.
|
||||
* @param parseNonStdAttributes {@code true} to parse non-standard
|
||||
* attributes found in the JSON-RPC 2.0
|
||||
* message.
|
||||
*
|
||||
* @return The corresponding JSON-RPC 2.0 response object.
|
||||
*
|
||||
* @throws JSONRPC2ParseException With detailed message if the parsing
|
||||
* failed.
|
||||
*/
|
||||
public static JSONRPC2Response parse(final String jsonString,
|
||||
final boolean preserveOrder,
|
||||
final boolean ignoreVersion,
|
||||
final boolean parseNonStdAttributes)
|
||||
throws JSONRPC2ParseException {
|
||||
|
||||
JSONRPC2Parser parser = new JSONRPC2Parser(preserveOrder, ignoreVersion, parseNonStdAttributes);
|
||||
|
||||
return parser.parseJSONRPC2Response(jsonString);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new JSON-RPC 2.0 response to a successful request.
|
||||
*
|
||||
* @param result The result. The value can <a href="#map">map</a>
|
||||
* to any JSON type. May be {@code null}.
|
||||
* @param id The request identifier echoed back to the caller. May
|
||||
* be {@code null} though not recommended.
|
||||
*/
|
||||
public JSONRPC2Response(final Object result, final Object id) {
|
||||
|
||||
setResult(result);
|
||||
setID(id);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new JSON-RPC 2.0 response to a successful request which
|
||||
* result is {@code null}.
|
||||
*
|
||||
* @param id The request identifier echoed back to the caller. May be
|
||||
* {@code null} though not recommended.
|
||||
*/
|
||||
public JSONRPC2Response(final Object id) {
|
||||
|
||||
setResult(null);
|
||||
setID(id);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new JSON-RPC 2.0 response to a failed request.
|
||||
*
|
||||
* @param error A JSON-RPC 2.0 error instance indicating the
|
||||
* cause of the failure. Must not be {@code null}.
|
||||
* @param id The request identifier echoed back to the caller.
|
||||
* Pass a {@code null} if the request identifier couldn't
|
||||
* be determined (e.g. due to a parse error).
|
||||
*/
|
||||
public JSONRPC2Response(final JSONRPC2Error error, final Object id) {
|
||||
|
||||
setError(error);
|
||||
setID(id);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Indicates a successful JSON-RPC 2.0 request and sets the result.
|
||||
* Note that if the response was previously indicating failure this
|
||||
* will turn it into a response indicating success. Any previously set
|
||||
* error data will be invalidated.
|
||||
*
|
||||
* @param result The result. The value can <a href="#map">map</a> to
|
||||
* any JSON type. May be {@code null}.
|
||||
*/
|
||||
public void setResult(final Object result) {
|
||||
|
||||
// result and error are mutually exclusive
|
||||
this.result = result;
|
||||
this.error = null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets the result of the request. The returned value has meaning
|
||||
* only if the request was successful. Use the
|
||||
* {@link #getError getError} method to check this.
|
||||
*
|
||||
* @return The result.
|
||||
*/
|
||||
public Object getResult() {
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Indicates a failed JSON-RPC 2.0 request and sets the error details.
|
||||
* Note that if the response was previously indicating success this
|
||||
* will turn it into a response indicating failure. Any previously set
|
||||
* result data will be invalidated.
|
||||
*
|
||||
* @param error A JSON-RPC 2.0 error instance indicating the cause of
|
||||
* the failure. Must not be {@code null}.
|
||||
*/
|
||||
public void setError(final JSONRPC2Error error) {
|
||||
|
||||
if (error == null)
|
||||
throw new IllegalArgumentException("The error object cannot be null");
|
||||
|
||||
// result and error are mutually exclusive
|
||||
this.error = error;
|
||||
this.result = null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets the error object indicating the cause of the request failure.
|
||||
* If a {@code null} is returned, the request succeeded and there was
|
||||
* no error.
|
||||
*
|
||||
* @return A JSON-RPC 2.0 error object, {@code null} if the response
|
||||
* indicates success.
|
||||
*/
|
||||
public JSONRPC2Error getError() {
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* A convinience method to check if the response indicates success or
|
||||
* failure of the request. Alternatively, you can use the
|
||||
* {@code #getError} method for this purpose.
|
||||
*
|
||||
* @return {@code true} if the request succeeded, {@code false} if
|
||||
* there was an error.
|
||||
*/
|
||||
public boolean indicatesSuccess() {
|
||||
|
||||
return error == null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets the request identifier echoed back to the caller.
|
||||
*
|
||||
* @param id The value must <a href="#map">map</a> to a JSON scalar.
|
||||
* Pass a {@code null} if the request identifier couldn't
|
||||
* be determined (e.g. due to a parse error).
|
||||
*/
|
||||
public void setID(final Object id) {
|
||||
|
||||
if (id == null ||
|
||||
id instanceof Boolean ||
|
||||
id instanceof Number ||
|
||||
id instanceof String
|
||||
) {
|
||||
this.id = id;
|
||||
} else {
|
||||
this.id = id.toString();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets the request identifier that is echoed back to the caller.
|
||||
*
|
||||
* @return The request identifier. If there was an error during the
|
||||
* the request retrieval (e.g. parse error) and the identifier
|
||||
* couldn't be determined, the value will be {@code null}.
|
||||
*/
|
||||
public Object getID() {
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public JSONObject toJSONObject() {
|
||||
|
||||
JSONObject out = new JSONObject();
|
||||
|
||||
// Result and error are mutually exclusive
|
||||
if (error != null) {
|
||||
out.put("error", error.toJSONObject());
|
||||
}
|
||||
else {
|
||||
out.put("result", result);
|
||||
}
|
||||
|
||||
out.put("id", id);
|
||||
|
||||
out.put("jsonrpc", "2.0");
|
||||
|
||||
|
||||
Map <String,Object> nonStdAttributes = getNonStdAttributes();
|
||||
|
||||
if (nonStdAttributes != null) {
|
||||
|
||||
for (final Map.Entry<String,Object> attr: nonStdAttributes.entrySet())
|
||||
out.put(attr.getKey(), attr.getValue());
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
}
|
@@ -0,0 +1,32 @@
|
||||
/**
|
||||
* Classes to represent, parse and serialise JSON-RPC 2.0 requests,
|
||||
* notifications and responses.
|
||||
*
|
||||
* <p>JSON-RPC is a protocol for
|
||||
* <a href="http://en.wikipedia.org/wiki/Remote_procedure_call">remote
|
||||
* procedure calls</a> (RPC) using <a href="http://www.json.org" >JSON</a>
|
||||
* - encoded requests and responses. It can be easily relayed over HTTP
|
||||
* and is of JavaScript origin, making it ideal for use in dynamic web
|
||||
* applications in the spirit of Ajax and Web 2.0.
|
||||
*
|
||||
* <p>This package implements <b>version 2.0</b> of the protocol, with the
|
||||
* exception of <i>batching / multicall</i>. This feature is deliberately left
|
||||
* out as it tends to confuse users (judging by posts in the JSON-RPC forum).
|
||||
*
|
||||
* <p>See the <a href="http://www.jsonrpc.org/specification"></a>JSON-RPC 2.0
|
||||
* specification</a> for more information or write to the
|
||||
* <a href="https://groups.google.com/forum/#!forum/json-rpc">user group</a> if
|
||||
* you have questions.
|
||||
*
|
||||
* <p><b>Package dependencies:</b> The classes in this package rely on the
|
||||
* {@code org.json.simple} and {@code org.json.simple.parser} packages
|
||||
* (version 1.1.1 and compabile) for JSON encoding and decoding. You can obtain
|
||||
* them from the <a href="http://code.google.com/p/json-smart/">JSON-Smart</a>
|
||||
* website.
|
||||
*
|
||||
* @author Vladimir Dzhuvinov
|
||||
*/
|
||||
package com.thetransactioncompany.jsonrpc2;
|
||||
|
||||
|
||||
|
@@ -0,0 +1,263 @@
|
||||
package com.thetransactioncompany.jsonrpc2.server;
|
||||
|
||||
|
||||
import java.util.Hashtable;
|
||||
|
||||
import com.thetransactioncompany.jsonrpc2.JSONRPC2Error;
|
||||
import com.thetransactioncompany.jsonrpc2.JSONRPC2Notification;
|
||||
import com.thetransactioncompany.jsonrpc2.JSONRPC2Request;
|
||||
import com.thetransactioncompany.jsonrpc2.JSONRPC2Response;
|
||||
|
||||
|
||||
/**
|
||||
* Dispatcher for JSON-RPC 2.0 requests and notifications. This class is
|
||||
* tread-safe.
|
||||
*
|
||||
* <p>Use the {@code register()} methods to add a request or notification
|
||||
* handler for an RPC method.
|
||||
*
|
||||
* <p>Use the {@code process()} methods to have an incoming request or
|
||||
* notification processed by the matching handler.
|
||||
*
|
||||
* <p>The {@code reportProcTime()} method enables reporting of request
|
||||
* processing time (in microseconds) by appending a non-standard "xProcTime"
|
||||
* attribute to the resulting JSON-RPC 2.0 response message.
|
||||
*
|
||||
* <p>Example:
|
||||
*
|
||||
* <pre>
|
||||
* {
|
||||
* "result" : "xyz",
|
||||
* "id" : 1,
|
||||
* "jsonrpc" : "2.0",
|
||||
* "xProcTime" : "189 us"
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* <p>Note: The dispatch(...) methods were deprecated in version 1.7. Use
|
||||
* process(...) instead.
|
||||
*
|
||||
* @author Vladimir Dzhuvinov
|
||||
*/
|
||||
public class Dispatcher implements RequestHandler, NotificationHandler {
|
||||
|
||||
|
||||
/**
|
||||
* Hashtable of request name / handler pairs.
|
||||
*/
|
||||
private final Hashtable<String,RequestHandler> requestHandlers;
|
||||
|
||||
|
||||
/**
|
||||
* Hashtable of notification name / handler pairs.
|
||||
*/
|
||||
private final Hashtable<String,NotificationHandler> notificationHandlers;
|
||||
|
||||
|
||||
/**
|
||||
* Controls reporting of request processing time by appending a
|
||||
* non-standard "xProcTime" attribute to the JSON-RPC 2.0 response.
|
||||
*/
|
||||
private boolean reportProcTime = false;
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new dispatcher with no registered handlers.
|
||||
*/
|
||||
public Dispatcher() {
|
||||
|
||||
requestHandlers = new Hashtable<String,RequestHandler>();
|
||||
notificationHandlers = new Hashtable<String,NotificationHandler>();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Registers a new JSON-RPC 2.0 request handler.
|
||||
*
|
||||
* @param handler The request handler to register. Must not be
|
||||
* {@code null}.
|
||||
*
|
||||
* @throws IllegalArgumentException On attempting to register a handler
|
||||
* that duplicates an existing request
|
||||
* name.
|
||||
*/
|
||||
public void register(final RequestHandler handler) {
|
||||
|
||||
for (String name: handler.handledRequests()) {
|
||||
|
||||
if (requestHandlers.containsKey(name))
|
||||
throw new IllegalArgumentException("Cannot register a duplicate JSON-RPC 2.0 handler for request " + name);
|
||||
|
||||
requestHandlers.put(name, handler);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Registers a new JSON-RPC 2.0 notification handler.
|
||||
*
|
||||
* @param handler The notification handler to register. Must not be
|
||||
* {@code null}.
|
||||
*
|
||||
* @throws IllegalArgumentException On attempting to register a handler
|
||||
* that duplicates an existing
|
||||
* notification name.
|
||||
*/
|
||||
public void register(final NotificationHandler handler) {
|
||||
|
||||
for (String name: handler.handledNotifications()) {
|
||||
|
||||
if (notificationHandlers.containsKey(name))
|
||||
throw new IllegalArgumentException("Cannot register a duplicate JSON-RPC 2.0 handler for notification " + name);
|
||||
|
||||
notificationHandlers.put(name, handler);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String[] handledRequests() {
|
||||
|
||||
java.util.Set<String> var = requestHandlers.keySet();
|
||||
return var.toArray(new String[var.size()]);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String[] handledNotifications() {
|
||||
|
||||
java.util.Set<String> var = notificationHandlers.keySet();
|
||||
return var.toArray(new String[var.size()]);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets the handler for the specified JSON-RPC 2.0 request name.
|
||||
*
|
||||
* @param requestName The request name to lookup.
|
||||
*
|
||||
* @return The corresponding request handler or {@code null} if none
|
||||
* was found.
|
||||
*/
|
||||
public RequestHandler getRequestHandler(final String requestName) {
|
||||
|
||||
return requestHandlers.get(requestName);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets the handler for the specified JSON-RPC 2.0 notification name.
|
||||
*
|
||||
* @param notificationName The notification name to lookup.
|
||||
*
|
||||
* @return The corresponding notification handler or {@code null} if
|
||||
* none was found.
|
||||
*/
|
||||
public NotificationHandler getNotificationHandler(final String notificationName) {
|
||||
|
||||
return notificationHandlers.get(notificationName);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
public JSONRPC2Response dispatch(final JSONRPC2Request request, final MessageContext requestCtx) {
|
||||
|
||||
return process(request, requestCtx);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public JSONRPC2Response process(final JSONRPC2Request request, final MessageContext requestCtx) {
|
||||
|
||||
long startNanosec = 0;
|
||||
|
||||
// Measure request processing time?
|
||||
if (reportProcTime)
|
||||
startNanosec = System.nanoTime();
|
||||
|
||||
|
||||
final String method = request.getMethod();
|
||||
|
||||
RequestHandler handler = getRequestHandler(method);
|
||||
|
||||
if (handler == null) {
|
||||
|
||||
// We didn't find a handler for the requested RPC
|
||||
|
||||
Object id = request.getID();
|
||||
|
||||
return new JSONRPC2Response(JSONRPC2Error.METHOD_NOT_FOUND, id);
|
||||
}
|
||||
|
||||
// Process the request
|
||||
|
||||
JSONRPC2Response response = handler.process(request, requestCtx);
|
||||
|
||||
if (reportProcTime) {
|
||||
|
||||
final long procTimeNanosec = System.nanoTime() - startNanosec;
|
||||
|
||||
response.appendNonStdAttribute("xProcTime", procTimeNanosec / 1000 + " us");
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
public void dispatch(final JSONRPC2Notification notification, final MessageContext notificationCtx) {
|
||||
|
||||
process(notification, notificationCtx);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void process(final JSONRPC2Notification notification, final MessageContext notificationCtx) {
|
||||
|
||||
final String method = notification.getMethod();
|
||||
|
||||
NotificationHandler handler = getNotificationHandler(method);
|
||||
|
||||
if (handler == null) {
|
||||
|
||||
// We didn't find a handler for the requested RPC
|
||||
return;
|
||||
}
|
||||
|
||||
// Process the notification
|
||||
|
||||
handler.process(notification, notificationCtx);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Controls reporting of request processing time by appending a
|
||||
* non-standard "xProcTime" attribute to the JSON-RPC 2.0 response.
|
||||
* Reporting is disabled by default.
|
||||
*
|
||||
* @param enable {@code true} to enable proccessing time reporting,
|
||||
* {@code false} to disable it.
|
||||
*/
|
||||
public void reportProcTime(final boolean enable) {
|
||||
|
||||
reportProcTime = enable;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns {@code true} if reporting of request processing time is
|
||||
* enabled. See the {@link #reportProcTime} description for more
|
||||
* information.
|
||||
*
|
||||
* @return {@code true} if reporting of request processing time is
|
||||
* enabled, else {@code false}.
|
||||
*/
|
||||
public boolean reportsProcTime() {
|
||||
|
||||
return reportProcTime;
|
||||
}
|
||||
}
|
@@ -0,0 +1,428 @@
|
||||
package com.thetransactioncompany.jsonrpc2.server;
|
||||
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.net.URLConnection;
|
||||
import java.security.Principal;
|
||||
import java.security.cert.X509Certificate;
|
||||
|
||||
import javax.net.ssl.HttpsURLConnection;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
|
||||
/**
|
||||
* Context information about JSON-RPC 2.0 request and notification messages.
|
||||
* This class is immutable.
|
||||
*
|
||||
* <ul>
|
||||
* <li>The client's host name.
|
||||
* <li>The client's IP address.
|
||||
* <li>Whether the request / notification was transmitted securely (e.g.
|
||||
* via HTTPS).
|
||||
* <li>The client principal(s) (user), if authenticated.
|
||||
* </ul>
|
||||
*
|
||||
* @author Vladimir Dzhuvinov
|
||||
*/
|
||||
public class MessageContext {
|
||||
|
||||
|
||||
/**
|
||||
* The client hostname, {@code null} if none was specified.
|
||||
*/
|
||||
private String clientHostName = null;
|
||||
|
||||
|
||||
/**
|
||||
* The client IP address, {@code null} if none was specified.
|
||||
*/
|
||||
private String clientInetAddress = null;
|
||||
|
||||
|
||||
/**
|
||||
* Indicates whether the request was received over a secure channel
|
||||
* (typically HTTPS).
|
||||
*/
|
||||
private boolean secure = false;
|
||||
|
||||
|
||||
/**
|
||||
* The authenticated client principals, {@code null} if none were
|
||||
* specified.
|
||||
*/
|
||||
private Principal[] principals = null;
|
||||
|
||||
|
||||
/**
|
||||
* Minimal implementation of the {@link java.security.Principal}
|
||||
* interface.
|
||||
*/
|
||||
public class BasicPrincipal implements Principal {
|
||||
|
||||
/**
|
||||
* The principal name.
|
||||
*/
|
||||
private String name;
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new principal.
|
||||
*
|
||||
* @param name The principal name, must not be {@code null} or
|
||||
* empty string.
|
||||
*
|
||||
* @throws IllegalArgumentException On a {@code null} or empty
|
||||
* principal name.
|
||||
*/
|
||||
public BasicPrincipal(final String name) {
|
||||
|
||||
if (name == null || name.trim().isEmpty())
|
||||
throw new IllegalArgumentException("The principal name must be defined");
|
||||
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Checks for equality.
|
||||
*
|
||||
* @param another The object to compare to.
|
||||
*/
|
||||
public boolean equals(final Object another) {
|
||||
|
||||
return another != null &&
|
||||
another instanceof Principal &&
|
||||
((Principal)another).getName().equals(this.getName());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns a hash code for this principal.
|
||||
*
|
||||
* @return The hash code.
|
||||
*/
|
||||
public int hashCode() {
|
||||
|
||||
return getName().hashCode();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the principal name.
|
||||
*
|
||||
* @return The principal name.
|
||||
*/
|
||||
public String getName() {
|
||||
|
||||
return name;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new JSON-RPC 2.0 request / notification context.
|
||||
*
|
||||
* @param clientHostName The client host name, {@code null} if
|
||||
* unknown.
|
||||
* @param clientInetAddress The client IP address, {@code null} if
|
||||
* unknown.
|
||||
* @param secure Specifies a request received over HTTPS.
|
||||
* @param principalName Specifies the authenticated client principle
|
||||
* name, {@code null} if unknown. The name must
|
||||
* not be an empty or blank string.
|
||||
*/
|
||||
public MessageContext(final String clientHostName,
|
||||
final String clientInetAddress,
|
||||
final boolean secure,
|
||||
final String principalName) {
|
||||
|
||||
this.clientHostName = clientHostName;
|
||||
this.clientInetAddress = clientInetAddress;
|
||||
this.secure = secure;
|
||||
|
||||
if (principalName != null) {
|
||||
principals = new Principal[1];
|
||||
principals[0] = new BasicPrincipal(principalName);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new JSON-RPC 2.0 request / notification context.
|
||||
*
|
||||
* @param clientHostName The client host name, {@code null} if
|
||||
* unknown.
|
||||
* @param clientInetAddress The client IP address, {@code null} if
|
||||
* unknown.
|
||||
* @param secure Specifies a request received over HTTPS.
|
||||
* @param principalNames Specifies the authenticated client principle
|
||||
* names, {@code null} if unknown. The names
|
||||
* must not be an empty or blank string.
|
||||
*/
|
||||
public MessageContext(final String clientHostName,
|
||||
final String clientInetAddress,
|
||||
final boolean secure,
|
||||
final String[] principalNames) {
|
||||
|
||||
this.clientHostName = clientHostName;
|
||||
this.clientInetAddress = clientInetAddress;
|
||||
this.secure = secure;
|
||||
|
||||
if (principalNames != null) {
|
||||
principals = new Principal[principalNames.length];
|
||||
|
||||
for (int i=0; i < principals.length; i++)
|
||||
principals[0] = new BasicPrincipal(principalNames[i]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new JSON-RPC 2.0 request / notification context. No
|
||||
* authenticated client principal is specified.
|
||||
*
|
||||
* @param clientHostName The client host name, {@code null} if
|
||||
* unknown.
|
||||
* @param clientInetAddress The client IP address, {@code null} if
|
||||
* unknown.
|
||||
* @param secure Specifies a request received over HTTPS.
|
||||
*/
|
||||
public MessageContext(final String clientHostName,
|
||||
final String clientInetAddress,
|
||||
final boolean secure) {
|
||||
|
||||
this.clientHostName = clientHostName;
|
||||
this.clientInetAddress = clientInetAddress;
|
||||
this.secure = secure;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new JSON-RPC 2.0 request / notification context. Indicates
|
||||
* an insecure transport (plain HTTP) and no authenticated client
|
||||
* principal.
|
||||
*
|
||||
* @param clientHostName The client host name, {@code null} if
|
||||
* unknown.
|
||||
* @param clientInetAddress The client IP address, {@code null} if
|
||||
* unknown.
|
||||
*/
|
||||
public MessageContext(final String clientHostName,
|
||||
final String clientInetAddress) {
|
||||
|
||||
this.clientHostName = clientHostName;
|
||||
this.clientInetAddress = clientInetAddress;
|
||||
this.secure = false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new JSON-RPC 2.0 request / notification context. Indicates
|
||||
* an insecure transport (plain HTTP) and no authenticated client
|
||||
* principal. Not client host name / IP is specified.
|
||||
*/
|
||||
public MessageContext() {
|
||||
|
||||
this.secure = false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new JSON-RPC 2.0 request / notification context from the
|
||||
* specified HTTP request.
|
||||
*
|
||||
* @param httpRequest The HTTP request.
|
||||
*/
|
||||
public MessageContext(final HttpServletRequest httpRequest) {
|
||||
|
||||
clientInetAddress = httpRequest.getRemoteAddr();
|
||||
|
||||
clientHostName = httpRequest.getRemoteHost();
|
||||
|
||||
if (clientHostName != null && clientHostName.equals(clientInetAddress))
|
||||
clientHostName = null; // not resolved actually
|
||||
|
||||
secure = httpRequest.isSecure();
|
||||
|
||||
X509Certificate[] certs = (X509Certificate[])httpRequest.getAttribute("javax.servlet.request.X509Certificate");
|
||||
|
||||
if (certs != null && certs.length > 0) {
|
||||
|
||||
principals = new Principal[certs.length];
|
||||
|
||||
for (int i=0; i < principals.length; i++)
|
||||
principals[i] = certs[i].getSubjectX500Principal();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new JSON-RPC 2.0 request / notification context from the
|
||||
* specified URL connection. Use this constructor in cases when the
|
||||
* HTTP server is the origin of the JSON-RPC 2.0 requests /
|
||||
* notifications. If the IP address of the HTTP server cannot be
|
||||
* resolved {@link #getClientInetAddress} will return {@code null}.
|
||||
*
|
||||
* @param connection The URL connection, must be established and not
|
||||
* {@code null}.
|
||||
*/
|
||||
public MessageContext(final URLConnection connection) {
|
||||
|
||||
clientHostName = connection.getURL().getHost();
|
||||
|
||||
InetAddress ip = null;
|
||||
|
||||
if (clientHostName != null) {
|
||||
|
||||
try {
|
||||
ip = InetAddress.getByName(clientHostName);
|
||||
|
||||
} catch (Exception e) {
|
||||
|
||||
// UnknownHostException, SecurityException
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
if (ip != null)
|
||||
clientInetAddress = ip.getHostAddress();
|
||||
|
||||
|
||||
if (connection instanceof HttpsURLConnection) {
|
||||
|
||||
secure = true;
|
||||
|
||||
HttpsURLConnection httpsConnection = (HttpsURLConnection)connection;
|
||||
|
||||
Principal prn = null;
|
||||
|
||||
try {
|
||||
prn = httpsConnection.getPeerPrincipal();
|
||||
|
||||
} catch (Exception e) {
|
||||
|
||||
// SSLPeerUnverifiedException, IllegalStateException
|
||||
// ignore
|
||||
}
|
||||
|
||||
if (prn != null) {
|
||||
|
||||
principals = new Principal[1];
|
||||
principals[0] = prn;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets the host name of the client that sent the request /
|
||||
* notification.
|
||||
*
|
||||
* @return The client host name, {@code null} if unknown.
|
||||
*/
|
||||
public String getClientHostName() {
|
||||
|
||||
return clientHostName;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets the IP address of the client that sent the request /
|
||||
* notification.
|
||||
*
|
||||
* @return The client IP address, {@code null} if unknown.
|
||||
*/
|
||||
public String getClientInetAddress() {
|
||||
|
||||
return clientInetAddress;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Indicates whether the request / notification was received over a
|
||||
* secure HTTPS connection.
|
||||
*
|
||||
* @return {@code true} If the request was received over HTTPS,
|
||||
* {@code false} if it was received over plain HTTP.
|
||||
*/
|
||||
public boolean isSecure() {
|
||||
|
||||
return secure;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the first authenticated client principal, {@code null} if
|
||||
* none.
|
||||
*
|
||||
* @return The first client principal, {@code null} if none.
|
||||
*/
|
||||
public Principal getPrincipal() {
|
||||
|
||||
if (principals != null)
|
||||
return principals[0];
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the authenticated client principals, {@code null} if
|
||||
* none.
|
||||
*
|
||||
* @return The client principals, {@code null} if none.
|
||||
*/
|
||||
public Principal[] getPrincipals() {
|
||||
|
||||
return principals;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the first authenticated client principal name, {@code null}
|
||||
* if none.
|
||||
*
|
||||
* @return The first client principal name, {@code null} if none.
|
||||
*/
|
||||
public String getPrincipalName() {
|
||||
|
||||
if (principals != null)
|
||||
return principals[0].getName();
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the authenticated client principal names, {@code null}
|
||||
* if none.
|
||||
*
|
||||
* @return The client principal names, {@code null} if none.
|
||||
*/
|
||||
public String[] getPrincipalNames() {
|
||||
|
||||
String[] names = new String[principals.length];
|
||||
|
||||
for (int i=0; i < names.length; i++)
|
||||
names[i] = principals[i].getName();
|
||||
|
||||
return names;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
|
||||
String s = "[host=" + clientHostName + " hostIP=" + clientInetAddress + " secure=" + secure;
|
||||
|
||||
if (principals != null) {
|
||||
|
||||
int i = 0;
|
||||
|
||||
for (Principal p: principals)
|
||||
s += " principal[" + (i++) + "]=" + p;
|
||||
}
|
||||
|
||||
return s + "]";
|
||||
}
|
||||
}
|
@@ -0,0 +1,35 @@
|
||||
package com.thetransactioncompany.jsonrpc2.server;
|
||||
|
||||
|
||||
import com.thetransactioncompany.jsonrpc2.JSONRPC2Notification;
|
||||
|
||||
|
||||
/**
|
||||
* Interface for handling JSON-RPC 2.0 notifications.
|
||||
*
|
||||
* @author Vladimir Dzhuvinov
|
||||
*/
|
||||
public interface NotificationHandler {
|
||||
|
||||
|
||||
/**
|
||||
* Gets the names of the handled JSON-RPC 2.0 notification methods.
|
||||
*
|
||||
* @return The names of the handled JSON-RPC 2.0 notification methods.
|
||||
*/
|
||||
public String[] handledNotifications();
|
||||
|
||||
|
||||
/**
|
||||
* Processes a JSON-RPC 2.0 notification.
|
||||
*
|
||||
* <p>Note that JSON-RPC 2.0 notifications don't produce a response!
|
||||
*
|
||||
* @param notification A valid JSON-RPC 2.0 notification instance.
|
||||
* Must not be {@code null}.
|
||||
* @param notificationCtx Context information about the notification
|
||||
* message, may be {@code null} if undefined.
|
||||
*/
|
||||
public void process(final JSONRPC2Notification notification, final MessageContext notificationCtx);
|
||||
|
||||
}
|
@@ -0,0 +1,36 @@
|
||||
package com.thetransactioncompany.jsonrpc2.server;
|
||||
|
||||
|
||||
import com.thetransactioncompany.jsonrpc2.JSONRPC2Request;
|
||||
import com.thetransactioncompany.jsonrpc2.JSONRPC2Response;
|
||||
|
||||
|
||||
/**
|
||||
* Interface for handling JSON-RPC 2.0 requests.
|
||||
*
|
||||
* @author Vladimir Dzhuvinov
|
||||
*/
|
||||
public interface RequestHandler {
|
||||
|
||||
|
||||
/**
|
||||
* Gets the names of the handled JSON-RPC 2.0 request methods.
|
||||
*
|
||||
* @return The names of the handled JSON-RPC 2.0 request methods.
|
||||
*/
|
||||
public String[] handledRequests();
|
||||
|
||||
|
||||
/**
|
||||
* Processes a JSON-RPC 2.0 request.
|
||||
*
|
||||
* @param request A valid JSON-RPC 2.0 request instance. Must not be
|
||||
* {@code null}.
|
||||
* @param requestCtx Context information about the request message, may
|
||||
* be {@code null} if undefined.
|
||||
*
|
||||
* @return The resulting JSON-RPC 2.0 response. It indicates success
|
||||
* or an error, such as METHOD_NOT_FOUND.
|
||||
*/
|
||||
public JSONRPC2Response process(final JSONRPC2Request request, final MessageContext requestCtx);
|
||||
}
|
@@ -0,0 +1,34 @@
|
||||
/**
|
||||
* Simple server framework for processing JSON-RPC 2.0 requests and
|
||||
* notifications.
|
||||
*
|
||||
* <p>Usage:
|
||||
*
|
||||
* <ol>
|
||||
* <li>Implement {@link com.thetransactioncompany.jsonrpc2.server.RequestHandler request}
|
||||
* and / or {@link com.thetransactioncompany.jsonrpc2.server.NotificationHandler notification}
|
||||
* handlers for the various expected JSON-RPC 2.0 messages. A handler
|
||||
* may process one or more request/notification methods (identified by
|
||||
* method name).
|
||||
* <li>Create a new {@link com.thetransactioncompany.jsonrpc2.server.Dispatcher}
|
||||
* and register the handlers with it.
|
||||
* <li>Pass the received JSON-RPC 2.0 requests and notifications to the
|
||||
* appropriate {@code Dispatcher.dispatch(...)} method, then, if the
|
||||
* message is a request, pass the resulting JSON-RPC 2.0 response back
|
||||
* to the client.
|
||||
* </ol>
|
||||
*
|
||||
* <p>Direct package dependencies:
|
||||
*
|
||||
* <ul>
|
||||
* <li><b><a href="http://software.dzhuvinov.com/json-rpc-2.0-base.html">JSON-RPC 2.0 Base</a></b>
|
||||
* [<i>com.thetransactioncompany.jsonrpc2</i>] to construct and represent
|
||||
* JSON-RPC 2.0 messages.
|
||||
* <li><b>Java Servlet API</b> [<i>javax.servlet.http</i>] for constructing
|
||||
* {@link com.thetransactioncompany.jsonrpc2.server.MessageContext}
|
||||
* objects from HTTP servlet requests.
|
||||
* </ul>
|
||||
*
|
||||
* @author Vladimir Dzhuvinov
|
||||
*/
|
||||
package com.thetransactioncompany.jsonrpc2.server;
|
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,80 @@
|
||||
package com.thetransactioncompany.jsonrpc2.util;
|
||||
|
||||
|
||||
/**
|
||||
* The base abstract class for the JSON-RPC 2.0 parameter retrievers.
|
||||
*
|
||||
* @author Vladimir Dzhuvinov
|
||||
*/
|
||||
public abstract class ParamsRetriever {
|
||||
|
||||
|
||||
/**
|
||||
* Returns the parameter count.
|
||||
*
|
||||
* @return The parameters count.
|
||||
*/
|
||||
public abstract int size();
|
||||
|
||||
|
||||
/**
|
||||
* Matches a string against an array of acceptable values.
|
||||
*
|
||||
* @param input The string to match.
|
||||
* @param enumStrings The acceptable string values. Must not be
|
||||
* {@code null}.
|
||||
* @param ignoreCase {@code true} for a case insensitive match.
|
||||
*
|
||||
* @return The matching string value, {@code null} if no match was
|
||||
* found.
|
||||
*/
|
||||
protected static String getEnumStringMatch(final String input,
|
||||
final String[] enumStrings,
|
||||
final boolean ignoreCase) {
|
||||
|
||||
for (final String en: enumStrings) {
|
||||
|
||||
if (ignoreCase) {
|
||||
if (en.equalsIgnoreCase(input))
|
||||
return en;
|
||||
}
|
||||
else {
|
||||
if (en.equals(input))
|
||||
return en;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Matches a string against an enumeration of acceptable values.
|
||||
*
|
||||
* @param input The string to match.
|
||||
* @param enumClass The enumeration class specifying the acceptable
|
||||
* string values. Must not be {@code null}.
|
||||
* @param ignoreCase {@code true} for a case insensitive match.
|
||||
*
|
||||
* @return The matching enumeration constant, {@code null} if no match
|
||||
* was found.
|
||||
*/
|
||||
protected static <T extends Enum<T>> T getEnumStringMatch(final String input,
|
||||
final Class<T> enumClass,
|
||||
final boolean ignoreCase) {
|
||||
|
||||
for (T en: enumClass.getEnumConstants()) {
|
||||
|
||||
if (ignoreCase) {
|
||||
if (en.toString().equalsIgnoreCase(input))
|
||||
return en;
|
||||
}
|
||||
else {
|
||||
if (en.toString().equals(input))
|
||||
return en;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,44 @@
|
||||
/**
|
||||
* Utility classes for typed retrieval of JSON-RPC 2.0 request parameters on the
|
||||
* server side.
|
||||
*
|
||||
* <p>The following parameter type conversion choices are available:
|
||||
*
|
||||
* <ul>
|
||||
* <li>JSON true/false to Java {@code boolean}
|
||||
* <li>JSON number to Java {@code int}, {@code long}, {@code float} or
|
||||
* {@code double}
|
||||
* <li>JSON string to {@code java.lang.String}
|
||||
* <li>Predefined (enumerated) JSON string to a Java {@code enum} constant
|
||||
* or {@code java.lang.String}
|
||||
* <li>JSON array to Java {@code boolean[]}, {@code int[]}, {@code long[]},
|
||||
* {@code float[]}, {@code double[]} or {@code string[]} array, or
|
||||
* to mixed type {@code java.util.List}
|
||||
* <li>JSON object to {@code java.util.Map}
|
||||
* </ul>
|
||||
*
|
||||
* <p>If a parameter cannot be retrieved, either because it's missing or
|
||||
* is of the wrong type, a standard
|
||||
* {@link com.thetransactioncompany.jsonrpc2.JSONRPC2Error#INVALID_PARAMS}
|
||||
* exception is thrown.
|
||||
*
|
||||
* <p>There are two concrete classes:
|
||||
*
|
||||
* <ul>
|
||||
* <li>The {@link com.thetransactioncompany.jsonrpc2.util.PositionalParamsRetriever}
|
||||
* class is for extracting <em>positional parameters</em> (packed in a
|
||||
* JSON array).
|
||||
* <li>The {@link com.thetransactioncompany.jsonrpc2.util.NamedParamsRetriever}
|
||||
* class is for extracting <em>named parameters</em> (packed in a JSON
|
||||
* object).
|
||||
* </ul>
|
||||
*
|
||||
*
|
||||
* <p><b>Package dependencies:</b> The classes in this package depend on the
|
||||
* sister {@link com.thetransactioncompany.jsonrpc2} package.
|
||||
*
|
||||
* @author Vladimir Dzhuvinov
|
||||
*/
|
||||
package com.thetransactioncompany.jsonrpc2.util;
|
||||
|
||||
|
Reference in New Issue
Block a user