001package com.thetransactioncompany.jsonrpc2.server;
002
003
004import java.util.Hashtable;
005
006import com.thetransactioncompany.jsonrpc2.JSONRPC2Error;
007import com.thetransactioncompany.jsonrpc2.JSONRPC2Notification;
008import com.thetransactioncompany.jsonrpc2.JSONRPC2Request;
009import 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 */
042public class Dispatcher implements RequestHandler, NotificationHandler {
043        
044        
045        /** 
046         * Hashtable of request name / handler pairs. 
047         */
048        private final Hashtable<String,RequestHandler> requestHandlers;
049        
050        
051        /**
052         * Hashtable of notification name / handler pairs.
053         */
054        private final 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                java.util.Set<String> var = requestHandlers.keySet();
122                return var.toArray(new String[var.size()]);
123        }
124        
125        
126        @Override
127        public String[] handledNotifications() {
128
129                java.util.Set<String> var = notificationHandlers.keySet();
130                return var.toArray(new String[var.size()]);
131        }
132        
133        
134        /**
135         * Gets the handler for the specified JSON-RPC 2.0 request name.
136         *
137         * @param requestName The request name to lookup.
138         *
139         * @return The corresponding request handler or {@code null} if none 
140         *         was found.
141         */
142        public RequestHandler getRequestHandler(final String requestName) {
143        
144                return requestHandlers.get(requestName);
145        }
146        
147        
148        /**
149         * Gets the handler for the specified JSON-RPC 2.0 notification name.
150         *
151         * @param notificationName The notification name to lookup.
152         *
153         * @return The corresponding notification handler or {@code null} if
154         *         none was found.
155         */
156        public NotificationHandler getNotificationHandler(final String notificationName) {
157        
158                return notificationHandlers.get(notificationName);
159        }
160        
161        
162        /**
163         * @deprecated
164         */
165        public JSONRPC2Response dispatch(final JSONRPC2Request request, final MessageContext requestCtx) {
166        
167                return process(request, requestCtx);
168        }
169        
170        
171        @Override
172        public JSONRPC2Response process(final JSONRPC2Request request, final MessageContext requestCtx) {
173        
174                long startNanosec = 0;
175                
176                // Measure request processing time?
177                if (reportProcTime)
178                        startNanosec = System.nanoTime();
179                
180        
181                final String method = request.getMethod();
182                
183                RequestHandler handler = getRequestHandler(method);
184                
185                if (handler == null) {
186                
187                        // We didn't find a handler for the requested RPC
188                
189                        Object id = request.getID();
190                        
191                        return new JSONRPC2Response(JSONRPC2Error.METHOD_NOT_FOUND, id);
192                }
193                        
194                // Process the request
195                
196                JSONRPC2Response response = handler.process(request, requestCtx);
197                
198                if (reportProcTime) {
199                
200                        final long procTimeNanosec = System.nanoTime() - startNanosec;
201                        
202                        response.appendNonStdAttribute("xProcTime", procTimeNanosec / 1000 + " us");
203                }
204                
205                return response;
206        }
207        
208        
209        /**
210         * @deprecated
211         */
212        public void dispatch(final JSONRPC2Notification notification, final MessageContext notificationCtx) {
213        
214                process(notification, notificationCtx);
215        }
216        
217        
218        @Override
219        public void process(final JSONRPC2Notification notification, final MessageContext notificationCtx) {
220        
221                final String method = notification.getMethod();
222                
223                NotificationHandler handler = getNotificationHandler(method);
224                
225                if (handler == null) {
226                
227                        // We didn't find a handler for the requested RPC
228                        return;
229                }
230                        
231                // Process the notification
232                
233                handler.process(notification, notificationCtx);
234        }
235        
236        
237        /**
238         * Controls reporting of request processing time by appending a 
239         * non-standard "xProcTime" attribute to the JSON-RPC 2.0 response.
240         * Reporting is disabled by default.
241         *
242         * @param enable {@code true} to enable proccessing time reporting, 
243         *               {@code false} to disable it.
244         */
245        public void reportProcTime(final boolean enable) {
246        
247                reportProcTime = enable;
248        }
249        
250        
251        /**
252         * Returns {@code true} if reporting of request processing time is 
253         * enabled. See the {@link #reportProcTime} description for more
254         * information.
255         *
256         * @return {@code true} if reporting of request processing time is 
257         *         enabled, else {@code false}.
258         */
259        public boolean reportsProcTime() {
260        
261                return reportProcTime;
262        }
263}