02b13ef6f3f8a85d05ee18a2528bb2d62a7f195d
[jalview.git] / src / jalview / ext / ensembl / EnsemblRestClient.java
1 package jalview.ext.ensembl;
2
3 import jalview.io.FileParse;
4
5 import java.io.BufferedReader;
6 import java.io.DataOutputStream;
7 import java.io.IOException;
8 import java.io.InputStream;
9 import java.io.InputStreamReader;
10 import java.net.HttpURLConnection;
11 import java.net.MalformedURLException;
12 import java.net.URL;
13 import java.util.List;
14
15 import javax.ws.rs.HttpMethod;
16
17 /**
18  * Base class for Ensembl REST service clients
19  * 
20  * @author gmcarstairs
21  */
22 abstract class EnsemblRestClient extends EnsemblSequenceFetcher
23 {
24   protected final static String ENSEMBL_REST = "http://rest.ensembl.org";
25
26   protected static final String SEQUENCE_ID_URL = ENSEMBL_REST
27           + "/sequence/id";
28
29   // @see https://github.com/Ensembl/ensembl-rest/wiki/Output-formats
30   private static final String PING_URL = "http://rest.ensembl.org/info/ping.json";
31
32   private final static long RETEST_INTERVAL = 10000L; // 10 seconds
33
34   private static boolean ensemblRestAvailable = false;
35
36   private static long lastCheck = -1;
37
38   protected volatile boolean inProgress = false;
39
40   @Override
41   public boolean queryInProgress()
42   {
43     return inProgress;
44   }
45
46   @Override
47   public StringBuffer getRawRecords()
48   {
49     return null;
50   }
51
52   /**
53    * Returns the URL for the client http request
54    * 
55    * @param ids
56    * @return
57    * @throws MalformedURLException
58    */
59   protected abstract URL getUrl(List<String> ids)
60           throws MalformedURLException;
61
62   /**
63    * Returns true if client uses GET method, false if it uses POST
64    * 
65    * @return
66    */
67   protected abstract boolean useGetRequest();
68
69   /**
70    * Return the desired value for the Content-Type request header
71    * 
72    * @return
73    * @see https://github.com/Ensembl/ensembl-rest/wiki/HTTP-Headers
74    */
75   protected abstract String getRequestMimeType();
76
77   /**
78    * Return the desired value for the Accept request header
79    * 
80    * @return
81    * @see https://github.com/Ensembl/ensembl-rest/wiki/HTTP-Headers
82    */
83   protected abstract String getResponseMimeType();
84
85   /**
86    * Tries to connect to Ensembl's REST 'ping' endpoint, and returns true if
87    * successful, else false
88    * 
89    * @return
90    */
91   private boolean checkEnsembl()
92   {
93     try
94     {
95       URL ping = new URL(PING_URL);
96       HttpURLConnection conn = (HttpURLConnection) ping.openConnection();
97       int rc = conn.getResponseCode();
98       conn.disconnect();
99       if (rc >= 200 && rc < 300)
100       {
101         return true;
102       }
103     } catch (Throwable t)
104     {
105       System.err.println("Error connecting to " + PING_URL + ": "
106               + t.getMessage());
107     }
108     return false;
109   }
110
111   /**
112    * returns a reader to a Fasta response from the Ensembl sequence endpoint
113    * 
114    * @param ids
115    * @return
116    * @throws IOException
117    */
118   protected FileParse getSequenceReader(List<String> ids)
119           throws IOException
120   {
121     URL url = getUrl(ids);
122   
123     HttpURLConnection connection = (HttpURLConnection) url.openConnection();
124   
125     /*
126      * POST method allows multiple queries in one request; it is supported for
127      * sequence queries, but not for overlap
128      */
129     connection.setRequestMethod(useGetRequest() ? HttpMethod.GET
130             : HttpMethod.POST);
131     connection.setRequestProperty("Content-Type", getRequestMimeType());
132     connection.setRequestProperty("Accept", getResponseMimeType());
133
134     connection.setUseCaches(false);
135     connection.setDoInput(true);
136     connection.setDoOutput(true);
137
138     if (!useGetRequest())
139     {
140       writePostBody(connection, ids);
141     }
142   
143     InputStream response = connection.getInputStream();
144     int responseCode = connection.getResponseCode();
145   
146     if (responseCode != 200)
147     {
148       throw new RuntimeException(
149               "Response code was not 200. Detected response was "
150                       + responseCode);
151     }
152   
153     BufferedReader reader = null;
154     reader = new BufferedReader(new InputStreamReader(response, "UTF-8"));
155     FileParse fp = new FileParse(reader, url.toString(), "HTTP_POST");
156     return fp;
157   }
158
159   /**
160    * Rechecks if Ensembl is responding, unless the last check was successful and
161    * the retest interval has not yet elapsed. Returns true if Ensembl is up,
162    * else false.
163    * 
164    * @return
165    */
166   protected boolean isEnsemblAvailable()
167   {
168     long now = System.currentTimeMillis();
169     boolean retest = now - lastCheck > RETEST_INTERVAL;
170     if (ensemblRestAvailable && !retest)
171     {
172       return true;
173     }
174     ensemblRestAvailable = checkEnsembl();
175     lastCheck = now;
176     return ensemblRestAvailable;
177   }
178
179   /**
180    * Constructs, writes and flushes the POST body of the request, containing the
181    * query ids in JSON format
182    * 
183    * @param connection
184    * @param ids
185    * @throws IOException
186    */
187   protected void writePostBody(HttpURLConnection connection,
188           List<String> ids) throws IOException
189   {
190     boolean first;
191     StringBuilder postBody = new StringBuilder(64);
192     postBody.append("{\"ids\":[");
193     first = true;
194     for (String id : ids)
195     {
196       if (!first)
197       {
198         postBody.append(",");
199       }
200       first = false;
201       postBody.append("\"");
202       postBody.append(id.trim());
203       postBody.append("\"");
204     }
205     postBody.append("]}");
206     byte[] thepostbody = postBody.toString().getBytes();
207     connection.setRequestProperty("Content-Length",
208             Integer.toString(thepostbody.length));
209     DataOutputStream wr = new DataOutputStream(connection.getOutputStream());
210     wr.write(thepostbody);
211     wr.flush();
212     wr.close();
213   }
214
215 }