From bc2ee2e16489d94627a8ead6d059aefd91c7c743 Mon Sep 17 00:00:00 2001 From: Ben Soares Date: Fri, 3 Sep 2021 18:17:24 +0100 Subject: [PATCH] JAL-3785 renaming, adding to interface, tidying --- src/jalview/httpserver/HttpServer.java | 11 ++++ src/jalview/rest/API.java | 11 +++- .../rest/{Endpoint.java => AbstractEndpoint.java} | 59 +++++++++++++++++--- ...dpointAsync.java => AbstractEndpointAsync.java} | 29 +++++----- ...ceEndpoint.java => FetchSequencesEndpoint.java} | 24 ++++---- src/jalview/rest/HighlightSequenceEndpoint.java | 19 ++++--- ...ntEndpoint.java => InputAlignmentEndpoint.java} | 50 +++++++++-------- src/jalview/rest/RestHandler.java | 24 +++++++- 8 files changed, 157 insertions(+), 70 deletions(-) rename src/jalview/rest/{Endpoint.java => AbstractEndpoint.java} (61%) rename src/jalview/rest/{EndpointAsync.java => AbstractEndpointAsync.java} (83%) rename src/jalview/rest/{FetchSequenceEndpoint.java => FetchSequencesEndpoint.java} (67%) rename src/jalview/rest/{OpenAlignmentEndpoint.java => InputAlignmentEndpoint.java} (74%) diff --git a/src/jalview/httpserver/HttpServer.java b/src/jalview/httpserver/HttpServer.java index 397f1a8..057c714 100644 --- a/src/jalview/httpserver/HttpServer.java +++ b/src/jalview/httpserver/HttpServer.java @@ -322,6 +322,17 @@ public class HttpServer } /** + * Gets the ContextHandler attached to this handler. Useful for obtaining the + * full path used to access a given handler. + * + * @param handler + */ + public ContextHandler getContextHandler(AbstractRequestHandler handler) + { + return myHandlers.get(handler); + } + + /** * This sets the "suggested" port to use. It can only be called once before * starting the HttpServer instance. After the server has actually started the * port is set to the actual port being used and cannot be changed. diff --git a/src/jalview/rest/API.java b/src/jalview/rest/API.java index e7b2ced..0bd3caa 100644 --- a/src/jalview/rest/API.java +++ b/src/jalview/rest/API.java @@ -14,6 +14,8 @@ public class API extends RestHandler private static Map requestMap = new HashMap<>(); + private static Map objectMap = new HashMap<>(); + private static API instance = null; public static API getInstance() throws BindException @@ -43,8 +45,8 @@ public class API extends RestHandler super.init(MY_PATH); // add endpoints here - addEndpoint(new FetchSequenceEndpoint(this)); - addEndpoint(new OpenAlignmentEndpoint(this)); + addEndpoint(new FetchSequencesEndpoint(this)); + addEndpoint(new InputAlignmentEndpoint(this)); addEndpoint(new HighlightSequenceEndpoint(this)); setPath(MY_PATH); @@ -72,4 +74,9 @@ public class API extends RestHandler { return requestMap; } + + protected static Map getObjectMap() + { + return objectMap; + } } diff --git a/src/jalview/rest/Endpoint.java b/src/jalview/rest/AbstractEndpoint.java similarity index 61% rename from src/jalview/rest/Endpoint.java rename to src/jalview/rest/AbstractEndpoint.java index e062564..2db0366 100644 --- a/src/jalview/rest/Endpoint.java +++ b/src/jalview/rest/AbstractEndpoint.java @@ -10,15 +10,31 @@ import javax.servlet.http.HttpServletResponse; import jalview.bin.Cache; import jalview.rest.RestHandler.EndpointI; -public abstract class Endpoint implements EndpointI +public abstract class AbstractEndpoint implements EndpointI { - protected final String name; + private final String path; - protected API api; + private API api; - public String getName() + private final String name; + + private final String parameters; + + private final String description; + + public AbstractEndpoint(API api, String path, String name, + String parameters, String description) { - return this.name; + this.api = api; + this.path = path; + this.name = name; + this.parameters = parameters; + this.description = description; + } + + public String getPath() + { + return this.path; } protected API getAPI() @@ -26,10 +42,19 @@ public abstract class Endpoint implements EndpointI return this.api; } - public Endpoint(API api, final String name) + public String getName() { - this.api = api; - this.name = name; + return this.name; + } + + public String getParameters() + { + return this.parameters; + } + + public String getDescription() + { + return this.description; } public abstract void processEndpoint(HttpServletRequest request, @@ -51,7 +76,7 @@ public abstract class Endpoint implements EndpointI HttpServletResponse response, String message) { response.setStatus(500); // set this to something better - String endpointName = getName(); + String endpointName = getPath(); Cache.error(getAPI().getName() + " error: endpoint " + endpointName + " failed: '" + message + "'"); try @@ -84,4 +109,20 @@ public abstract class Endpoint implements EndpointI return sb.toString(); } + protected boolean checkParameters(HttpServletRequest request, + HttpServletResponse response, int i) + { + String[] parameters = getEndpointPathParameters(request); + + // check we can run fetchsequence + if (parameters.length < i) + { + returnError(request, response, + "requires parameters:" + getParameters() + "\n" + getName() + + ": " + getDescription()); + return false; + } + return true; + } + } \ No newline at end of file diff --git a/src/jalview/rest/EndpointAsync.java b/src/jalview/rest/AbstractEndpointAsync.java similarity index 83% rename from src/jalview/rest/EndpointAsync.java rename to src/jalview/rest/AbstractEndpointAsync.java index ff11db1..eb8280f 100644 --- a/src/jalview/rest/EndpointAsync.java +++ b/src/jalview/rest/AbstractEndpointAsync.java @@ -12,11 +12,12 @@ import javax.servlet.http.HttpServletResponse; import jalview.bin.Cache; import jalview.rest.RestHandler.Status; -public abstract class EndpointAsync extends Endpoint +public abstract class AbstractEndpointAsync extends AbstractEndpoint { - public EndpointAsync(API api, final String name) + public AbstractEndpointAsync(API api, String path, String name, + String parameters, String description) { - super(api, name); + super(api, path, name, parameters, description); } protected String idExtension = null; @@ -27,7 +28,7 @@ public abstract class EndpointAsync extends Endpoint protected Map> cfMap = new HashMap<>(); - protected Map stringMap = new HashMap<>(); + protected Map stringsPassedToProcessAsync = new HashMap<>(); protected void setCompletableFuture(CompletableFuture cf) { @@ -50,7 +51,7 @@ public abstract class EndpointAsync extends Endpoint protected void setIdExtension(String idExtension) { - setId(getName() + "::" + idExtension); + setId(getPath() + "::" + idExtension); } protected String getId() @@ -72,7 +73,7 @@ public abstract class EndpointAsync extends Endpoint setId(request, null); } - protected abstract void process(HttpServletRequest request, + protected abstract void processAsync(HttpServletRequest request, HttpServletResponse response, final Map finalMap); protected void finalise(HttpServletRequest request, @@ -96,10 +97,10 @@ public abstract class EndpointAsync extends Endpoint if (getCompletableFuture() == null) { - final Map finalMap = stringMap; + final Map finalMap = stringsPassedToProcessAsync; setCompletableFuture(CompletableFuture.runAsync(() -> { // subclass method - this.process(request, response, finalMap); + this.processAsync(request, response, finalMap); })); } finaliseCompletableFuture(); @@ -139,12 +140,12 @@ public abstract class EndpointAsync extends Endpoint // don't change a job's status if it has finished or died if (getStatus() == Status.FINISHED || getStatus() == Status.ERROR) return; - api.getStatusMap().put(id, status); + getAPI().getStatusMap().put(id, status); } protected Status getStatus() { - return api.getStatusMap().get(getId()); + return getAPI().getStatusMap().get(getId()); } protected void returnStatus(HttpServletResponse response) @@ -157,10 +158,10 @@ public abstract class EndpointAsync extends Endpoint { writer.write("id=" + id + "\n"); } - if (api.getRequestMap().get(id) != null) + if (getAPI().getRequestMap().get(id) != null) { - writer.write( - "request=" + api.getRequestMap().get(id).toString() + "\n"); + writer.write("request=" + + getAPI().getRequestMap().get(id).toString() + "\n"); } if (getStatus() != null) { @@ -191,7 +192,7 @@ public abstract class EndpointAsync extends Endpoint { if (set != null) changeStatus(set); - api.getRequestMap().put(id, request.getRequestURI()); + getAPI().getRequestMap().put(id, request.getRequestURI()); return false; } else diff --git a/src/jalview/rest/FetchSequenceEndpoint.java b/src/jalview/rest/FetchSequencesEndpoint.java similarity index 67% rename from src/jalview/rest/FetchSequenceEndpoint.java rename to src/jalview/rest/FetchSequencesEndpoint.java index 8bf54c2..71734c6 100644 --- a/src/jalview/rest/FetchSequenceEndpoint.java +++ b/src/jalview/rest/FetchSequencesEndpoint.java @@ -10,14 +10,20 @@ import jalview.gui.Desktop; import jalview.gui.SequenceFetcher; import jalview.util.DBRefUtils; -public class FetchSequenceEndpoint extends EndpointAsync +public class FetchSequencesEndpoint extends AbstractEndpointAsync { - public FetchSequenceEndpoint(API api) + public FetchSequencesEndpoint(API api) { - super(api, name); + super(api, path, name, parameters, description); } - protected static final String name = "fetchsequence"; + private static final String path = "fetchsequences"; + + private static final String name = "Fetch Sequences"; + + private static final String parameters = ","; + + private static final String description = "Fetch sequences from online resource"; private SequenceFetcher sf; @@ -25,15 +31,11 @@ public class FetchSequenceEndpoint extends EndpointAsync protected void initialise(HttpServletRequest request, HttpServletResponse response) { - String[] parameters = getEndpointPathParameters(request); - - // check we can run fetchsequence - if (parameters.length < 2) + if (!checkParameters(request, response, 2)) { - returnError(request, response, - "requires 2 path parameters: dbname, ids"); return; } + String[] parameters = getEndpointPathParameters(request); String dbName = parameters[0]; String dbId = parameters[1]; @@ -48,7 +50,7 @@ public class FetchSequenceEndpoint extends EndpointAsync setCompletableFuture(sf.ok_actionPerformed(true)); } - protected void process(HttpServletRequest request, + protected void processAsync(HttpServletRequest request, HttpServletResponse response, Map map) { // all the work being done by the SequenceFetcher! diff --git a/src/jalview/rest/HighlightSequenceEndpoint.java b/src/jalview/rest/HighlightSequenceEndpoint.java index 556d45b..3f63ebb 100644 --- a/src/jalview/rest/HighlightSequenceEndpoint.java +++ b/src/jalview/rest/HighlightSequenceEndpoint.java @@ -3,26 +3,29 @@ package jalview.rest; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -public class HighlightSequenceEndpoint extends Endpoint +public class HighlightSequenceEndpoint extends AbstractEndpoint { public HighlightSequenceEndpoint(API api) { - super(api, name); + super(api, path, name, parameters, description); } - protected static final String name = "highlight"; + protected static final String path = "highlight"; + + private static final String name = "Highlight positions"; + + private static final String parameters = ","; + + private static final String description = "Highlight the specified sequences with the specified range(s)"; public void processEndpoint(HttpServletRequest request, HttpServletResponse response) { - String[] parameters = getEndpointPathParameters(request); - - // check we can run highlight - if (parameters.length < 1) + if (!checkParameters(request, response, 2)) { - returnError(request, response, "requires 1 path parameters: ranges"); return; } + String[] parameters = getEndpointPathParameters(request); String rangesString = parameters[0]; String[] rangeStrings = rangesString.split(","); diff --git a/src/jalview/rest/OpenAlignmentEndpoint.java b/src/jalview/rest/InputAlignmentEndpoint.java similarity index 74% rename from src/jalview/rest/OpenAlignmentEndpoint.java rename to src/jalview/rest/InputAlignmentEndpoint.java index 7e4b472..fd73b2d 100644 --- a/src/jalview/rest/OpenAlignmentEndpoint.java +++ b/src/jalview/rest/InputAlignmentEndpoint.java @@ -17,14 +17,20 @@ import jalview.bin.Cache; import jalview.gui.CutAndPasteTransfer; import jalview.gui.Desktop; -public class OpenAlignmentEndpoint extends EndpointAsync +public class InputAlignmentEndpoint extends AbstractEndpointAsync { - public OpenAlignmentEndpoint(API api) + public InputAlignmentEndpoint(API api) { - super(api, name); + super(api, path, name, parameters, description); } - protected static final String name = "openalignment"; + protected static final String path = "inputalignment"; + + private static final String name = "Input Alignment"; + + private static final String parameters = "POST | GET ?[data=|file=|url=]"; + + private static final String description = "Input an alignment from POST request body, GET request 'data' parameter, local file in 'file' parameter, url in 'url' parameter"; private String fileString; @@ -32,19 +38,17 @@ public class OpenAlignmentEndpoint extends EndpointAsync private String method; - private String dataString; + private String data; private String body; - private String content; + private boolean isPost; - private boolean post; + private boolean hasData; - private boolean data; + private String access = null; - String access = null; - - String ref = null; + private String ref = null; @Override protected void initialise(HttpServletRequest request, @@ -53,12 +57,12 @@ public class OpenAlignmentEndpoint extends EndpointAsync fileString = request.getParameter("file"); urlString = request.getParameter("url"); method = request.getMethod().toLowerCase(); - dataString = request.getParameter("data"); + data = request.getParameter("data"); body = null; - post = method.equalsIgnoreCase("post"); - data = dataString != null; + isPost = method.equalsIgnoreCase("post"); + hasData = data != null; - if (post) + if (isPost) { access = "post"; try @@ -72,7 +76,7 @@ public class OpenAlignmentEndpoint extends EndpointAsync } // for ref see md5 later } - else if (data) + else if (hasData) { access = "data"; // for ref see md5 later @@ -97,9 +101,11 @@ public class OpenAlignmentEndpoint extends EndpointAsync // final content used in Future final String content; - if (post || data) + if (isPost || hasData) { - content = post ? body : dataString; + content = isPost ? body : data; + stringsPassedToProcessAsync.put("content", content); // needed as "final + // String" in process try { MessageDigest md5 = MessageDigest.getInstance("MD5"); @@ -116,22 +122,20 @@ public class OpenAlignmentEndpoint extends EndpointAsync content = null; } - stringMap.put("content", content); - setId(request, access + "::" + ref); } protected void process(HttpServletRequest request, HttpServletResponse response) { - process(request, response, null); + processAsync(request, response, null); } - protected void process(HttpServletRequest request, + protected void processAsync(HttpServletRequest request, HttpServletResponse response, final Map finalMap) { String content = finalMap.get("content"); - if (post || data) + if (isPost || hasData) { // Sequence file contents being posted // use File -> Input Alignment -> from Textbox diff --git a/src/jalview/rest/RestHandler.java b/src/jalview/rest/RestHandler.java index 39fe26a..0c95354 100644 --- a/src/jalview/rest/RestHandler.java +++ b/src/jalview/rest/RestHandler.java @@ -30,8 +30,11 @@ import java.util.Map; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import org.eclipse.jetty.server.handler.ContextHandler; + import jalview.bin.Cache; import jalview.httpserver.AbstractRequestHandler; +import jalview.httpserver.HttpServer; /** * A simple handler to process (or delegate) HTTP requests on /jalview/rest @@ -46,8 +49,14 @@ public class RestHandler extends AbstractRequestHandler public interface EndpointI { + public String getPath(); + public String getName(); + public String getParameters(); + + public String getDescription(); + public void processEndpoint(HttpServletRequest request, HttpServletResponse response); @@ -146,12 +155,21 @@ public class RestHandler extends AbstractRequestHandler 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.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"); + } writer.close(); + response.setStatus(400); return; } @@ -195,8 +213,8 @@ public class RestHandler extends AbstractRequestHandler { endpoints = new HashMap<>(); } - Endpoint e = (Endpoint) ep; - endpoints.put(ep.getName(), ep); + AbstractEndpoint e = (AbstractEndpoint) ep; + endpoints.put(ep.getPath(), ep); return true; } -- 1.7.10.2