/* * 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 jalview.bin.Cache; import jalview.httpserver.AbstractRequestHandler; /** * 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 } public interface EndpointI { public void setName(String name); public String getName(); public void processEndpoint(HttpServletRequest request, HttpServletResponse response); } public interface Endpoint { public void processEndpoint(String endpointName, 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; /** * 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); writer.close(); return; } String endpointName = getEndpointName(request); if (!endpoints.containsKey(endpointName) || endpoints.get(endpointName) == null) { response.setHeader("Cache-Control", "no-cache/no-store"); response.setHeader("Content-type", "text/plain"); response.setStatus(400); PrintWriter writer = response.getWriter(); writer.write(missingEndpointMessage == null ? "REST endpoint '" + endpointName + "' not defined" : missingEndpointMessage); writer.close(); return; } response.setHeader("Cache-Control", "no-cache/no-store"); response.setHeader("Content-type", "text/plain"); Endpoint ep = endpoints.get(endpointName); ep.processEndpoint(endpointName, 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(String name, Endpoint ep) { if (endpoints == null) { endpoints = new HashMap<>(); } endpoints.put(name, ep); return true; } protected String getEndpointName(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(500); // set this to something better String endpointName = getEndpointName(request); Cache.error(this.MY_NAME + " error: endpoint " + endpointName + " failed: '" + message + "'"); try { PrintWriter writer = response.getWriter(); writer.write("Endpoint " + endpointName + ": " + message); writer.close(); } catch (IOException e) { Cache.debug(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) { Cache.debug(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(); } }