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.rest.RestHandler.Status;
15 public abstract class AbstractEndpointAsync extends AbstractEndpoint
17 public AbstractEndpointAsync(API api, String path, String name,
18 String parameters, String description)
20 super(api, path, name, parameters, description);
23 protected String idExtension = null;
25 protected String id = null;
27 protected CompletableFuture<Void> cf = null;
29 protected Map<String, CompletableFuture<Void>> cfMap = new HashMap<>();
31 protected Map<String, Object> objectsPassedToProcessAsync = new HashMap<>();
33 private Status tempStatus = null;
35 protected void setCompletableFuture(CompletableFuture<Void> cf)
39 cfMap.put(getId(), cf);
42 protected CompletableFuture<Void> getCompletableFuture()
44 if (cf == null && getId() != null && cfMap.get(getId()) != null)
45 cf = cfMap.get(getId());
49 protected void setId(String id)
54 protected void setIdExtension(String idExtension)
56 setId(getPath() + "::" + idExtension);
59 protected String getId()
65 * Override the three methods
66 * initialise (get parameters, set id (extension), set cf if using an existing one)
67 * process (what to do in the cf if not using an existing one)
68 * finalise (extra stuff to do at the end of the first call to this)
70 protected void initialise(HttpServletRequest request,
71 HttpServletResponse response)
73 // should be overridden
74 // MUST setId(request, extension)
75 this.setId(request, null);
77 this.saveParameters(request);
80 protected void saveParameters(HttpServletRequest request)
83 this.getFromId(request);
84 this.getEndpointPathParameters(request);
85 this.getQueryParameters(request);
86 this.getOptions(request);
87 this.getRequestUrl(request);
90 protected abstract void processAsync(HttpServletRequest request,
91 HttpServletResponse response, final Map<String, Object> finalMap);
93 protected void finalise(HttpServletRequest request,
94 HttpServletResponse response)
100 public void processEndpoint(HttpServletRequest request,
101 HttpServletResponse response)
105 initialise(request, response);
107 if (checkStatus(request, response, Status.STARTED))
109 String alreadyFinishedString = null;
110 if (getStatus() == Status.FINISHED)
112 alreadyFinishedString = finishedResponseString(request, response);
114 returnStatus(request, response, alreadyFinishedString);
118 if (getCompletableFuture() == null)
120 final Map<String, Object> finalObjectMap = objectsPassedToProcessAsync;
121 setCompletableFuture(CompletableFuture.runAsync(() -> {
125 this.processAsync(request, response, finalObjectMap);
126 } catch (ClassCastException e)
128 Console.info("Something went wrong with async endpoint execution"
133 addWhenCompleteCompletableFuture();
136 finalise(request, response);
138 returnStatus(response);
139 changeStatus(Status.IN_PROGRESS);
142 protected void atEnd()
146 protected String finishedResponseString(HttpServletRequest request,
147 HttpServletResponse response)
153 * Shared methods below here
156 protected String setId(HttpServletRequest request, String extension)
158 String idString = getId(request);
159 Console.debug("GOT ID '" + idString + "'");
160 if (idString == null)
162 setIdExtension(extension);
172 protected void changeStatus(Status status)
175 // don't change a job's status if it has finished or died
176 if (getStatus() == Status.FINISHED || getStatus() == Status.ERROR)
179 if (status != Status.NOT_RUN)
180 API.getStatusMap().put(id, status);
183 protected Status getStatus()
185 Status status = API.getStatusMap().get(getId());
186 return status == null ? tempStatus : status;
189 protected void returnStatus(HttpServletResponse response)
191 returnStatus(null, response, null);
194 protected void returnStatus(HttpServletRequest request,
195 HttpServletResponse response, String message)
200 PrintWriter writer = response.getWriter();
203 writer.write("id=" + id + "\n");
205 if (API.getRequestMap().get(id) != null)
208 "request=" + API.getRequestMap().get(id).toString() + "\n");
210 if (getStatus() != null)
215 if (getRequestUrl(request) != null)
217 response.sendRedirect(getRequestUrl(request));
218 Console.debug(getStatus() + ": redirecting to '"
219 + getRequestUrl(request) + "'");
223 } catch (InterruptedException e)
229 response.setStatus(HttpServletResponse.SC_ACCEPTED);
233 if (getRequestUrl(request) != null)
235 response.sendRedirect(getRequestUrl(request));
236 Console.debug(getStatus() + ": redirecting to '"
237 + getRequestUrl(request) + "'");
241 } catch (InterruptedException e)
247 response.setStatus(HttpServletResponse.SC_ACCEPTED);
251 response.setStatus(HttpServletResponse.SC_CREATED);
254 response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
258 writer.write("status=" + getStatus().toString() + "\n");
262 writer.write(message);
264 } catch (IOException e)
266 Console.debug("Exception writing to REST response", e);
270 protected boolean checkStatus(HttpServletRequest request,
271 HttpServletResponse response)
273 return checkStatus(request, response, null);
276 protected boolean checkStatus(HttpServletRequest request,
277 HttpServletResponse response, Status set)
280 Status status = getStatus();
285 API.getRequestMap().put(id, request.getRequestURI());
294 protected void addWhenCompleteCompletableFuture()
297 cf.whenComplete((Void, e) -> {
300 Console.error("Endpoint job " + id + " did not complete", e);
301 changeStatus(Status.ERROR);
305 Console.info("Endpoint job " + id + " completed successfully");
306 changeStatus(Status.FINISHED);
313 protected void returnError(HttpServletRequest request,
314 HttpServletResponse response, String message)
316 changeStatus(Status.NOT_RUN);
317 super.returnError(request, response, message);
321 protected boolean deleteFromCache()