001 package com.thetransactioncompany.jsonrpc2;
002
003
004 import java.util.HashMap;
005 import java.util.List;
006 import java.util.Map;
007
008 import net.minidev.json.JSONAware;
009 import 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 */
060 public 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 }