/*
* 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();
}
}