/*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9.0b2)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
+ * Copyright (C) $$Year-Rel$$ The Jalview Authors
*
* This file is part of Jalview.
*
*/
package jalview.rest;
-import jalview.httpserver.AbstractRequestHandler;
-
+import java.io.BufferedReader;
import java.io.IOException;
+import java.io.InputStreamReader;
import java.io.PrintWriter;
+import java.lang.reflect.InvocationTargetException;
import java.net.BindException;
+import java.util.HashMap;
+import java.util.Map;
+import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
+import org.eclipse.jetty.server.handler.ContextHandler;
+
+import jalview.bin.Console;
+import jalview.httpserver.AbstractRequestHandler;
+import jalview.httpserver.HttpServer;
+
/**
* A simple handler to process (or delegate) HTTP requests on /jalview/rest
*/
public class RestHandler extends AbstractRequestHandler
{
+
+ public enum Status
+ {
+ STARTED, IN_PROGRESS, FINISHED, ERROR, NOT_RUN
+ }
+
+ public interface EndpointI
+ {
+ public String getPath();
+
+ public String getName();
+
+ public String getParameters();
+
+ public String getDescription();
+
+ public void processEndpoint(HttpServletRequest request,
+ HttpServletResponse response);
+
+ }
+
private static final String MY_PATH = "rest";
private static final String MY_NAME = "Rest";
+ private String missingEndpointMessage = null;
+
+ private boolean init = false;
+
+ // map of method names and method handlers
+ private Map<String, AbstractEndpoint> endpoints = null;
+
+ protected Map<String, AbstractEndpoint> getEndpoints()
+ {
+ return endpoints;
+ }
+
+ protected AbstractEndpoint getNewEndpoint(String name)
+ {
+ if (getEndpoints() == null)
+ {
+ return null;
+ }
+ try
+ {
+ return getEndpoints().get(name).getClass()
+ .getDeclaredConstructor(API.class).newInstance(this);
+ } catch (InstantiationException | IllegalAccessException
+ | IllegalArgumentException | InvocationTargetException
+ | NoSuchMethodException | SecurityException e)
+ {
+ Console.debug("Could not instantiate new endpoint '" + name + "'", e);
+ }
+ return null;
+ }
+
/**
* Singleton instance of this class
*/
*
* @throws BindException
*/
- private RestHandler() throws BindException
+ protected RestHandler() throws BindException
{
- setPath(MY_PATH);
+ init();
/*
* We don't register the handler here - this is done as a special case in
* Currently just echoes the request; add helper classes as required to
* process requests
*/
- final String queryString = request.getQueryString();
- final String reply = "REST not yet implemented; received "
- + request.getMethod() + ": " + request.getRequestURL()
- + (queryString == null ? "" : "?" + queryString);
- System.out.println(reply);
+ // This "pointless" call to request.getInputStream() seems to preserve the
+ // InputStream for use later in getRequestBody.
+ request.getInputStream();
+
+ String remoteAddr = request.getRemoteAddr();
+ if (!("127.0.0.1".equals(remoteAddr) || "localhost".equals(remoteAddr)))
+ {
+ returnError(request, response, "Not authorised: " + remoteAddr,
+ HttpServletResponse.SC_UNAUTHORIZED);
+ return;
+ }
+ if (getEndpoints() == null)
+ {
+ final String queryString = request.getQueryString();
+ final String reply = "REST not yet implemented; received "
+ + request.getMethod() + ": " + request.getRequestURL()
+ + (queryString == null ? "" : "?" + queryString);
+ Console.error(reply);
+
+ response.setHeader("Cache-Control", "no-cache/no-store");
+ response.setHeader("Content-type", "text/plain");
+ final PrintWriter writer = response.getWriter();
+ writer.write(reply);
+ return;
+ }
+
+ String endpointName = getRequestedEndpointName(request);
+ Console.debug(endpointName);
+
+ if (!getEndpoints().containsKey(endpointName)
+ || getEndpoints().get(endpointName) == null)
+ {
+ Console.debug(
+ "RestHandler did not find endpoint '" + endpointName + "'");
+
+ response.setHeader("Cache-Control", "no-cache/no-store");
+ response.setHeader("Content-type", "text/plain");
+ PrintWriter writer = response.getWriter();
+ writer.write(missingEndpointMessage == null
+ ? "REST endpoint '" + endpointName + "' not defined"
+ : missingEndpointMessage);
+ writer.write("\n");
+ writer.write("Available endpoints are:\n");
+ ContextHandler ch = HttpServer.getInstance().getContextHandler(this);
+ String base = HttpServer.getInstance().getUri().toString();
+ String contextPath = ch == null ? "" : ch.getContextPath();
+ for (String key : getEndpoints().keySet())
+ {
+ writer.write(base + contextPath + "/" + key + "\n");
+ }
+ response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
+ return;
+ }
response.setHeader("Cache-Control", "no-cache/no-store");
response.setHeader("Content-type", "text/plain");
- final PrintWriter writer = response.getWriter();
- writer.write(reply);
- writer.close();
+ EndpointI ep = getNewEndpoint(endpointName);
+ ep.processEndpoint(request, response);
+
+ return;
}
/**
return MY_NAME;
}
-}
+ /**
+ * Initialise methods
+ *
+ * @throws BindException
+ */
+ protected void init() throws BindException
+ {
+ init(MY_PATH);
+ }
+
+ protected void init(String path) throws BindException
+ {
+ setPath(path);
+ // Override this in extended class
+ // e.g. registerHandler and addEndpoints
+ }
+
+ protected void addEndpoint(AbstractEndpoint ep)
+ {
+ if (getEndpoints() == null)
+ {
+ endpoints = new HashMap<>();
+ }
+ endpoints.put(ep.getPath(), ep);
+ Console.debug("REST API, added endpoint '" + ep.getPath() + "'");
+ }
+
+ protected String getRequestedEndpointName(HttpServletRequest request)
+ {
+ String pathInfo = request.getPathInfo();
+ int slashpos = pathInfo.indexOf('/', 1);
+ return slashpos > 1 ? pathInfo.substring(1, slashpos)
+ : pathInfo.substring(1);
+ }
+
+ protected void returnError(HttpServletRequest request,
+ HttpServletResponse response, String message)
+ {
+ returnError(request, response, message,
+ HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
+ }
+
+ protected void returnError(HttpServletRequest request,
+ HttpServletResponse response, String message, int statusCode)
+ {
+ response.setStatus(statusCode);
+ String endpointName = getRequestedEndpointName(request);
+ Console.error(getName() + " error: endpoint " + endpointName
+ + " failed: '" + message + "'");
+ try
+ {
+ PrintWriter writer = response.getWriter();
+ writer.write("Endpoint " + endpointName + ": " + message);
+ } catch (IOException e)
+ {
+ Console.debug("Could not write to REST response for endpoint "
+ + endpointName, e);
+ }
+ }
+
+ protected void returnStatus(HttpServletResponse response, String id,
+ Status status)
+ {
+ try
+ {
+ PrintWriter writer = response.getWriter();
+ if (id != null)
+ writer.write("id=" + id + "\n");
+ if (status != null)
+ writer.write("status=" + status.toString() + "\n");
+ } catch (IOException e)
+ {
+ Console.debug("Could not write status to REST response for id:" + id,
+ e);
+ }
+ }
+
+ protected String getRequestBody(HttpServletRequest request,
+ HttpServletResponse response) throws IOException
+ {
+ StringBuilder sb = new StringBuilder();
+ BufferedReader reader = null;
+ Console.debug("REQUEST=" + request.toString());
+ Console.debug("REQUEST.Content-Length=" + request.getContentLength());
+ try
+ {
+ reader = request.getReader();
+ Console.debug("Using getReader()");
+ } catch (IllegalStateException e)
+ {
+ ServletInputStream is = request.getInputStream();
+ reader = new BufferedReader(new InputStreamReader(is));
+ Console.debug("Using getInputStream()");
+ }
+ if (reader != null)
+ {
+ try
+ {
+ String line;
+ while ((line = reader.readLine()) != null)
+ {
+ sb.append(line).append('\n');
+ }
+ } finally
+ {
+ reader.close();
+ }
+ }
+ else
+ {
+ returnError(request, response, "Error reading body of HTTP request");
+ }
+ return sb.toString();
+ }
+}
\ No newline at end of file