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}