/* * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$) * Copyright (C) $$Year-Rel$$ The Jalview Authors * * This file is part of Jalview. * * Jalview is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation, either version 3 * of the License, or (at your option) any later version. * * Jalview is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty * of MERCHANTABILITY or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Jalview. If not, see . * The Jalview Authors are detailed in the 'AUTHORS' file. */ package jalview.rest; import java.io.BufferedReader; import java.io.IOException; import java.io.PrintWriter; import java.net.BindException; import java.util.HashMap; import java.util.Map; 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 endpoints = null; protected Map getEndpoints() { return endpoints; } /** * Singleton instance of this class */ private static RestHandler instance = null; /** * Returns the singleton instance of this class * * @return * @throws BindException */ public static RestHandler getInstance() throws BindException { synchronized (RestHandler.class) { if (instance == null) { instance = new RestHandler(); } } return instance; } /** * Private constructor enforces use of singleton * * @throws BindException */ protected RestHandler() throws BindException { init(); /* * We don't register the handler here - this is done as a special case in * HttpServer initialisation; to do it here would invite an infinite loop of * RestHandler/HttpServer constructor */ } /** * Handle a jalview/rest request * * @throws IOException */ @Override protected void processRequest(HttpServletRequest request, HttpServletResponse response) throws IOException { /* * Currently just echoes the request; add helper classes as required to * process requests */ System.out.println(request.toString()); if (endpoints == null) { final String queryString = request.getQueryString(); final String reply = "REST not yet implemented; received " + request.getMethod() + ": " + request.getRequestURL() + (queryString == null ? "" : "?" + queryString); System.out.println(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); if (!endpoints.containsKey(endpointName) || endpoints.get(endpointName) == null) { 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 : endpoints.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"); EndpointI ep = endpoints.get(endpointName); ep.processEndpoint(request, response); return; } /** * Returns a display name for this service */ @Override public String getName() { 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 boolean addEndpoint(EndpointI ep) { if (endpoints == null) { endpoints = new HashMap<>(); } AbstractEndpoint e = (AbstractEndpoint) ep; endpoints.put(ep.getPath(), ep); return true; } 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 String[] getEndpointPathParameters(HttpServletRequest request) { String pathInfo = request.getPathInfo(); int slashpos = pathInfo.indexOf('/', 1); return slashpos < 1 ? null : pathInfo.substring(slashpos + 1).split("/"); } protected void returnError(HttpServletRequest request, HttpServletResponse response, String message) { response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); 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) throws IOException { StringBuilder sb = new StringBuilder(); BufferedReader reader = request.getReader(); try { String line; while ((line = reader.readLine()) != null) { sb.append(line).append('\n'); } } finally { reader.close(); } return sb.toString(); } }