package jalview.rest; import java.io.IOException; import java.io.PrintWriter; import java.util.HashMap; import java.util.Map; import java.util.concurrent.CompletableFuture; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import jalview.bin.Cache; import jalview.rest.RestHandler.Status; public abstract class AbstractEndpointAsync extends AbstractEndpoint { public AbstractEndpointAsync(API api, String path, String name, String parameters, String description) { super(api, path, name, parameters, description); } protected String idExtension = null; protected String id = null; protected CompletableFuture cf = null; protected Map> cfMap = new HashMap<>(); protected Map stringsPassedToProcessAsync = new HashMap<>(); protected void setCompletableFuture(CompletableFuture cf) { this.cf = cf; if (getId() != null) cfMap.put(getId(), cf); } protected CompletableFuture getCompletableFuture() { if (cf == null && getId() != null && cfMap.get(getId()) != null) cf = cfMap.get(getId()); return this.cf; } protected void setId(String id) { this.id = id; } protected void setIdExtension(String idExtension) { setId(getPath() + "::" + idExtension); } protected String getId() { return this.id; } /* * Override the three methods * initialise (get parameters, set id (extension), set cf if using an existing one) * process (what to do in the cf if not using an existing one) * finalise (extra stuff to do at the end of the first call to this) */ protected void initialise(HttpServletRequest request, HttpServletResponse response) { // should be overridden // must setId(request, extension) setId(request, null); } protected abstract void processAsync(HttpServletRequest request, HttpServletResponse response, final Map finalMap); protected void finalise(HttpServletRequest request, HttpServletResponse response) { // can be Overridden } @Override public void processEndpoint(HttpServletRequest request, HttpServletResponse response) { // subclass method initialise(request, response); if (checkStatus(request, response, Status.STARTED)) { String finishedString = null; if (getStatus() == Status.FINISHED) { finishedString = finished(request, response); } returnStatus(request, response, finishedString); return; } if (getCompletableFuture() == null) { final Map finalStringMap = stringsPassedToProcessAsync; setCompletableFuture(CompletableFuture.runAsync(() -> { // subclass method this.processAsync(request, response, finalStringMap); })); } addWhenCompleteCompletableFuture(); // subclass method finalise(request, response); returnStatus(response); changeStatus(Status.IN_PROGRESS); } protected void atEnd() { } protected String finished(HttpServletRequest request, HttpServletResponse response) { return null; } /* * Shared methods below here */ protected String setId(HttpServletRequest request, String extension) { String idString = request.getParameter("id"); if (idString == null) { setIdExtension(extension); } else { setId(idString); } return getId(); } protected void changeStatus(Status status) { String id = getId(); // 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); } protected Status getStatus() { getAPI(); return API.getStatusMap().get(getId()); } protected void returnStatus(HttpServletResponse response) { returnStatus(null, response, null); } protected void returnStatus(HttpServletRequest request, HttpServletResponse response, String message) { String id = getId(); try { PrintWriter writer = response.getWriter(); if (id != null) { writer.write("id=" + id + "\n"); } getAPI(); if (API.getRequestMap().get(id) != null) { getAPI(); writer.write( "request=" + API.getRequestMap().get(id).toString() + "\n"); } if (getStatus() != null) { if (getStatus() == Status.ERROR) { response.setStatus(500); } writer.write("status=" + getStatus().toString() + "\n"); } if (message != null) { writer.write(message); } } catch (IOException e) { Cache.debug(e); } } protected boolean checkStatus(HttpServletRequest request, HttpServletResponse response) { return checkStatus(request, response, null); } protected boolean checkStatus(HttpServletRequest request, HttpServletResponse response, Status set) { String id = getId(); Status status = getStatus(); if (status == null) { if (set != null) changeStatus(set); API.getRequestMap().put(id, request.getRequestURI()); return false; } else { return true; } } protected void addWhenCompleteCompletableFuture() { String id = getId(); cf.whenComplete((Void, e) -> { if (e != null) { Cache.error("Endpoint job " + id + " did not complete"); Cache.debug(e); changeStatus(Status.ERROR); } else { Cache.info("Endpoint job " + id + " completed successfully"); changeStatus(Status.FINISHED); atEnd(); } }); } }