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 }