2 * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3 * Copyright (C) $$Year-Rel$$ The Jalview Authors
5 * This file is part of Jalview.
7 * Jalview is free software: you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation, either version 3
10 * of the License, or (at your option) any later version.
12 * Jalview is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty
14 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15 * PURPOSE. See the GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with Jalview. If not, see <http://www.gnu.org/licenses/>.
19 * The Jalview Authors are detailed in the 'AUTHORS' file.
23 import java.io.BufferedReader;
24 import java.io.IOException;
25 import java.io.InputStreamReader;
26 import java.io.PrintWriter;
27 import java.net.BindException;
28 import java.util.HashMap;
31 import javax.servlet.ServletInputStream;
32 import javax.servlet.http.HttpServletRequest;
33 import javax.servlet.http.HttpServletResponse;
35 import org.eclipse.jetty.server.handler.ContextHandler;
37 import jalview.bin.Console;
38 import jalview.httpserver.AbstractRequestHandler;
39 import jalview.httpserver.HttpServer;
42 * A simple handler to process (or delegate) HTTP requests on /jalview/rest
44 public class RestHandler extends AbstractRequestHandler
49 STARTED, IN_PROGRESS, FINISHED, ERROR, NOT_RUN
52 public interface EndpointI
54 public String getPath();
56 public String getName();
58 public String getParameters();
60 public String getDescription();
62 public void processEndpoint(HttpServletRequest request,
63 HttpServletResponse response);
67 private static final String MY_PATH = "rest";
69 private static final String MY_NAME = "Rest";
71 private String missingEndpointMessage = null;
73 private boolean init = false;
75 // map of method names and method handlers
76 private Map<String, EndpointI> endpoints = null;
78 protected Map<String, EndpointI> getEndpoints()
84 * Singleton instance of this class
86 private static RestHandler instance = null;
89 * Returns the singleton instance of this class
92 * @throws BindException
94 public static RestHandler getInstance() throws BindException
96 synchronized (RestHandler.class)
100 instance = new RestHandler();
107 * Private constructor enforces use of singleton
109 * @throws BindException
111 protected RestHandler() throws BindException
116 * We don't register the handler here - this is done as a special case in
117 * HttpServer initialisation; to do it here would invite an infinite loop of
118 * RestHandler/HttpServer constructor
123 * Handle a jalview/rest request
125 * @throws IOException
128 protected void processRequest(HttpServletRequest request,
129 HttpServletResponse response) throws IOException
132 * Currently just echoes the request; add helper classes as required to
135 // This "pointless" call to request.getInputStream() seems to preserve the
136 // InputStream for use later in getRequestBody.
137 request.getInputStream();
138 if (endpoints == null)
140 final String queryString = request.getQueryString();
141 final String reply = "REST not yet implemented; received "
142 + request.getMethod() + ": " + request.getRequestURL()
143 + (queryString == null ? "" : "?" + queryString);
144 System.out.println(reply);
146 response.setHeader("Cache-Control", "no-cache/no-store");
147 response.setHeader("Content-type", "text/plain");
148 final PrintWriter writer = response.getWriter();
153 String endpointName = getRequestedEndpointName(request);
155 if (!endpoints.containsKey(endpointName)
156 || endpoints.get(endpointName) == null)
159 response.setHeader("Cache-Control", "no-cache/no-store");
160 response.setHeader("Content-type", "text/plain");
161 PrintWriter writer = response.getWriter();
162 writer.write(missingEndpointMessage == null
163 ? "REST endpoint '" + endpointName + "' not defined"
164 : missingEndpointMessage);
166 writer.write("Available endpoints are:\n");
167 ContextHandler ch = HttpServer.getInstance().getContextHandler(this);
168 String base = HttpServer.getInstance().getUri().toString();
169 String contextPath = ch == null ? "" : ch.getContextPath();
170 for (String key : endpoints.keySet())
172 writer.write(base + contextPath + "/" + key + "\n");
174 response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
178 response.setHeader("Cache-Control", "no-cache/no-store");
179 response.setHeader("Content-type", "text/plain");
180 EndpointI ep = endpoints.get(endpointName);
181 ep.processEndpoint(request, response);
187 * Returns a display name for this service
190 public String getName()
198 * @throws BindException
200 protected void init() throws BindException
205 protected void init(String path) throws BindException
208 // Override this in extended class
209 // e.g. registerHandler and addEndpoints
212 protected boolean addEndpoint(EndpointI ep)
214 if (endpoints == null)
216 endpoints = new HashMap<>();
218 AbstractEndpoint e = (AbstractEndpoint) ep;
219 endpoints.put(ep.getPath(), ep);
223 protected String getRequestedEndpointName(HttpServletRequest request)
225 String pathInfo = request.getPathInfo();
226 int slashpos = pathInfo.indexOf('/', 1);
227 return slashpos > 1 ? pathInfo.substring(1, slashpos)
228 : pathInfo.substring(1);
231 protected String[] getEndpointPathParameters(HttpServletRequest request)
233 String pathInfo = request.getPathInfo();
234 int slashpos = pathInfo.indexOf('/', 1);
235 return slashpos < 1 ? null
236 : pathInfo.substring(slashpos + 1).split("/");
239 protected void returnError(HttpServletRequest request,
240 HttpServletResponse response, String message)
242 response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
243 String endpointName = getRequestedEndpointName(request);
244 Console.error(getName() + " error: endpoint " + endpointName
245 + " failed: '" + message + "'");
248 PrintWriter writer = response.getWriter();
249 writer.write("Endpoint " + endpointName + ": " + message);
250 } catch (IOException e)
252 Console.debug("Could not write to REST response for endpoint "
257 protected void returnStatus(HttpServletResponse response, String id,
262 PrintWriter writer = response.getWriter();
264 writer.write("id=" + id + "\n");
266 writer.write("status=" + status.toString() + "\n");
267 } catch (IOException e)
269 Console.debug("Could not write status to REST response for id:" + id,
274 protected String getRequestBody(HttpServletRequest request,
275 HttpServletResponse response) throws IOException
277 StringBuilder sb = new StringBuilder();
278 BufferedReader reader = null;
279 Console.debug("REQUEST=" + request.toString());
280 Console.debug("REQUEST.Content-Lenggth=" + request.getContentLength());
283 reader = request.getReader();
284 Console.debug("Using getReader()");
285 } catch (IllegalStateException e)
287 ServletInputStream is = request.getInputStream();
288 Console.debug("INPUTSTREAM "
289 + (is.isFinished() ? "FINISHED" : "NOT FINISHED"));
290 reader = new BufferedReader(new InputStreamReader(is));
291 Console.debug("Using getInputStream()");
298 while ((line = reader.readLine()) != null)
300 sb.append(line).append('\n');
309 returnError(request, response, "Error reading body of HTTP request");
311 return sb.toString();