JAL-3851 re-engineering endpoints as separate classes
[jalview.git] / src / jalview / rest / EndpointAsync.java
1 package jalview.rest;
2
3 import java.io.IOException;
4 import java.io.PrintWriter;
5 import java.util.concurrent.CompletableFuture;
6
7 import javax.servlet.http.HttpServletRequest;
8 import javax.servlet.http.HttpServletResponse;
9
10 import jalview.bin.Cache;
11 import jalview.rest.RestHandler.Status;
12
13 public abstract class EndpointAsync extends Endpoint
14 {
15   private String name = null;
16
17   private String idExtension = null;
18
19   private String id = null;
20
21   private CompletableFuture<Void> cf = null;
22
23   protected void setCompletableFuture(CompletableFuture<Void> cf)
24   {
25     this.cf = cf;
26   }
27
28   protected CompletableFuture<Void> getCompletableFuture()
29   {
30     return this.cf;
31   }
32
33   protected void setId(String idExtension)
34   {
35     this.id = getName() + "::" + idExtension;
36   }
37
38   protected String getId()
39   {
40     return this.id;
41   }
42
43   /*
44    * Override the three methods
45    * initialise (get parameters, set id (extension), set cf if using an existing one)
46    * process (what to do in the cf if not using an existing one)
47    * finalise (extra stuff to do at the end of the first call to this)
48    */
49   protected void initialise(HttpServletRequest request,
50           HttpServletResponse response)
51   {
52     // should be overridden
53     // must setId(request, extension)
54     setId(request, null);
55   }
56
57   protected abstract void process(HttpServletRequest request,
58           HttpServletResponse response);
59
60   protected void finalise(HttpServletRequest request,
61           HttpServletResponse response)
62   {
63     // can be Overridden
64   }
65
66   protected void passCompletableFuture()
67   {
68     // Override this if you want to use an existing CompletableFuture
69   }
70
71   @Override
72   public void processEndpoint(HttpServletRequest request,
73           HttpServletResponse response)
74   {
75     initialise(request, response);
76     final String id = getId();
77
78     if (checkStatus(request, response))
79       return;
80
81     passCompletableFuture();
82     if (getCompletableFuture() == null)
83     {
84       setCompletableFuture(CompletableFuture.runAsync(() -> {
85         this.process(request, response);
86       }));
87     }
88     finaliseCompletableFuture();
89
90     finalise(request, response);
91
92     returnStatus(response);
93     changeStatus(Status.IN_PROGRESS);
94   }
95
96   /*
97    * Shared methods below here
98    */
99
100   protected String setId(HttpServletRequest request, String extension)
101   {
102     String idString = request.getParameter("id");
103     if (idString == null)
104     {
105       setId(extension);
106       idString = getId();
107     }
108     return idString;
109   }
110
111   protected void changeStatus(Status status)
112   {
113     String id = getId();
114     // don't change a job's status if it has finished or died
115     if (API.getStatusMap().get(id) == Status.FINISHED
116             || API.getStatusMap().get(id) == Status.ERROR)
117       return;
118     API.getStatusMap().put(id, status);
119   }
120
121   protected Status getStatus()
122   {
123     return API.getStatusMap().get(getId());
124   }
125
126   protected void returnStatus(HttpServletResponse response)
127   {
128     String id = getId();
129     try
130     {
131       PrintWriter writer = response.getWriter();
132       if (id != null)
133         writer.write("id=" + id + "\n");
134       if (API.getRequestMap().get(id) != null)
135         writer.write(
136                 "request=" + API.getRequestMap().get(id).toString() + "\n");
137       if (API.getStatusMap().get(id) != null)
138       {
139         if (API.getStatusMap().get(id) == Status.ERROR)
140           response.setStatus(500);
141         writer.write(
142                 "status=" + API.getStatusMap().get(id).toString() + "\n");
143       }
144     } catch (IOException e)
145     {
146       Cache.debug(e);
147     }
148   }
149
150   protected boolean checkStatus(HttpServletRequest request,
151           HttpServletResponse response)
152   {
153     String id = getId();
154     Status status = API.getStatusMap().get(id);
155     if (status == null)
156     {
157       API.getStatusMap().put(id, Status.STARTED);
158       API.getRequestMap().put(id, request.getRequestURI());
159     }
160     else
161     {
162       returnStatus(response);
163       return true;
164     }
165     return false;
166   }
167
168   protected void finaliseCompletableFuture()
169   {
170     String id = getId();
171     cf.whenComplete((Void, e) -> {
172       if (e != null)
173       {
174         Cache.error("Endpoint job " + id + " did not complete");
175         Cache.debug(e);
176         changeStatus(Status.ERROR);
177       }
178       else
179       {
180         Cache.info("Endpoint job " + id + " completed successfully");
181         changeStatus(Status.FINISHED);
182       }
183     });
184   }
185 }