001    package com.thetransactioncompany.jsonrpc2.server;
002    
003    
004    import java.util.Hashtable;
005    
006    import com.thetransactioncompany.jsonrpc2.JSONRPC2Error;
007    import com.thetransactioncompany.jsonrpc2.JSONRPC2Notification;
008    import com.thetransactioncompany.jsonrpc2.JSONRPC2Request;
009    import com.thetransactioncompany.jsonrpc2.JSONRPC2Response;
010    
011    
012    /**
013     * Dispatcher for JSON-RPC 2.0 requests and notifications. This class is
014     * tread-safe.
015     *
016     * <p>Use the {@code register()} methods to add a request or notification
017     * handler for an RPC method.
018     *
019     * <p>Use the {@code process()} methods to have an incoming request or
020     * notification processed by the matching handler.
021     *
022     * <p>The {@code reportProcTime()} method enables reporting of request 
023     * processing time (in microseconds) by appending a non-standard "xProcTime" 
024     * attribute to the resulting JSON-RPC 2.0 response message.
025     *
026     * <p>Example:
027     *
028     * <pre>
029     * { 
030     *   "result"    : "xyz",
031     *   "id"        : 1,
032     *   "jsonrpc"   : "2.0",
033     *   "xProcTime" : "189 us"
034     * }
035     * </pre>
036     *
037     * <p>Note: The dispatch(...) methods were deprecated in version 1.7. Use 
038     * process(...) instead.
039     *
040     * @author Vladimir Dzhuvinov
041     */
042    public class Dispatcher implements RequestHandler, NotificationHandler {
043            
044            
045            /** 
046             * Hashtable of request name / handler pairs. 
047             */
048            private Hashtable<String,RequestHandler> requestHandlers;
049            
050            
051            /**
052             * Hashtable of notification name / handler pairs.
053             */
054            private Hashtable<String,NotificationHandler> notificationHandlers;
055            
056            
057            /**
058             * Controls reporting of request processing time by appending a 
059             * non-standard "xProcTime" attribute to the JSON-RPC 2.0 response.
060             */
061            private boolean reportProcTime = false;
062            
063            
064            /**
065             * Creates a new dispatcher with no registered handlers.
066             */
067            public Dispatcher() {
068            
069                    requestHandlers = new Hashtable<String,RequestHandler>();
070                    notificationHandlers = new Hashtable<String,NotificationHandler>();
071            }
072            
073            
074            /**
075             * Registers a new JSON-RPC 2.0 request handler.
076             *
077             * @param handler The request handler to register. Must not be 
078             *                {@code null}.
079             *
080             * @throws IllegalArgumentException On attempting to register a handler
081             *                                  that duplicates an existing request
082             *                                  name.
083             */
084            public void register(final RequestHandler handler) {
085            
086                    for (String name: handler.handledRequests()) {
087                    
088                            if (requestHandlers.containsKey(name))
089                                    throw new IllegalArgumentException("Cannot register a duplicate JSON-RPC 2.0 handler for request " + name);
090                    
091                            requestHandlers.put(name, handler);
092                    }
093            }
094            
095            
096            /**
097             * Registers a new JSON-RPC 2.0 notification handler.
098             *
099             * @param handler The notification handler to register. Must not be
100             *                {@code null}.
101             *
102             * @throws IllegalArgumentException On attempting to register a handler
103             *                                  that duplicates an existing
104             *                                  notification name.
105             */
106            public void register(final NotificationHandler handler) {
107            
108                    for (String name: handler.handledNotifications()) {
109                    
110                            if (notificationHandlers.containsKey(name))
111                                    throw new IllegalArgumentException("Cannot register a duplicate JSON-RPC 2.0 handler for notification " + name);
112                    
113                            notificationHandlers.put(name, handler);
114                    }
115            }
116            
117            
118            @Override
119            public String[] handledRequests() {
120            
121                    return requestHandlers.keySet().toArray(new String[0]);
122            }
123            
124            
125            @Override
126            public String[] handledNotifications() {
127            
128                    return notificationHandlers.keySet().toArray(new String[0]);
129            }
130            
131            
132            /**
133             * Gets the handler for the specified JSON-RPC 2.0 request name.
134             *
135             * @param requestName The request name to lookup.
136             *
137             * @return The corresponding request handler or {@code null} if none 
138             *         was found.
139             */
140            public RequestHandler getRequestHandler(final String requestName) {
141            
142                    return requestHandlers.get(requestName);
143            }
144            
145            
146            /**
147             * Gets the handler for the specified JSON-RPC 2.0 notification name.
148             *
149             * @param notificationName The notification name to lookup.
150             *
151             * @return The corresponding notification handler or {@code null} if
152             *         none was found.
153             */
154            public NotificationHandler getNotificationHandler(final String notificationName) {
155            
156                    return notificationHandlers.get(notificationName);
157            }
158            
159            
160            /**
161             * @deprecated
162             */
163            public JSONRPC2Response dispatch(final JSONRPC2Request request, final MessageContext requestCtx) {
164            
165                    return process(request, requestCtx);
166            }
167            
168            
169            @Override
170            public JSONRPC2Response process(final JSONRPC2Request request, final MessageContext requestCtx) {
171            
172                    long startNanosec = 0;
173                    
174                    // Measure request processing time?
175                    if (reportProcTime)
176                            startNanosec = System.nanoTime();
177                    
178            
179                    final String method = request.getMethod();
180                    
181                    RequestHandler handler = getRequestHandler(method);
182                    
183                    if (handler == null) {
184                    
185                            // We didn't find a handler for the requested RPC
186                    
187                            Object id = request.getID();
188                            
189                            return new JSONRPC2Response(JSONRPC2Error.METHOD_NOT_FOUND, id);
190                    }
191                            
192                    // Process the request
193                    
194                    JSONRPC2Response response = handler.process(request, requestCtx);
195                    
196                    if (reportProcTime) {
197                    
198                            final long procTimeNanosec = System.nanoTime() - startNanosec;
199                            
200                            response.appendNonStdAttribute("xProcTime", procTimeNanosec / 1000 + " us");
201                    }
202                    
203                    return response;
204            }
205            
206            
207            /**
208             * @deprecated
209             */
210            public void dispatch(final JSONRPC2Notification notification, final MessageContext notificationCtx) {
211            
212                    process(notification, notificationCtx);
213            }
214            
215            
216            @Override
217            public void process(final JSONRPC2Notification notification, final MessageContext notificationCtx) {
218            
219                    final String method = notification.getMethod();
220                    
221                    NotificationHandler handler = getNotificationHandler(method);
222                    
223                    if (handler == null) {
224                    
225                            // We didn't find a handler for the requested RPC
226                            return;
227                    }
228                            
229                    // Process the notification
230                    
231                    handler.process(notification, notificationCtx);
232            }
233            
234            
235            /**
236             * Controls reporting of request processing time by appending a 
237             * non-standard "xProcTime" attribute to the JSON-RPC 2.0 response.
238             * Reporting is disabled by default.
239             *
240             * @param enable {@code true} to enable proccessing time reporting, 
241             *               {@code false} to disable it.
242             */
243            public void reportProcTime(final boolean enable) {
244            
245                    reportProcTime = enable;
246            }
247            
248            
249            /**
250             * Returns {@code true} if reporting of request processing time is 
251             * enabled. See the {@link #reportProcTime} description for more
252             * information.
253             *
254             * @return {@code true} if reporting of request processing time is 
255             *         enabled, else {@code false}.
256             */
257            public boolean reportsProcTime() {
258            
259                    return reportProcTime;
260            }
261    }