3 import java.io.IOException;
4 import java.io.PrintWriter;
5 import java.util.HashMap;
7 import java.util.concurrent.CompletableFuture;
9 import javax.servlet.http.HttpServletRequest;
10 import javax.servlet.http.HttpServletResponse;
12 import jalview.bin.Console;
13 import jalview.gui.AlignFrame;
14 import jalview.rest.RestHandler.Status;
16 public abstract class AbstractEndpointAsync extends AbstractEndpoint
18 public AbstractEndpointAsync(API api, String path, String name,
19 String parameters, String description)
21 super(api, path, name, parameters, description);
24 protected String idExtension = null;
26 protected String id = null;
28 protected CompletableFuture<Void> cf = null;
30 protected Map<String, CompletableFuture<Void>> cfMap = new HashMap<>();
32 protected Map<String, Object> objectsPassedToProcessAsync = new HashMap<>();
34 private Status tempStatus = null;
36 protected void removeFromCaches(HttpServletRequest request)
38 removeFromCaches(getId(request));
41 protected void removeFromCaches(String id)
43 CompletableFuture cf = cfMap.get(id);
44 if (cf == null || cf.isDone())
46 AlignFrame.removeFromAlignFrameCache(id);
47 API.getStatusMap().remove(getId());
50 protected void setCompletableFuture(CompletableFuture<Void> cf)
54 cfMap.put(getId(), cf);
57 protected CompletableFuture<Void> getCompletableFuture()
59 if (cf == null && getId() != null && cfMap.get(getId()) != null)
60 cf = cfMap.get(getId());
64 protected void setId(String id)
69 protected void setIdExtension(String idExtension)
71 setId(getPath() + "::" + idExtension);
74 protected String getId()
80 * Override the three methods
81 * initialise (get parameters, set id (extension), set cf if using an existing one)
82 * process (what to do in the cf if not using an existing one)
83 * finalise (extra stuff to do at the end of the first call to this)
85 protected void initialise(HttpServletRequest request,
86 HttpServletResponse response)
88 // should be overridden
89 // MUST setId(request, extension)
90 this.setId(request, null);
92 this.saveParameters(request);
95 protected void saveParameters(HttpServletRequest request)
98 this.getFromId(request);
99 this.getEndpointPathParameters(request);
100 this.getQueryParameters(request);
101 this.getOptions(request);
102 this.getRequestUrl(request);
105 protected abstract void processAsync(HttpServletRequest request,
106 HttpServletResponse response, final Map<String, Object> finalMap);
108 protected void finalise(HttpServletRequest request,
109 HttpServletResponse response)
115 public void processEndpoint(HttpServletRequest request,
116 HttpServletResponse response)
120 initialise(request, response);
122 Console.debug("**** STATUS=" + getStatus());
123 if (checkStatus(request, response, Status.STARTED))
126 // double check alignframe
127 Console.debug("**** STATUS2=" + getStatus());
128 if (getStatus().compareTo(Status.IN_PROGRESS) > 0
129 && getAlignFrameUsingId(request) == null)
131 Console.debug("**** STATUS3=" + getStatus());
132 // delete key from cache
133 Console.debug("Cannot find cached AlignFrame for '" + getId()
134 + "', deleting key from cache");
135 removeFromCaches(getId());
136 this.changeStatus(null);
137 this.setCompletableFuture(null);
142 String alreadyFinishedString = null;
143 if (getStatus() == Status.FINISHED)
145 alreadyFinishedString = finishedResponseString(request, response);
147 returnStatus(request, response, alreadyFinishedString);
154 if (getCompletableFuture() == null)
156 final Map<String, Object> finalObjectMap = objectsPassedToProcessAsync;
157 setCompletableFuture(CompletableFuture.runAsync(() -> {
161 this.processAsync(request, response, finalObjectMap);
162 } catch (ClassCastException e)
164 Console.info("Something went wrong with async endpoint execution"
169 addWhenCompleteCompletableFuture();
172 finalise(request, response);
174 returnStatus(response);
175 changeStatus(Status.IN_PROGRESS);
178 protected void atEnd()
182 protected String finishedResponseString(HttpServletRequest request,
183 HttpServletResponse response)
189 * Shared methods below here
192 protected String setId(HttpServletRequest request, String extension)
194 String idString = getId(request);
195 Console.debug("GOT ID '" + idString + "'");
196 if (idString == null)
198 setIdExtension(extension);
208 protected void changeStatus(Status status)
211 // don't change a job's status if it has finished or died
212 if (getStatus() == Status.FINISHED || getStatus() == Status.ERROR)
215 if (status != Status.NOT_RUN)
216 API.getStatusMap().put(id, status);
219 protected Status getStatus()
221 Status status = API.getStatusMap().get(getId());
222 return status == null ? tempStatus : status;
225 protected void returnStatus(HttpServletResponse response)
227 returnStatus(null, response, null);
230 protected void returnStatus(HttpServletRequest request,
231 HttpServletResponse response, String message)
236 PrintWriter writer = response.getWriter();
239 writer.write("id=" + id + "\n");
241 if (API.getRequestMap().get(id) != null)
244 "request=" + API.getRequestMap().get(id).toString() + "\n");
246 if (getStatus() != null)
251 if (getRequestUrl(request) != null)
253 response.sendRedirect(getRequestUrl(request));
254 Console.debug(getStatus() + ": redirecting to '"
255 + getRequestUrl(request) + "'");
259 } catch (InterruptedException e)
265 response.setStatus(HttpServletResponse.SC_ACCEPTED);
269 if (getRequestUrl(request) != null)
271 response.sendRedirect(getRequestUrl(request));
272 Console.debug(getStatus() + ": redirecting to '"
273 + getRequestUrl(request) + "'");
277 } catch (InterruptedException e)
283 response.setStatus(HttpServletResponse.SC_ACCEPTED);
287 response.setStatus(HttpServletResponse.SC_CREATED);
290 response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
294 writer.write("status=" + getStatus().toString() + "\n");
298 writer.write(message);
300 } catch (IOException e)
302 Console.debug("Exception writing to REST response", e);
306 protected boolean checkStatus(HttpServletRequest request,
307 HttpServletResponse response)
309 return checkStatus(request, response, null);
312 protected boolean checkStatus(HttpServletRequest request,
313 HttpServletResponse response, Status set)
316 Status status = getStatus();
321 API.getRequestMap().put(id, request.getRequestURI());
330 protected void addWhenCompleteCompletableFuture()
333 cf.whenComplete((Void, e) -> {
336 Console.error("Endpoint job " + id + " did not complete", e);
337 changeStatus(Status.ERROR);
341 Console.info("Endpoint job " + id + " completed successfully");
342 changeStatus(Status.FINISHED);
349 protected void returnError(HttpServletRequest request,
350 HttpServletResponse response, String message)
352 changeStatus(Status.NOT_RUN);
353 super.returnError(request, response, message);
357 protected boolean deleteFromCache()