JAL-3851 some changes. HighlightSequenceEndpoint and SelectSequenceEndpoint
[jalview.git] / src / jalview / rest / AbstractEndpointAsync.java
1 package jalview.rest;
2
3 import java.io.IOException;
4 import java.io.PrintWriter;
5 import java.util.HashMap;
6 import java.util.Map;
7 import java.util.concurrent.CompletableFuture;
8
9 import javax.servlet.http.HttpServletRequest;
10 import javax.servlet.http.HttpServletResponse;
11
12 import jalview.bin.Cache;
13 import jalview.rest.RestHandler.Status;
14
15 public abstract class AbstractEndpointAsync extends AbstractEndpoint
16 {
17   public AbstractEndpointAsync(API api, String path, String name,
18           String parameters, String description)
19   {
20     super(api, path, name, parameters, description);
21   }
22
23   protected String idExtension = null;
24
25   protected String id = null;
26
27   protected CompletableFuture<Void> cf = null;
28
29   protected Map<String, CompletableFuture<Void>> cfMap = new HashMap<>();
30
31   protected Map<String, String> stringsPassedToProcessAsync = new HashMap<>();
32
33   protected void setCompletableFuture(CompletableFuture<Void> cf)
34   {
35     this.cf = cf;
36     if (getId() != null)
37       cfMap.put(getId(), cf);
38   }
39
40   protected CompletableFuture<Void> getCompletableFuture()
41   {
42     if (cf == null && getId() != null && cfMap.get(getId()) != null)
43       cf = cfMap.get(getId());
44     return this.cf;
45   }
46
47   protected void setId(String id)
48   {
49     this.id = id;
50   }
51
52   protected void setIdExtension(String idExtension)
53   {
54     setId(getPath() + "::" + idExtension);
55   }
56
57   protected String getId()
58   {
59     return this.id;
60   }
61
62   /*
63    * Override the three methods
64    * initialise (get parameters, set id (extension), set cf if using an existing one)
65    * process (what to do in the cf if not using an existing one)
66    * finalise (extra stuff to do at the end of the first call to this)
67    */
68   protected void initialise(HttpServletRequest request,
69           HttpServletResponse response)
70   {
71     // should be overridden
72     // must setId(request, extension)
73     setId(request, null);
74   }
75
76   protected abstract void processAsync(HttpServletRequest request,
77           HttpServletResponse response, final Map<String, String> finalMap);
78
79   protected void finalise(HttpServletRequest request,
80           HttpServletResponse response)
81   {
82     // can be Overridden
83   }
84
85   @Override
86   public void processEndpoint(HttpServletRequest request,
87           HttpServletResponse response)
88   {
89     // subclass method
90     initialise(request, response);
91
92     if (checkStatus(request, response, Status.STARTED))
93     {
94       String finishedString = null;
95       if (getStatus() == Status.FINISHED)
96       {
97         finishedString = finished(request, response);
98       }
99       returnStatus(request, response, finishedString);
100       return;
101     }
102
103     if (getCompletableFuture() == null)
104     {
105       final Map<String, String> finalStringMap = stringsPassedToProcessAsync;
106       setCompletableFuture(CompletableFuture.runAsync(() -> {
107         // subclass method
108         this.processAsync(request, response, finalStringMap);
109       }));
110     }
111     addWhenCompleteCompletableFuture();
112
113     // subclass method
114     finalise(request, response);
115
116     returnStatus(response);
117     changeStatus(Status.IN_PROGRESS);
118   }
119
120   protected void atEnd()
121   {
122   }
123
124   protected String finished(HttpServletRequest request,
125           HttpServletResponse response)
126   {
127     return null;
128   }
129
130   /*
131    * Shared methods below here
132    */
133
134   protected String setId(HttpServletRequest request, String extension)
135   {
136     String idString = request.getParameter("id");
137     if (idString == null)
138     {
139       setIdExtension(extension);
140     }
141     else
142     {
143       setId(idString);
144     }
145     return getId();
146   }
147
148   protected void changeStatus(Status status)
149   {
150     String id = getId();
151     // don't change a job's status if it has finished or died
152     if (getStatus() == Status.FINISHED || getStatus() == Status.ERROR)
153       return;
154     API.getStatusMap().put(id, status);
155   }
156
157   protected Status getStatus()
158   {
159     getAPI();
160     return API.getStatusMap().get(getId());
161   }
162
163   protected void returnStatus(HttpServletResponse response)
164   {
165     returnStatus(null, response, null);
166   }
167
168   protected void returnStatus(HttpServletRequest request,
169           HttpServletResponse response, String message)
170   {
171     String id = getId();
172     try
173     {
174       PrintWriter writer = response.getWriter();
175       if (id != null)
176       {
177         writer.write("id=" + id + "\n");
178       }
179       getAPI();
180       if (API.getRequestMap().get(id) != null)
181       {
182         getAPI();
183         writer.write(
184                 "request=" + API.getRequestMap().get(id).toString() + "\n");
185       }
186       if (getStatus() != null)
187       {
188         if (getStatus() == Status.ERROR)
189         {
190           response.setStatus(500);
191         }
192         writer.write("status=" + getStatus().toString() + "\n");
193       }
194       if (message != null)
195       {
196         writer.write(message);
197       }
198     } catch (IOException e)
199     {
200       Cache.debug(e);
201     }
202   }
203
204   protected boolean checkStatus(HttpServletRequest request,
205           HttpServletResponse response)
206   {
207     return checkStatus(request, response, null);
208   }
209
210   protected boolean checkStatus(HttpServletRequest request,
211           HttpServletResponse response, Status set)
212   {
213     String id = getId();
214     Status status = getStatus();
215     if (status == null)
216     {
217       if (set != null)
218         changeStatus(set);
219       API.getRequestMap().put(id, request.getRequestURI());
220       return false;
221     }
222     else
223     {
224       return true;
225     }
226   }
227
228   protected void addWhenCompleteCompletableFuture()
229   {
230     String id = getId();
231     cf.whenComplete((Void, e) -> {
232       if (e != null)
233       {
234         Cache.error("Endpoint job " + id + " did not complete");
235         Cache.debug(e);
236         changeStatus(Status.ERROR);
237       }
238       else
239       {
240         Cache.info("Endpoint job " + id + " completed successfully");
241         changeStatus(Status.FINISHED);
242         atEnd();
243       }
244     });
245   }
246 }