JAL-3851 fix caching and tidy up old methods
[jalview.git] / src / jalview / rest / RestHandler.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3  * Copyright (C) $$Year-Rel$$ The Jalview Authors
4  * 
5  * This file is part of Jalview.
6  * 
7  * Jalview is free software: you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License 
9  * as published by the Free Software Foundation, either version 3
10  * of the License, or (at your option) any later version.
11  *  
12  * Jalview is distributed in the hope that it will be useful, but 
13  * WITHOUT ANY WARRANTY; without even the implied warranty 
14  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
15  * PURPOSE.  See the GNU General Public License for more details.
16  * 
17  * You should have received a copy of the GNU General Public License
18  * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
19  * The Jalview Authors are detailed in the 'AUTHORS' file.
20  */
21 package jalview.rest;
22
23 import java.io.BufferedReader;
24 import java.io.IOException;
25 import java.io.PrintWriter;
26 import java.net.BindException;
27 import java.util.HashMap;
28 import java.util.Map;
29
30 import javax.servlet.http.HttpServletRequest;
31 import javax.servlet.http.HttpServletResponse;
32
33 import jalview.bin.Cache;
34 import jalview.httpserver.AbstractRequestHandler;
35
36 /**
37  * A simple handler to process (or delegate) HTTP requests on /jalview/rest
38  */
39 public class RestHandler extends AbstractRequestHandler
40 {
41
42   public enum Status
43   {
44     STARTED, IN_PROGRESS, FINISHED, ERROR
45   }
46
47   public interface EndpointI
48   {
49     public String getName();
50
51     public void processEndpoint(HttpServletRequest request,
52             HttpServletResponse response);
53
54   }
55
56   public interface EndpointOld
57   {
58     public void processEndpoint(String endpointName,
59             HttpServletRequest request, HttpServletResponse response);
60   }
61
62   private static final String MY_PATH = "rest";
63
64   private static final String MY_NAME = "Rest";
65
66   private String missingEndpointMessage = null;
67
68   private boolean init = false;
69
70   // map of method names and method handlers
71   private Map<String, EndpointI> endpoints = null;
72
73   protected Map<String, EndpointI> getEndpoints()
74   {
75     return endpoints;
76   }
77
78   /**
79    * Singleton instance of this class
80    */
81   private static RestHandler instance = null;
82
83   /**
84    * Returns the singleton instance of this class
85    * 
86    * @return
87    * @throws BindException
88    */
89   public static RestHandler getInstance() throws BindException
90   {
91     synchronized (RestHandler.class)
92     {
93       if (instance == null)
94       {
95         instance = new RestHandler();
96       }
97     }
98     return instance;
99   }
100
101   /**
102    * Private constructor enforces use of singleton
103    * 
104    * @throws BindException
105    */
106   protected RestHandler() throws BindException
107   {
108     init();
109
110     /*
111      * We don't register the handler here - this is done as a special case in
112      * HttpServer initialisation; to do it here would invite an infinite loop of
113      * RestHandler/HttpServer constructor
114      */
115   }
116
117   /**
118    * Handle a jalview/rest request
119    * 
120    * @throws IOException
121    */
122   @Override
123   protected void processRequest(HttpServletRequest request,
124           HttpServletResponse response) throws IOException
125   {
126     /*
127      * Currently just echoes the request; add helper classes as required to
128      * process requests
129      */
130     System.out.println(request.toString());
131     if (endpoints == null)
132     {
133       final String queryString = request.getQueryString();
134       final String reply = "REST not yet implemented; received "
135               + request.getMethod() + ": " + request.getRequestURL()
136               + (queryString == null ? "" : "?" + queryString);
137       System.out.println(reply);
138
139       response.setHeader("Cache-Control", "no-cache/no-store");
140       response.setHeader("Content-type", "text/plain");
141       final PrintWriter writer = response.getWriter();
142       writer.write(reply);
143       writer.close();
144       return;
145     }
146
147     String endpointName = getRequestedEndpointName(request);
148
149     Cache.info("REMOVEME endpointName=" + endpointName);
150     Cache.info("REMOVEME endpoints[" + endpointName + "]="
151             + endpoints.get(endpointName));
152     if (!endpoints.containsKey(endpointName)
153             || endpoints.get(endpointName) == null)
154     {
155
156       response.setHeader("Cache-Control", "no-cache/no-store");
157       response.setHeader("Content-type", "text/plain");
158       response.setStatus(400);
159       PrintWriter writer = response.getWriter();
160       writer.write(missingEndpointMessage == null
161               ? "REST endpoint '" + endpointName + "' not defined"
162               : missingEndpointMessage);
163       writer.close();
164       return;
165     }
166
167     response.setHeader("Cache-Control", "no-cache/no-store");
168     response.setHeader("Content-type", "text/plain");
169     EndpointI ep = endpoints.get(endpointName);
170     ep.processEndpoint(request, response);
171
172     return;
173   }
174
175   /**
176    * Returns a display name for this service
177    */
178   @Override
179   public String getName()
180   {
181     return MY_NAME;
182   }
183
184   /**
185    * Initialise methods
186    * 
187    * @throws BindException
188    */
189   protected void init() throws BindException
190   {
191     init(MY_PATH);
192   }
193
194   protected void init(String path) throws BindException
195   {
196     setPath(path);
197     // Override this in extended class
198     // e.g. registerHandler and addEndpoints
199   }
200
201   protected boolean addEndpoint(EndpointI ep)
202   {
203     if (endpoints == null)
204     {
205       endpoints = new HashMap<>();
206     }
207     Cache.info("REMOVEME Adding Endpoint ep=" + ep);
208     Cache.info("REMOVEME Adding Endpoint ep.getName()=" + ep.getName());
209     Endpoint e = (Endpoint) ep;
210     Cache.info("REMOVEME Adding Endpoint e.name=" + e.name);
211     endpoints.put(ep.getName(), ep);
212     return true;
213   }
214
215   protected String getRequestedEndpointName(HttpServletRequest request)
216   {
217     String pathInfo = request.getPathInfo();
218     int slashpos = pathInfo.indexOf('/', 1);
219     return slashpos > 1 ? pathInfo.substring(1, slashpos)
220             : pathInfo.substring(1);
221   }
222
223   protected String[] getEndpointPathParameters(HttpServletRequest request)
224   {
225     String pathInfo = request.getPathInfo();
226     int slashpos = pathInfo.indexOf('/', 1);
227     return slashpos < 1 ? null
228             : pathInfo.substring(slashpos + 1).split("/");
229   }
230
231   protected void returnError(HttpServletRequest request,
232           HttpServletResponse response, String message)
233   {
234     response.setStatus(500); // set this to something better
235     String endpointName = getRequestedEndpointName(request);
236     Cache.error(this.MY_NAME + " error: endpoint " + endpointName
237             + " failed: '" + message + "'");
238     try
239     {
240       PrintWriter writer = response.getWriter();
241       writer.write("Endpoint " + endpointName + ": " + message);
242       writer.close();
243     } catch (IOException e)
244     {
245       Cache.debug(e);
246     }
247   }
248
249   protected void returnStatus(HttpServletResponse response, String id,
250           Status status)
251   {
252     try
253     {
254       PrintWriter writer = response.getWriter();
255       if (id != null)
256         writer.write("id=" + id + "\n");
257       if (status != null)
258         writer.write("status=" + status.toString() + "\n");
259     } catch (IOException e)
260     {
261       Cache.debug(e);
262     }
263   }
264
265   protected String getRequestBody(HttpServletRequest request)
266           throws IOException
267   {
268     StringBuilder sb = new StringBuilder();
269     BufferedReader reader = request.getReader();
270     try
271     {
272       String line;
273       while ((line = reader.readLine()) != null)
274       {
275         sb.append(line).append('\n');
276       }
277     } finally
278     {
279       reader.close();
280     }
281     return sb.toString();
282   }
283
284 }