001    package com.thetransactioncompany.jsonrpc2;
002    
003    
004    import java.util.List;
005    import java.util.Map;
006    
007    import net.minidev.json.JSONObject;
008    
009    
010    /** 
011     * Represents a JSON-RPC 2.0 notification. 
012     *
013     * <p>Notifications provide a mean for calling a remote procedure without 
014     * generating a response. Note that notifications are inherently unreliable 
015     * as no confirmation is sent back to the caller.
016     *
017     * <p>Notifications have the same JSON structure as requests, except that they
018     * lack an identifier:
019     * <ul>
020     *     <li>{@code method} The name of the remote method to call.
021     *     <li>{@code params} The required method parameters (if any), which can 
022     *         be packed into a JSON array or object.
023     *     <li>{@code jsonrpc} A string indicating the JSON-RPC protocol version 
024     *         set to "2.0".
025     * </ul>
026     *
027     * <p>Here is a sample JSON-RPC 2.0 notification string:
028     *
029     * <pre>
030     * {  
031     *    "method"  : "progressNotify",
032     *    "params"  : ["75%"],
033     *    "jsonrpc" : "2.0"
034     * }
035     * </pre>
036     *
037     * <p>This class provides two methods to obtain a request object:
038     * <ul>
039     *     <li>Pass a JSON-RPC 2.0 notification string to the static 
040     *         {@link #parse} method, or 
041     *     <li>Invoke one of the constructors with the appropriate arguments.
042     * </ul>
043     *
044     * <p>Example 1: Parsing a notification string:
045     *
046     * <pre>
047     * String jsonString = "{\"method\":\"progressNotify\",\"params\":[\"75%\"],\"jsonrpc\":\"2.0\"}";
048     * 
049     * JSONRPC2Notification notification = null;
050     * 
051     * try {
052     *         notification = JSONRPC2Notification.parse(jsonString);
053     *
054     * } catch (JSONRPC2ParseException e) {
055     *         // handle exception
056     * }
057     * </pre>
058     *
059     * <p>Example 2: Recreating the above request:
060     * 
061     * <pre>
062     * String method = "progressNotify";
063     * List&lt;Object&gt; params = new Vector&lt;Object&gt;();
064     * params.add("75%");
065     *
066     * JSONRPC2Notification notification = new JSONRPC2Notification(method, params);
067     *
068     * System.out.println(notification);
069     * </pre>
070     *
071     * <p id="map">The mapping between JSON and Java entities (as defined by the 
072     * underlying JSON Smart library): 
073     *
074     * <pre>
075     *     true|false  <--->  java.lang.Boolean
076     *     number      <--->  java.lang.Number
077     *     string      <--->  java.lang.String
078     *     array       <--->  java.util.List
079     *     object      <--->  java.util.Map
080     *     null        <--->  null
081     * </pre>
082     *
083     * <p>The JSON-RPC 2.0 specification and user group forum can be found 
084     * <a href="http://groups.google.com/group/json-rpc">here</a>.
085     * 
086     * @author Vladimir Dzhuvinov
087     */
088    public class JSONRPC2Notification extends JSONRPC2Message {
089    
090    
091            /** 
092             * The requested method name. 
093             */
094            private String method;
095    
096            
097            /**
098             * The positional parameters, {@code null} if none.
099             */
100            private List<Object> positionalParams;
101    
102    
103            /**
104             * The named parameters, {@code null} if none.
105             */
106            private Map<String,Object> namedParams;
107            
108            
109            /** 
110             * Parses a JSON-RPC 2.0 notification string. This method is 
111             * thread-safe.
112             *
113             * @param jsonString The JSON-RPC 2.0 notification string, UTF-8 
114             *                   encoded. Must not be {@code null}.
115             *
116             * @return The corresponding JSON-RPC 2.0 notification object.
117             *
118             * @throws JSONRPC2ParseException With detailed message if parsing 
119             *                                failed.
120             */
121            public static JSONRPC2Notification parse(final String jsonString)
122                    throws JSONRPC2ParseException {
123            
124                    return parse(jsonString, false, false, false);
125            }
126            
127            
128            /** 
129             * Parses a JSON-RPC 2.0 notification string. This method is 
130             * thread-safe.
131             *
132             * @param jsonString    The JSON-RPC 2.0 notification string, UTF-8 
133             *                      encoded. Must not be {@code null}.
134             * @param preserveOrder {@code true} to preserve the order of JSON 
135             *                      object members in parameters.
136             *
137             * @return The corresponding JSON-RPC 2.0 notification object.
138             *
139             * @throws JSONRPC2ParseException With detailed message if parsing 
140             *                                failed.
141             */
142            public static JSONRPC2Notification parse(final String jsonString, 
143                                                     final boolean preserveOrder)
144                    throws JSONRPC2ParseException {
145                    
146                    return parse(jsonString, preserveOrder, false, false);
147            }
148            
149            
150            /** 
151             * Parses a JSON-RPC 2.0 notification string. This method is 
152             * thread-safe.
153             *
154             * @param jsonString    The JSON-RPC 2.0 notification string, UTF-8 
155             *                      encoded. Must not be {@code null}.
156             * @param preserveOrder {@code true} to preserve the order of JSON 
157             *                      object members in parameters.
158             * @param ignoreVersion {@code true} to skip a check of the 
159             *                      {@code "jsonrpc":"2.0"} version attribute in the 
160             *                      JSON-RPC 2.0 message.
161             *
162             * @return The corresponding JSON-RPC 2.0 notification object.
163             *
164             * @throws JSONRPC2ParseException With detailed message if parsing 
165             *                                failed.
166             */
167            public static JSONRPC2Notification parse(final String jsonString, 
168                                                     final boolean preserveOrder, 
169                                                     final boolean ignoreVersion)
170                    throws JSONRPC2ParseException {
171                    
172                    return parse(jsonString, preserveOrder, ignoreVersion, false);
173            }
174            
175            
176            /** 
177             * Parses a JSON-RPC 2.0 notification string. This method is 
178             * thread-safe.
179             *
180             * @param jsonString            The JSON-RPC 2.0 notification string, 
181             *                              UTF-8 encoded. Must not be {@code null}.
182             * @param preserveOrder         {@code true} to preserve the order of
183             *                              JSON object members in parameters.
184             * @param ignoreVersion         {@code true} to skip a check of the 
185             *                              {@code "jsonrpc":"2.0"} version 
186             *                              attribute in the JSON-RPC 2.0 message.
187             * @param parseNonStdAttributes {@code true} to parse non-standard
188             *                              attributes found in the JSON-RPC 2.0 
189             *                              message.
190             *
191             * @return The corresponding JSON-RPC 2.0 notification object.
192             *
193             * @throws JSONRPC2ParseException With detailed message if parsing 
194             *                                failed.
195             */
196            public static JSONRPC2Notification parse(final String jsonString, 
197                                                     final boolean preserveOrder, 
198                                                     final boolean ignoreVersion, 
199                                                     final boolean parseNonStdAttributes)
200                    throws JSONRPC2ParseException {
201                    
202                    JSONRPC2Parser parser = new JSONRPC2Parser(preserveOrder, ignoreVersion, parseNonStdAttributes);
203                    
204                    return parser.parseJSONRPC2Notification(jsonString);
205            }
206            
207            
208            /** 
209             * Constructs a new JSON-RPC 2.0 notification with no parameters.
210             *
211             * @param method The name of the requested method. Must not be 
212             *               {@code null}.
213             */
214            public JSONRPC2Notification(final String method) {
215                    
216                    setMethod(method);
217                    setParams(null);
218            }
219    
220    
221            /** 
222             * Constructs a new JSON-RPC 2.0 notification with positional (JSON 
223             * array) parameters.
224             *
225             * @param method           The name of the requested method. Must not 
226             *                         be {@code null}.
227             * @param positionalParams The positional (JSON array) parameters, 
228             *                         {@code null} if none.
229             */
230            public JSONRPC2Notification(final String method, 
231                                        final List<Object> positionalParams) {
232                    
233                    setMethod(method);
234                    setPositionalParams(positionalParams);
235            }
236                    
237            
238            /** 
239             * Constructs a new JSON-RPC 2.0 notification with named (JSON object)
240             * parameters.
241             *
242             * @param method      The name of the requested method.
243             * @param namedParams The named (JSON object) parameters, {@code null} 
244             *                    if none.
245             */
246            public JSONRPC2Notification(final String method, 
247                                        final Map <String,Object> namedParams) {
248                    
249                    setMethod(method);
250                    setNamedParams(namedParams);
251            }
252            
253            
254            /** 
255             * Gets the name of the requested method.
256             *
257             * @return The method name.
258             */
259            public String getMethod() {
260                    
261                    return method;
262            }
263            
264            
265            /**
266             * Sets the name of the requested method.
267             *
268             * @param method The method name. Must not be {@code null}.
269             */
270            public void setMethod(final String method) {
271                    
272                    // The method name is mandatory
273                    if (method == null)
274                            throw new IllegalArgumentException("The method name must not be null");
275    
276                    this.method = method;
277            }
278            
279            
280            /** 
281             * Gets the parameters type ({@link JSONRPC2ParamsType#ARRAY positional}, 
282             * {@link JSONRPC2ParamsType#OBJECT named} or 
283             * {@link JSONRPC2ParamsType#NO_PARAMS none}).
284             *
285             * @return The parameters type.
286             */
287            public JSONRPC2ParamsType getParamsType() {
288            
289                    if (positionalParams == null && namedParams == null)
290                            return JSONRPC2ParamsType.NO_PARAMS;
291    
292                    if (positionalParams != null)
293                            return JSONRPC2ParamsType.ARRAY;
294    
295                    if (namedParams != null)
296                            return JSONRPC2ParamsType.OBJECT;
297    
298                    else
299                            return JSONRPC2ParamsType.NO_PARAMS;
300            }
301            
302            /** 
303             * Gets the notification parameters.
304             *
305             * <p>This method was deprecated in version 1.30. Use
306             * {@link #getPositionalParams} or {@link #getNamedParams} instead.
307             *
308             * @return The parameters as {@code List<Object>} for positional (JSON 
309             *         array), {@code Map<String,Object>} for named (JSON object),
310             *         or {@code null} if none.
311             */
312            @Deprecated
313            public Object getParams() {
314                    
315                    switch (getParamsType()) {
316    
317                            case ARRAY:
318                                    return positionalParams;
319    
320                            case OBJECT:
321                                    return namedParams;
322    
323                            default:
324                                    return null;
325                    }
326            }
327    
328    
329            /**
330             * Gets the positional (JSON array) parameters.
331             *
332             * @since 1.30
333             *
334             * @return The positional (JSON array) parameters, {@code null} if none
335             *         or named.
336             */
337            public List<Object> getPositionalParams() {
338    
339                    return positionalParams;
340            }
341    
342    
343            /**
344             * Gets the named parameters.
345             *
346             * @since 1.30
347             *
348             * @return The named (JSON object) parameters, {@code null} if none or 
349             *         positional.
350             */
351            public Map<String,Object> getNamedParams() {
352    
353                    return namedParams;
354            }
355            
356            
357            /**
358             * Sets the notification parameters.
359             *
360             * <p>This method was deprecated in version 1.30. Use
361             * {@link #setPositionalParams} or {@link #setNamedParams} instead.
362             *
363             * @param params The parameters. For positional (JSON array) pass a 
364             *               {@code List<Object>}. For named (JSON object) pass a
365             *               {@code Map<String,Object>}. If there are no 
366             *               parameters pass {@code null}.
367             */
368            @Deprecated
369            @SuppressWarnings("unchecked")
370            public void setParams(final Object params) {
371            
372                    if (params == null)
373                            return;
374                            
375                    else if (params instanceof List)
376                            positionalParams = (List<Object>)params;
377                            
378                    else if (params instanceof Map)
379                            namedParams = (Map<String,Object>)params;
380                            
381                    else
382                            throw new IllegalArgumentException("The notification parameters must be of type List, Map or null");
383            }
384    
385    
386            /**
387             * Sets the positional (JSON array) request parameters.
388             *
389             * @since 1.30
390             *
391             * @param positionalParams The positional (JSON array) request 
392             *                         parameters, {@code null} if none.
393             */
394            public void setPositionalParams(final List<Object> positionalParams) {
395    
396                    if (positionalParams == null)
397                            return;
398    
399                    this.positionalParams = positionalParams;
400            }
401    
402    
403            /**
404             * Sets the named (JSON object) request parameters.
405             *
406             * @since 1.30
407             *
408             * @param namedParams The named (JSON object) request parameters,
409             *                    {@code null} if none.
410             */
411            public void setNamedParams(final Map<String,Object> namedParams) {
412    
413                    if (namedParams == null)
414                            return;
415    
416                    this.namedParams = namedParams;
417            }
418            
419            
420            @Override
421            public JSONObject toJSONObject() {
422            
423                    JSONObject notf = new JSONObject();
424                    
425                    notf.put("method", method);
426                    
427                    // The params can be omitted if none
428                    switch (getParamsType()) {
429    
430                            case ARRAY:
431                                    notf.put("params", positionalParams);
432                                    break;
433    
434                            case OBJECT:
435                                    notf.put("params", namedParams);
436                                    break;
437                    }
438                    
439                    notf.put("jsonrpc", "2.0");
440                    
441                    
442                    Map <String,Object> nonStdAttributes = getNonStdAttributes();
443                    
444                    if (nonStdAttributes != null) {
445                    
446                            for (final Map.Entry<String,Object> attr: nonStdAttributes.entrySet())
447                                    notf.put(attr.getKey(), attr.getValue());
448                    }
449                    
450                    return notf;
451            }
452    }