[6091] | 1 | // License: GPL. For details, see LICENSE file.
|
---|
| 2 | package org.openstreetmap.josm.io.remotecontrol;
|
---|
| 3 |
|
---|
| 4 | import java.io.BufferedOutputStream;
|
---|
| 5 | import java.io.BufferedReader;
|
---|
| 6 | import java.io.IOException;
|
---|
| 7 | import java.io.InputStreamReader;
|
---|
| 8 | import java.io.OutputStream;
|
---|
| 9 | import java.io.OutputStreamWriter;
|
---|
| 10 | import java.io.PrintWriter;
|
---|
| 11 | import java.io.StringWriter;
|
---|
| 12 | import java.io.Writer;
|
---|
| 13 | import java.net.Socket;
|
---|
| 14 | import java.util.Arrays;
|
---|
| 15 | import java.util.Date;
|
---|
| 16 | import java.util.HashMap;
|
---|
| 17 | import java.util.Map;
|
---|
| 18 | import java.util.Map.Entry;
|
---|
| 19 | import java.util.StringTokenizer;
|
---|
| 20 | import java.util.TreeMap;
|
---|
| 21 | import java.util.regex.Matcher;
|
---|
| 22 | import java.util.regex.Pattern;
|
---|
| 23 |
|
---|
[6248] | 24 | import org.openstreetmap.josm.Main;
|
---|
[6143] | 25 | import org.openstreetmap.josm.gui.help.HelpUtil;
|
---|
[6091] | 26 | import org.openstreetmap.josm.io.remotecontrol.handler.AddNodeHandler;
|
---|
| 27 | import org.openstreetmap.josm.io.remotecontrol.handler.AddWayHandler;
|
---|
| 28 | import org.openstreetmap.josm.io.remotecontrol.handler.FeaturesHandler;
|
---|
| 29 | import org.openstreetmap.josm.io.remotecontrol.handler.ImageryHandler;
|
---|
| 30 | import org.openstreetmap.josm.io.remotecontrol.handler.ImportHandler;
|
---|
| 31 | import org.openstreetmap.josm.io.remotecontrol.handler.LoadAndZoomHandler;
|
---|
| 32 | import org.openstreetmap.josm.io.remotecontrol.handler.LoadObjectHandler;
|
---|
| 33 | import org.openstreetmap.josm.io.remotecontrol.handler.OpenFileHandler;
|
---|
| 34 | import org.openstreetmap.josm.io.remotecontrol.handler.RequestHandler;
|
---|
| 35 | import org.openstreetmap.josm.io.remotecontrol.handler.RequestHandler.RequestHandlerBadRequestException;
|
---|
| 36 | import org.openstreetmap.josm.io.remotecontrol.handler.RequestHandler.RequestHandlerErrorException;
|
---|
| 37 | import org.openstreetmap.josm.io.remotecontrol.handler.RequestHandler.RequestHandlerForbiddenException;
|
---|
| 38 | import org.openstreetmap.josm.io.remotecontrol.handler.VersionHandler;
|
---|
| 39 | import org.openstreetmap.josm.tools.Utils;
|
---|
| 40 |
|
---|
| 41 | /**
|
---|
| 42 | * Processes HTTP "remote control" requests.
|
---|
| 43 | */
|
---|
| 44 | public class RequestProcessor extends Thread {
|
---|
| 45 | /**
|
---|
| 46 | * RemoteControl protocol version. Change minor number for compatible
|
---|
| 47 | * interface extensions. Change major number in case of incompatible
|
---|
| 48 | * changes.
|
---|
| 49 | */
|
---|
| 50 | public static final String PROTOCOLVERSION = "{\"protocolversion\": {\"major\": " +
|
---|
| 51 | RemoteControl.protocolMajorVersion + ", \"minor\": " +
|
---|
| 52 | RemoteControl.protocolMinorVersion +
|
---|
| 53 | "}, \"application\": \"JOSM RemoteControl\"}";
|
---|
| 54 |
|
---|
| 55 | /** The socket this processor listens on */
|
---|
| 56 | private Socket request;
|
---|
| 57 |
|
---|
| 58 | /**
|
---|
| 59 | * Collection of request handlers.
|
---|
| 60 | * Will be initialized with default handlers here. Other plug-ins
|
---|
| 61 | * can extend this list by using @see addRequestHandler
|
---|
| 62 | */
|
---|
| 63 | private static Map<String, Class<? extends RequestHandler>> handlers = new TreeMap<String, Class<? extends RequestHandler>>();
|
---|
| 64 |
|
---|
| 65 | /**
|
---|
| 66 | * Constructor
|
---|
| 67 | *
|
---|
| 68 | * @param request A socket to read the request.
|
---|
| 69 | */
|
---|
| 70 | public RequestProcessor(Socket request) {
|
---|
| 71 | super("RemoteControl request processor");
|
---|
| 72 | this.setDaemon(true);
|
---|
| 73 | this.request = request;
|
---|
| 74 | }
|
---|
| 75 |
|
---|
| 76 | /**
|
---|
| 77 | * Spawns a new thread for the request
|
---|
[6223] | 78 | * @param request The request to process
|
---|
[6091] | 79 | */
|
---|
| 80 | public static void processRequest(Socket request) {
|
---|
| 81 | RequestProcessor processor = new RequestProcessor(request);
|
---|
| 82 | processor.start();
|
---|
| 83 | }
|
---|
| 84 |
|
---|
| 85 | /**
|
---|
| 86 | * Add external request handler. Can be used by other plug-ins that
|
---|
| 87 | * want to use remote control.
|
---|
| 88 | *
|
---|
| 89 | * @param command The command to handle.
|
---|
| 90 | * @param handler The additional request handler.
|
---|
| 91 | */
|
---|
| 92 | static void addRequestHandlerClass(String command,
|
---|
| 93 | Class<? extends RequestHandler> handler) {
|
---|
| 94 | addRequestHandlerClass(command, handler, false);
|
---|
| 95 | }
|
---|
| 96 |
|
---|
| 97 | /**
|
---|
| 98 | * Add external request handler. Message can be suppressed.
|
---|
| 99 | * (for internal use)
|
---|
| 100 | *
|
---|
| 101 | * @param command The command to handle.
|
---|
| 102 | * @param handler The additional request handler.
|
---|
| 103 | * @param silent Don't show message if true.
|
---|
| 104 | */
|
---|
| 105 | private static void addRequestHandlerClass(String command,
|
---|
| 106 | Class<? extends RequestHandler> handler, boolean silent) {
|
---|
[6792] | 107 | if(command.charAt(0) == '/') {
|
---|
[6091] | 108 | command = command.substring(1);
|
---|
| 109 | }
|
---|
| 110 | String commandWithSlash = "/" + command;
|
---|
| 111 | if (handlers.get(commandWithSlash) != null) {
|
---|
[6248] | 112 | Main.info("RemoteControl: ignoring duplicate command " + command
|
---|
[6091] | 113 | + " with handler " + handler.getName());
|
---|
| 114 | } else {
|
---|
| 115 | if (!silent) {
|
---|
[6248] | 116 | Main.info("RemoteControl: adding command \"" +
|
---|
[6091] | 117 | command + "\" (handled by " + handler.getSimpleName() + ")");
|
---|
| 118 | }
|
---|
| 119 | handlers.put(commandWithSlash, handler);
|
---|
| 120 | }
|
---|
| 121 | }
|
---|
| 122 |
|
---|
| 123 | /** Add default request handlers */
|
---|
| 124 | static {
|
---|
| 125 | addRequestHandlerClass(LoadAndZoomHandler.command, LoadAndZoomHandler.class, true);
|
---|
| 126 | addRequestHandlerClass(LoadAndZoomHandler.command2, LoadAndZoomHandler.class, true);
|
---|
| 127 | addRequestHandlerClass(ImageryHandler.command, ImageryHandler.class, true);
|
---|
| 128 | addRequestHandlerClass(AddNodeHandler.command, AddNodeHandler.class, true);
|
---|
| 129 | addRequestHandlerClass(AddWayHandler.command, AddWayHandler.class, true);
|
---|
| 130 | addRequestHandlerClass(ImportHandler.command, ImportHandler.class, true);
|
---|
| 131 | addRequestHandlerClass(VersionHandler.command, VersionHandler.class, true);
|
---|
| 132 | addRequestHandlerClass(LoadObjectHandler.command, LoadObjectHandler.class, true);
|
---|
| 133 | addRequestHandlerClass(OpenFileHandler.command, OpenFileHandler.class, true);
|
---|
| 134 | addRequestHandlerClass(FeaturesHandler.command, FeaturesHandler.class, true);
|
---|
| 135 | }
|
---|
| 136 |
|
---|
| 137 | /**
|
---|
| 138 | * The work is done here.
|
---|
| 139 | */
|
---|
| 140 | @Override
|
---|
| 141 | public void run() {
|
---|
| 142 | Writer out = null;
|
---|
| 143 | try {
|
---|
| 144 | OutputStream raw = new BufferedOutputStream(request.getOutputStream());
|
---|
| 145 | out = new OutputStreamWriter(raw);
|
---|
| 146 | BufferedReader in = new BufferedReader(new InputStreamReader(request.getInputStream(), "ASCII"));
|
---|
| 147 |
|
---|
| 148 | String get = in.readLine();
|
---|
| 149 | if (get == null) {
|
---|
| 150 | sendError(out);
|
---|
| 151 | return;
|
---|
| 152 | }
|
---|
[6248] | 153 | Main.info("RemoteControl received: " + get);
|
---|
[6091] | 154 |
|
---|
| 155 | StringTokenizer st = new StringTokenizer(get);
|
---|
| 156 | if (!st.hasMoreTokens()) {
|
---|
| 157 | sendError(out);
|
---|
| 158 | return;
|
---|
| 159 | }
|
---|
| 160 | String method = st.nextToken();
|
---|
| 161 | if (!st.hasMoreTokens()) {
|
---|
| 162 | sendError(out);
|
---|
| 163 | return;
|
---|
| 164 | }
|
---|
| 165 | String url = st.nextToken();
|
---|
| 166 |
|
---|
| 167 | if (!method.equals("GET")) {
|
---|
| 168 | sendNotImplemented(out);
|
---|
| 169 | return;
|
---|
| 170 | }
|
---|
| 171 |
|
---|
| 172 | int questionPos = url.indexOf('?');
|
---|
| 173 |
|
---|
| 174 | String command = questionPos < 0 ? url : url.substring(0, questionPos);
|
---|
| 175 |
|
---|
| 176 | Map <String,String> headers = new HashMap<String, String>();
|
---|
| 177 | int k=0, MAX_HEADERS=20;
|
---|
| 178 | while (k<MAX_HEADERS) {
|
---|
| 179 | get=in.readLine();
|
---|
| 180 | if (get==null) break;
|
---|
| 181 | k++;
|
---|
| 182 | String[] h = get.split(": ", 2);
|
---|
| 183 | if (h.length==2) {
|
---|
| 184 | headers.put(h[0], h[1]);
|
---|
| 185 | } else break;
|
---|
| 186 | }
|
---|
| 187 |
|
---|
| 188 | // Who sent the request: trying our best to detect
|
---|
| 189 | // not from localhost => sender = IP
|
---|
| 190 | // from localhost: sender = referer header, if exists
|
---|
| 191 | String sender = null;
|
---|
| 192 |
|
---|
| 193 | if (!request.getInetAddress().isLoopbackAddress()) {
|
---|
| 194 | sender = request.getInetAddress().getHostAddress();
|
---|
| 195 | } else {
|
---|
| 196 | String ref = headers.get("Referer");
|
---|
| 197 | Pattern r = Pattern.compile("(https?://)?([^/]*)");
|
---|
| 198 | if (ref!=null) {
|
---|
| 199 | Matcher m = r.matcher(ref);
|
---|
| 200 | if (m.find()) {
|
---|
| 201 | sender = m.group(2);
|
---|
| 202 | }
|
---|
| 203 | }
|
---|
| 204 | if (sender == null) {
|
---|
| 205 | sender = "localhost";
|
---|
| 206 | }
|
---|
| 207 | }
|
---|
| 208 |
|
---|
| 209 | // find a handler for this command
|
---|
| 210 | Class<? extends RequestHandler> handlerClass = handlers.get(command);
|
---|
| 211 | if (handlerClass == null) {
|
---|
| 212 | String usage = getUsageAsHtml();
|
---|
[6143] | 213 | String websiteDoc = HelpUtil.getWikiBaseHelpUrl() +"/Help/Preferences/RemoteControl";
|
---|
[6223] | 214 | String help = "No command specified! The following commands are available:<ul>" + usage
|
---|
[6091] | 215 | + "</ul>" + "See <a href=\""+websiteDoc+"\">"+websiteDoc+"</a> for complete documentation.";
|
---|
| 216 | sendBadRequest(out, help);
|
---|
| 217 | } else {
|
---|
| 218 | // create handler object
|
---|
| 219 | RequestHandler handler = handlerClass.newInstance();
|
---|
| 220 | try {
|
---|
| 221 | handler.setCommand(command);
|
---|
| 222 | handler.setUrl(url);
|
---|
| 223 | handler.setSender(sender);
|
---|
| 224 | handler.handle();
|
---|
| 225 | sendHeader(out, "200 OK", handler.getContentType(), false);
|
---|
| 226 | out.write("Content-length: " + handler.getContent().length()
|
---|
| 227 | + "\r\n");
|
---|
| 228 | out.write("\r\n");
|
---|
| 229 | out.write(handler.getContent());
|
---|
| 230 | out.flush();
|
---|
| 231 | } catch (RequestHandlerErrorException ex) {
|
---|
| 232 | sendError(out);
|
---|
| 233 | } catch (RequestHandlerBadRequestException ex) {
|
---|
| 234 | sendBadRequest(out, ex.getMessage());
|
---|
| 235 | } catch (RequestHandlerForbiddenException ex) {
|
---|
| 236 | sendForbidden(out, ex.getMessage());
|
---|
| 237 | }
|
---|
| 238 | }
|
---|
| 239 |
|
---|
| 240 | } catch (IOException ioe) {
|
---|
[6643] | 241 | Main.debug(Main.getErrorMessage(ioe));
|
---|
[6091] | 242 | } catch (Exception e) {
|
---|
[6643] | 243 | Main.error(e);
|
---|
[6091] | 244 | try {
|
---|
| 245 | sendError(out);
|
---|
| 246 | } catch (IOException e1) {
|
---|
[6792] | 247 | Main.warn(e1);
|
---|
[6091] | 248 | }
|
---|
| 249 | } finally {
|
---|
| 250 | try {
|
---|
| 251 | request.close();
|
---|
| 252 | } catch (IOException e) {
|
---|
[6643] | 253 | Main.debug(Main.getErrorMessage(e));
|
---|
[6091] | 254 | }
|
---|
| 255 | }
|
---|
| 256 | }
|
---|
| 257 |
|
---|
| 258 | /**
|
---|
| 259 | * Sends a 500 error: server error
|
---|
| 260 | *
|
---|
| 261 | * @param out
|
---|
| 262 | * The writer where the error is written
|
---|
| 263 | * @throws IOException
|
---|
| 264 | * If the error can not be written
|
---|
| 265 | */
|
---|
| 266 | private void sendError(Writer out) throws IOException {
|
---|
| 267 | sendHeader(out, "500 Internal Server Error", "text/html", true);
|
---|
| 268 | out.write("<HTML>\r\n");
|
---|
| 269 | out.write("<HEAD><TITLE>Internal Error</TITLE>\r\n");
|
---|
| 270 | out.write("</HEAD>\r\n");
|
---|
| 271 | out.write("<BODY>");
|
---|
[6907] | 272 | out.write("<H1>HTTP Error 500: Internal Server Error</H1>\r\n");
|
---|
[6091] | 273 | out.write("</BODY></HTML>\r\n");
|
---|
| 274 | out.flush();
|
---|
| 275 | }
|
---|
| 276 |
|
---|
| 277 | /**
|
---|
| 278 | * Sends a 501 error: not implemented
|
---|
| 279 | *
|
---|
| 280 | * @param out
|
---|
| 281 | * The writer where the error is written
|
---|
| 282 | * @throws IOException
|
---|
| 283 | * If the error can not be written
|
---|
| 284 | */
|
---|
| 285 | private void sendNotImplemented(Writer out) throws IOException {
|
---|
| 286 | sendHeader(out, "501 Not Implemented", "text/html", true);
|
---|
| 287 | out.write("<HTML>\r\n");
|
---|
| 288 | out.write("<HEAD><TITLE>Not Implemented</TITLE>\r\n");
|
---|
| 289 | out.write("</HEAD>\r\n");
|
---|
| 290 | out.write("<BODY>");
|
---|
| 291 | out.write("<H1>HTTP Error 501: Not Implemented</h2>\r\n");
|
---|
| 292 | out.write("</BODY></HTML>\r\n");
|
---|
| 293 | out.flush();
|
---|
| 294 | }
|
---|
| 295 |
|
---|
| 296 | /**
|
---|
| 297 | * Sends a 403 error: forbidden
|
---|
| 298 | *
|
---|
| 299 | * @param out
|
---|
| 300 | * The writer where the error is written
|
---|
| 301 | * @throws IOException
|
---|
| 302 | * If the error can not be written
|
---|
| 303 | */
|
---|
| 304 | private void sendForbidden(Writer out, String help) throws IOException {
|
---|
| 305 | sendHeader(out, "403 Forbidden", "text/html", true);
|
---|
| 306 | out.write("<HTML>\r\n");
|
---|
| 307 | out.write("<HEAD><TITLE>Forbidden</TITLE>\r\n");
|
---|
| 308 | out.write("</HEAD>\r\n");
|
---|
| 309 | out.write("<BODY>");
|
---|
| 310 | out.write("<H1>HTTP Error 403: Forbidden</h2>\r\n");
|
---|
| 311 | if (help != null) {
|
---|
| 312 | out.write(help);
|
---|
| 313 | }
|
---|
| 314 | out.write("</BODY></HTML>\r\n");
|
---|
| 315 | out.flush();
|
---|
| 316 | }
|
---|
| 317 |
|
---|
| 318 | /**
|
---|
| 319 | * Sends a 403 error: forbidden
|
---|
| 320 | *
|
---|
| 321 | * @param out
|
---|
| 322 | * The writer where the error is written
|
---|
| 323 | * @throws IOException
|
---|
| 324 | * If the error can not be written
|
---|
| 325 | */
|
---|
| 326 | private void sendBadRequest(Writer out, String help) throws IOException {
|
---|
| 327 | sendHeader(out, "400 Bad Request", "text/html", true);
|
---|
| 328 | out.write("<HTML>\r\n");
|
---|
| 329 | out.write("<HEAD><TITLE>Bad Request</TITLE>\r\n");
|
---|
| 330 | out.write("</HEAD>\r\n");
|
---|
| 331 | out.write("<BODY>");
|
---|
| 332 | out.write("<H1>HTTP Error 400: Bad Request</h2>\r\n");
|
---|
| 333 | if (help != null) {
|
---|
| 334 | out.write(help);
|
---|
| 335 | }
|
---|
| 336 | out.write("</BODY></HTML>\r\n");
|
---|
| 337 | out.flush();
|
---|
| 338 | }
|
---|
| 339 |
|
---|
| 340 | /**
|
---|
| 341 | * Send common HTTP headers to the client.
|
---|
| 342 | *
|
---|
| 343 | * @param out
|
---|
| 344 | * The Writer
|
---|
| 345 | * @param status
|
---|
| 346 | * The status string ("200 OK", "500", etc)
|
---|
| 347 | * @param contentType
|
---|
| 348 | * The content type of the data sent
|
---|
| 349 | * @param endHeaders
|
---|
| 350 | * If true, adds a new line, ending the headers.
|
---|
| 351 | * @throws IOException
|
---|
| 352 | * When error
|
---|
| 353 | */
|
---|
| 354 | private void sendHeader(Writer out, String status, String contentType,
|
---|
| 355 | boolean endHeaders) throws IOException {
|
---|
| 356 | out.write("HTTP/1.1 " + status + "\r\n");
|
---|
| 357 | Date now = new Date();
|
---|
| 358 | out.write("Date: " + now + "\r\n");
|
---|
| 359 | out.write("Server: JOSM RemoteControl\r\n");
|
---|
| 360 | out.write("Content-type: " + contentType + "\r\n");
|
---|
| 361 | out.write("Access-Control-Allow-Origin: *\r\n");
|
---|
| 362 | if (endHeaders)
|
---|
| 363 | out.write("\r\n");
|
---|
| 364 | }
|
---|
[6792] | 365 |
|
---|
[6091] | 366 | public static String getHandlersInfoAsJSON() {
|
---|
| 367 | StringBuilder r = new StringBuilder();
|
---|
| 368 | boolean first = true;
|
---|
| 369 | r.append("[");
|
---|
[6792] | 370 |
|
---|
[6091] | 371 | for (Entry<String, Class<? extends RequestHandler>> p : handlers.entrySet()) {
|
---|
| 372 | if (first) {
|
---|
| 373 | first = false;
|
---|
| 374 | } else {
|
---|
| 375 | r.append(", ");
|
---|
| 376 | }
|
---|
[6332] | 377 | r.append(getHandlerInfoAsJSON(p.getKey()));
|
---|
[6091] | 378 | }
|
---|
| 379 | r.append("]");
|
---|
| 380 |
|
---|
| 381 | return r.toString();
|
---|
| 382 | }
|
---|
| 383 |
|
---|
[6332] | 384 | public static String getHandlerInfoAsJSON(String cmd) {
|
---|
[6091] | 385 | StringWriter w = new StringWriter();
|
---|
| 386 | PrintWriter r = new PrintWriter(w);
|
---|
| 387 | RequestHandler handler = null;
|
---|
| 388 | try {
|
---|
[6142] | 389 | Class<?> c = handlers.get(cmd);
|
---|
[6091] | 390 | if (c==null) return null;
|
---|
| 391 | handler = handlers.get(cmd).newInstance();
|
---|
| 392 | } catch (Exception ex) {
|
---|
[6643] | 393 | Main.error(ex);
|
---|
[6091] | 394 | return null;
|
---|
| 395 | }
|
---|
| 396 |
|
---|
| 397 | r.printf("{ \"request\" : \"%s\"", cmd);
|
---|
[6536] | 398 | if (handler.getUsage() != null) {
|
---|
| 399 | r.printf(", \"usage\" : \"%s\"", handler.getUsage());
|
---|
| 400 | }
|
---|
[6091] | 401 | r.append(", \"parameters\" : [");
|
---|
| 402 |
|
---|
[6362] | 403 | String[] params = handler.getMandatoryParams();
|
---|
[6091] | 404 | if (params != null) {
|
---|
| 405 | for (int i = 0; i < params.length; i++) {
|
---|
| 406 | if (i == 0) {
|
---|
| 407 | r.append('\"');
|
---|
| 408 | } else {
|
---|
| 409 | r.append(", \"");
|
---|
| 410 | }
|
---|
| 411 | r.append(params[i]).append('\"');
|
---|
| 412 | }
|
---|
| 413 | }
|
---|
| 414 | r.append("], \"optional\" : [");
|
---|
[6362] | 415 | String[] optional = handler.getOptionalParams();
|
---|
[6091] | 416 | if (optional != null) {
|
---|
| 417 | for (int i = 0; i < optional.length; i++) {
|
---|
| 418 | if (i == 0) {
|
---|
| 419 | r.append('\"');
|
---|
| 420 | } else {
|
---|
| 421 | r.append(", \"");
|
---|
| 422 | }
|
---|
| 423 | r.append(optional[i]).append('\"');
|
---|
| 424 | }
|
---|
| 425 | }
|
---|
[6792] | 426 |
|
---|
[6091] | 427 | r.append("], \"examples\" : [");
|
---|
[6362] | 428 | String[] examples = handler.getUsageExamples(cmd.substring(1));
|
---|
[6091] | 429 | if (examples != null) {
|
---|
| 430 | for (int i = 0; i < examples.length; i++) {
|
---|
| 431 | if (i == 0) {
|
---|
| 432 | r.append('\"');
|
---|
| 433 | } else {
|
---|
| 434 | r.append(", \"");
|
---|
| 435 | }
|
---|
| 436 | r.append(examples[i]).append('\"');
|
---|
| 437 | }
|
---|
| 438 | }
|
---|
| 439 | r.append("]}");
|
---|
| 440 | try {
|
---|
| 441 | return w.toString();
|
---|
| 442 | } finally {
|
---|
| 443 | try {
|
---|
| 444 | w.close();
|
---|
| 445 | } catch (IOException ex) {
|
---|
[6792] | 446 | Main.warn(ex);
|
---|
[6091] | 447 | }
|
---|
| 448 | }
|
---|
| 449 | }
|
---|
| 450 |
|
---|
| 451 | /**
|
---|
| 452 | * Reports HTML message with the description of all available commands
|
---|
[6223] | 453 | * @return HTML message with the description of all available commands
|
---|
[6091] | 454 | * @throws IllegalAccessException
|
---|
[6643] | 455 | * @throws InstantiationException
|
---|
[6091] | 456 | */
|
---|
| 457 | public static String getUsageAsHtml() throws IllegalAccessException, InstantiationException {
|
---|
| 458 | StringBuilder usage = new StringBuilder(1024);
|
---|
| 459 | for (Entry<String, Class<? extends RequestHandler>> handler : handlers.entrySet()) {
|
---|
| 460 | RequestHandler sample = handler.getValue().newInstance();
|
---|
| 461 | String[] mandatory = sample.getMandatoryParams();
|
---|
| 462 | String[] optional = sample.getOptionalParams();
|
---|
[6332] | 463 | String[] examples = sample.getUsageExamples(handler.getKey().substring(1));
|
---|
[6091] | 464 | usage.append("<li>");
|
---|
| 465 | usage.append(handler.getKey());
|
---|
[6536] | 466 | if (sample.getUsage() != null && !sample.getUsage().isEmpty()) {
|
---|
| 467 | usage.append(" — <i>").append(sample.getUsage()).append("</i>");
|
---|
| 468 | }
|
---|
[6091] | 469 | if (mandatory != null) {
|
---|
| 470 | usage.append("<br/>mandatory parameters: ").append(Utils.join(", ", Arrays.asList(mandatory)));
|
---|
| 471 | }
|
---|
| 472 | if (optional != null) {
|
---|
| 473 | usage.append("<br/>optional parameters: ").append(Utils.join(", ", Arrays.asList(optional)));
|
---|
| 474 | }
|
---|
| 475 | if (examples != null) {
|
---|
| 476 | usage.append("<br/>examples: ");
|
---|
| 477 | for (String ex: examples) {
|
---|
| 478 | usage.append("<br/> <a href=\"http://localhost:8111"+ex+"\">"+ex+"</a>");
|
---|
| 479 | }
|
---|
| 480 | }
|
---|
| 481 | usage.append("</li>");
|
---|
| 482 | }
|
---|
| 483 | return usage.toString();
|
---|
| 484 | }
|
---|
| 485 | }
|
---|