001package com.thetransactioncompany.jsonrpc2; 002 003 004import java.util.HashMap; 005import java.util.List; 006import java.util.Map; 007 008import net.minidev.json.JSONAware; 009import net.minidev.json.JSONObject; 010 011 012/** 013 * The base abstract class for JSON-RPC 2.0 requests, notifications and 014 * responses. Provides common methods for parsing (from JSON string) and 015 * serialisation (to JSON string) of these three message types. 016 * 017 * <p>Example parsing and serialisation back to JSON: 018 * 019 * <pre> 020 * String jsonString = "{\"method\":\"progressNotify\",\"params\":[\"75%\"],\"jsonrpc\":\"2.0\"}"; 021 * 022 * JSONRPC2Message message = null; 023 * 024 * // parse 025 * try { 026 * message = JSONRPC2Message.parse(jsonString); 027 * } catch (JSONRPC2ParseException e) { 028 * // handle parse exception 029 * } 030 * 031 * if (message instanceof JSONRPC2Request) 032 * System.out.println("The message is a request"); 033 * else if (message instanceof JSONRPC2Notification) 034 * System.out.println("The message is a notification"); 035 * else if (message instanceof JSONRPC2Response) 036 * System.out.println("The message is a response"); 037 * 038 * // serialise back to JSON string 039 * System.out.println(message); 040 * 041 * </pre> 042 * 043 * <p id="map">The mapping between JSON and Java entities (as defined by the 044 * underlying JSON Smart library): 045 * 046 * <pre> 047 * true|false <---> java.lang.Boolean 048 * number <---> java.lang.Number 049 * string <---> java.lang.String 050 * array <---> java.util.List 051 * object <---> java.util.Map 052 * null <---> null 053 * </pre> 054 * 055 * <p>The JSON-RPC 2.0 specification and user group forum can be found 056 * <a href="http://groups.google.com/group/json-rpc">here</a>. 057 * 058 * @author Vladimir Dzhuvinov 059 */ 060public abstract class JSONRPC2Message implements JSONAware { 061 062 063 /** 064 * Map of non-standard JSON-RPC 2.0 message attributes, {@code null} if 065 * none. 066 */ 067 private Map <String,Object> nonStdAttributes = null; 068 069 070 /** 071 * Provides common parsing of JSON-RPC 2.0 requests, notifications 072 * and responses. Use this method if you don't know which type of 073 * JSON-RPC message the input JSON string represents. 074 * 075 * <p>Batched requests / notifications are not supported. 076 * 077 * <p>This method is thread-safe. 078 * 079 * <p>If you are certain about the message type use the dedicated 080 * {@link JSONRPC2Request#parse}, {@link JSONRPC2Notification#parse} 081 * or {@link JSONRPC2Response#parse} methods. They are more efficient 082 * and provide a more detailed parse error reporting. 083 * 084 * <p>The member order of parsed JSON objects will not be preserved 085 * (for efficiency reasons) and the JSON-RPC 2.0 version field must be 086 * set to "2.0". To change this behaviour check the optional {@link 087 * #parse(String,boolean,boolean)} method. 088 * 089 * @param jsonString A JSON string representing a JSON-RPC 2.0 request, 090 * notification or response, UTF-8 encoded. Must not 091 * be {@code null}. 092 * 093 * @return An instance of {@link JSONRPC2Request}, 094 * {@link JSONRPC2Notification} or {@link JSONRPC2Response}. 095 * 096 * @throws JSONRPC2ParseException With detailed message if parsing 097 * failed. 098 */ 099 public static JSONRPC2Message parse(final String jsonString) 100 throws JSONRPC2ParseException { 101 102 return parse(jsonString, false, false); 103 } 104 105 106 /** 107 * Provides common parsing of JSON-RPC 2.0 requests, notifications 108 * and responses. Use this method if you don't know which type of 109 * JSON-RPC message the input string represents. 110 * 111 * <p>Batched requests / notifications are not supported. 112 * 113 * <p>This method is thread-safe. 114 * 115 * <p>If you are certain about the message type use the dedicated 116 * {@link JSONRPC2Request#parse}, {@link JSONRPC2Notification#parse} 117 * or {@link JSONRPC2Response#parse} methods. They are more efficient 118 * and provide a more detailed parse error reporting. 119 * 120 * @param jsonString A JSON string representing a JSON-RPC 2.0 121 * request, notification or response, UTF-8 122 * encoded. Must not be {@code null}. 123 * @param preserveOrder If {@code true} the member order of JSON objects 124 * in parameters and results must be preserved. 125 * @param ignoreVersion If {@code true} the {@code "jsonrpc":"2.0"} 126 * version field in the JSON-RPC 2.0 message will 127 * not be checked. 128 * 129 * @return An instance of {@link JSONRPC2Request}, 130 * {@link JSONRPC2Notification} or {@link JSONRPC2Response}. 131 * 132 * @throws JSONRPC2ParseException With detailed message if parsing 133 * failed. 134 */ 135 public static JSONRPC2Message parse(final String jsonString, final boolean preserveOrder, final boolean ignoreVersion) 136 throws JSONRPC2ParseException { 137 138 JSONRPC2Parser parser = new JSONRPC2Parser(preserveOrder, ignoreVersion); 139 140 return parser.parseJSONRPC2Message(jsonString); 141 } 142 143 144 /** 145 * Appends a non-standard attribute to this JSON-RPC 2.0 message. This is 146 * done by adding a new member (key / value pair) to the top level JSON 147 * object representing the message. 148 * 149 * <p>You may use this method to add meta and debugging attributes, 150 * such as the request processing time, to a JSON-RPC 2.0 message. 151 * 152 * @param name The attribute name. Must not conflict with the existing 153 * "method", "id", "params", "result", "error" and "jsonrpc" 154 * attributes reserved by the JSON-RPC 2.0 protocol, else 155 * an {@code IllegalArgumentException} will be thrown. Must 156 * not be {@code null} either. 157 * @param value The attribute value. Must be of type String, boolean, 158 * number, List, Map or null, else an 159 * {@code IllegalArgumentException} will be thrown. 160 */ 161 public void appendNonStdAttribute(final String name, final Object value) { 162 163 // Name check 164 if (name == null || 165 name.equals("method") || 166 name.equals("id") || 167 name.equals("params") || 168 name.equals("result") || 169 name.equals("error") || 170 name.equals("jsonrpc") ) 171 172 throw new IllegalArgumentException("Non-standard attribute name violation"); 173 174 // Value check 175 if ( value != null && 176 ! (value instanceof Boolean) && 177 ! (value instanceof Number) && 178 ! (value instanceof String) && 179 ! (value instanceof List) && 180 ! (value instanceof Map) ) 181 182 throw new IllegalArgumentException("Illegal non-standard attribute value, must map to a valid JSON type"); 183 184 185 if (nonStdAttributes == null) 186 nonStdAttributes = new HashMap<String,Object>(); 187 188 nonStdAttributes.put(name, value); 189 } 190 191 192 /** 193 * Retrieves a non-standard JSON-RPC 2.0 message attribute. 194 * 195 * @param name The name of the non-standard attribute to retrieve. Must 196 * not be {@code null}. 197 * 198 * @return The value of the non-standard attribute (may also be 199 * {@code null}, {@code null} if not found. 200 */ 201 public Object getNonStdAttribute(final String name) { 202 203 if (nonStdAttributes == null) 204 return null; 205 206 return nonStdAttributes.get(name); 207 } 208 209 210 /** 211 * Retrieves the non-standard JSON-RPC 2.0 message attributes. 212 * 213 * @return The non-standard attributes as a map, {@code null} if none. 214 */ 215 public Map<String,Object> getNonStdAttributes() { 216 217 return nonStdAttributes; 218 } 219 220 221 /** 222 * Returns a JSON object representing this JSON-RPC 2.0 message. 223 * 224 * @return The JSON object. 225 */ 226 public abstract JSONObject toJSONObject(); 227 228 229 /** 230 * Returns a JSON string representation of this JSON-RPC 2.0 message. 231 * 232 * @see #toString 233 * 234 * @return The JSON object string representing this JSON-RPC 2.0 235 * message. 236 */ 237 @Override 238 public String toJSONString() { 239 240 return toString(); 241 } 242 243 244 /** 245 * Serialises this JSON-RPC 2.0 message to a JSON object string. 246 * 247 * @return The JSON object string representing this JSON-RPC 2.0 248 * message. 249 */ 250 @Override 251 public String toString() { 252 253 return toJSONObject().toString(); 254 } 255}