+package jalview.rest;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+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 EndpointAsync extends Endpoint
+{
+ private String name = null;
+
+ private String idExtension = null;
+
+ private String id = null;
+
+ private CompletableFuture<Void> cf = null;
+
+ protected void setCompletableFuture(CompletableFuture<Void> cf)
+ {
+ this.cf = cf;
+ }
+
+ protected CompletableFuture<Void> getCompletableFuture()
+ {
+ return this.cf;
+ }
+
+ protected void setId(String idExtension)
+ {
+ this.id = getName() + "::" + 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 process(HttpServletRequest request,
+ HttpServletResponse response);
+
+ protected void finalise(HttpServletRequest request,
+ HttpServletResponse response)
+ {
+ // can be Overridden
+ }
+
+ protected void passCompletableFuture()
+ {
+ // Override this if you want to use an existing CompletableFuture
+ }
+
+ @Override
+ public void processEndpoint(HttpServletRequest request,
+ HttpServletResponse response)
+ {
+ initialise(request, response);
+ final String id = getId();
+
+ if (checkStatus(request, response))
+ return;
+
+ passCompletableFuture();
+ if (getCompletableFuture() == null)
+ {
+ setCompletableFuture(CompletableFuture.runAsync(() -> {
+ this.process(request, response);
+ }));
+ }
+ finaliseCompletableFuture();
+
+ finalise(request, response);
+
+ returnStatus(response);
+ changeStatus(Status.IN_PROGRESS);
+ }
+
+ /*
+ * Shared methods below here
+ */
+
+ protected String setId(HttpServletRequest request, String extension)
+ {
+ String idString = request.getParameter("id");
+ if (idString == null)
+ {
+ setId(extension);
+ idString = getId();
+ }
+ return idString;
+ }
+
+ protected void changeStatus(Status status)
+ {
+ String id = getId();
+ // don't change a job's status if it has finished or died
+ if (API.getStatusMap().get(id) == Status.FINISHED
+ || API.getStatusMap().get(id) == Status.ERROR)
+ return;
+ API.getStatusMap().put(id, status);
+ }
+
+ protected Status getStatus()
+ {
+ return API.getStatusMap().get(getId());
+ }
+
+ protected void returnStatus(HttpServletResponse response)
+ {
+ String id = getId();
+ try
+ {
+ PrintWriter writer = response.getWriter();
+ if (id != null)
+ writer.write("id=" + id + "\n");
+ if (API.getRequestMap().get(id) != null)
+ writer.write(
+ "request=" + API.getRequestMap().get(id).toString() + "\n");
+ if (API.getStatusMap().get(id) != null)
+ {
+ if (API.getStatusMap().get(id) == Status.ERROR)
+ response.setStatus(500);
+ writer.write(
+ "status=" + API.getStatusMap().get(id).toString() + "\n");
+ }
+ } catch (IOException e)
+ {
+ Cache.debug(e);
+ }
+ }
+
+ protected boolean checkStatus(HttpServletRequest request,
+ HttpServletResponse response)
+ {
+ String id = getId();
+ Status status = API.getStatusMap().get(id);
+ if (status == null)
+ {
+ API.getStatusMap().put(id, Status.STARTED);
+ API.getRequestMap().put(id, request.getRequestURI());
+ }
+ else
+ {
+ returnStatus(response);
+ return true;
+ }
+ return false;
+ }
+
+ protected void finaliseCompletableFuture()
+ {
+ 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);
+ }
+ });
+ }
+}