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 * @author Vladimir Dzhuvinov 056 */ 057public abstract class JSONRPC2Message implements JSONAware { 058 059 060 /** 061 * Map of non-standard JSON-RPC 2.0 message attributes, {@code null} if 062 * none. 063 */ 064 private Map <String,Object> nonStdAttributes = null; 065 066 067 /** 068 * Provides common parsing of JSON-RPC 2.0 requests, notifications 069 * and responses. Use this method if you don't know which type of 070 * JSON-RPC message the input JSON string represents. 071 * 072 * <p>Batched requests / notifications are not supported. 073 * 074 * <p>This method is thread-safe. 075 * 076 * <p>If you are certain about the message type use the dedicated 077 * {@link JSONRPC2Request#parse}, {@link JSONRPC2Notification#parse} 078 * or {@link JSONRPC2Response#parse} methods. They are more efficient 079 * and provide a more detailed parse error reporting. 080 * 081 * <p>The member order of parsed JSON objects will not be preserved 082 * (for efficiency reasons) and the JSON-RPC 2.0 version field must be 083 * set to "2.0". To change this behaviour check the optional {@link 084 * #parse(String,boolean,boolean)} method. 085 * 086 * @param jsonString A JSON string representing a JSON-RPC 2.0 request, 087 * notification or response, UTF-8 encoded. Must not 088 * be {@code null}. 089 * 090 * @return An instance of {@link JSONRPC2Request}, 091 * {@link JSONRPC2Notification} or {@link JSONRPC2Response}. 092 * 093 * @throws JSONRPC2ParseException With detailed message if parsing 094 * failed. 095 */ 096 public static JSONRPC2Message parse(final String jsonString) 097 throws JSONRPC2ParseException { 098 099 return parse(jsonString, false, false); 100 } 101 102 103 /** 104 * Provides common parsing of JSON-RPC 2.0 requests, notifications 105 * and responses. Use this method if you don't know which type of 106 * JSON-RPC message the input string represents. 107 * 108 * <p>Batched requests / notifications are not supported. 109 * 110 * <p>This method is thread-safe. 111 * 112 * <p>If you are certain about the message type use the dedicated 113 * {@link JSONRPC2Request#parse}, {@link JSONRPC2Notification#parse} 114 * or {@link JSONRPC2Response#parse} methods. They are more efficient 115 * and provide a more detailed parse error reporting. 116 * 117 * @param jsonString A JSON string representing a JSON-RPC 2.0 118 * request, notification or response, UTF-8 119 * encoded. Must not be {@code null}. 120 * @param preserveOrder If {@code true} the member order of JSON objects 121 * in parameters and results must be preserved. 122 * @param ignoreVersion If {@code true} the {@code "jsonrpc":"2.0"} 123 * version field in the JSON-RPC 2.0 message will 124 * not be checked. 125 * 126 * @return An instance of {@link JSONRPC2Request}, 127 * {@link JSONRPC2Notification} or {@link JSONRPC2Response}. 128 * 129 * @throws JSONRPC2ParseException With detailed message if parsing 130 * failed. 131 */ 132 public static JSONRPC2Message parse(final String jsonString, final boolean preserveOrder, final boolean ignoreVersion) 133 throws JSONRPC2ParseException { 134 135 JSONRPC2Parser parser = new JSONRPC2Parser(preserveOrder, ignoreVersion); 136 137 return parser.parseJSONRPC2Message(jsonString); 138 } 139 140 141 /** 142 * Appends a non-standard attribute to this JSON-RPC 2.0 message. This is 143 * done by adding a new member (key / value pair) to the top level JSON 144 * object representing the message. 145 * 146 * <p>You may use this method to add meta and debugging attributes, 147 * such as the request processing time, to a JSON-RPC 2.0 message. 148 * 149 * @param name The attribute name. Must not conflict with the existing 150 * "method", "id", "params", "result", "error" and "jsonrpc" 151 * attributes reserved by the JSON-RPC 2.0 protocol, else 152 * an {@code IllegalArgumentException} will be thrown. Must 153 * not be {@code null} either. 154 * @param value The attribute value. Must be of type String, boolean, 155 * number, List, Map or null, else an 156 * {@code IllegalArgumentException} will be thrown. 157 */ 158 public void appendNonStdAttribute(final String name, final Object value) { 159 160 // Name check 161 if (name == null || 162 name.equals("method") || 163 name.equals("id") || 164 name.equals("params") || 165 name.equals("result") || 166 name.equals("error") || 167 name.equals("jsonrpc") ) 168 169 throw new IllegalArgumentException("Non-standard attribute name violation"); 170 171 // Value check 172 if ( value != null && 173 ! (value instanceof Boolean) && 174 ! (value instanceof Number) && 175 ! (value instanceof String) && 176 ! (value instanceof List) && 177 ! (value instanceof Map) ) 178 179 throw new IllegalArgumentException("Illegal non-standard attribute value, must map to a valid JSON type"); 180 181 182 if (nonStdAttributes == null) 183 nonStdAttributes = new HashMap<String,Object>(); 184 185 nonStdAttributes.put(name, value); 186 } 187 188 189 /** 190 * Retrieves a non-standard JSON-RPC 2.0 message attribute. 191 * 192 * @param name The name of the non-standard attribute to retrieve. Must 193 * not be {@code null}. 194 * 195 * @return The value of the non-standard attribute (may also be 196 * {@code null}, {@code null} if not found. 197 */ 198 public Object getNonStdAttribute(final String name) { 199 200 if (nonStdAttributes == null) 201 return null; 202 203 return nonStdAttributes.get(name); 204 } 205 206 207 /** 208 * Retrieves the non-standard JSON-RPC 2.0 message attributes. 209 * 210 * @return The non-standard attributes as a map, {@code null} if none. 211 */ 212 public Map<String,Object> getNonStdAttributes() { 213 214 return nonStdAttributes; 215 } 216 217 218 /** 219 * Returns a JSON object representing this JSON-RPC 2.0 message. 220 * 221 * @return The JSON object. 222 */ 223 public abstract JSONObject toJSONObject(); 224 225 226 /** 227 * Returns a JSON string representation of this JSON-RPC 2.0 message. 228 * 229 * @see #toString 230 * 231 * @return The JSON object string representing this JSON-RPC 2.0 232 * message. 233 */ 234 @Override 235 public String toJSONString() { 236 237 return toString(); 238 } 239 240 241 /** 242 * Serialises this JSON-RPC 2.0 message to a JSON object string. 243 * 244 * @return The JSON object string representing this JSON-RPC 2.0 245 * message. 246 */ 247 @Override 248 public String toString() { 249 250 return toJSONObject().toString(); 251 } 252}