1 package jalview.ext.ensembl;
3 import jalview.io.FileParse;
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;
13 import java.util.List;
15 import javax.ws.rs.HttpMethod;
17 import com.stevesoft.pat.Regex;
20 * Base class for Ensembl REST service clients
24 abstract class EnsemblRestClient extends EnsemblSequenceFetcher
26 protected final static String ENSEMBL_REST = "http://rest.ensembl.org";
28 protected static final String SEQUENCE_ID_URL = ENSEMBL_REST
31 // @see https://github.com/Ensembl/ensembl-rest/wiki/Output-formats
32 private static final String PING_URL = "http://rest.ensembl.org/info/ping.json";
34 private final static long RETEST_INTERVAL = 10000L; // 10 seconds
36 private static final Regex TRANSCRIPT_REGEX = new Regex(
37 "(ENS)([A-Z]{3}|)T[0-9]{11}$");
39 private static final Regex GENE_REGEX = new Regex(
40 "(ENS)([A-Z]{3}|)G[0-9]{11}$");
42 private static boolean ensemblRestAvailable = false;
44 private static long lastCheck = -1;
46 protected volatile boolean inProgress = false;
48 public static boolean isTranscriptIdentifier(String query)
50 return query == null ? false : TRANSCRIPT_REGEX.search(query);
53 public static boolean isGeneIdentifier(String query)
55 return query == null ? false : GENE_REGEX.search(query);
59 public boolean queryInProgress()
65 public StringBuffer getRawRecords()
71 * Returns the URL for the client http request
75 * @throws MalformedURLException
77 protected abstract URL getUrl(List<String> ids)
78 throws MalformedURLException;
81 * Returns true if client uses GET method, false if it uses POST
85 protected abstract boolean useGetRequest();
88 * Return the desired value for the Content-Type request header
93 * @see https://github.com/Ensembl/ensembl-rest/wiki/HTTP-Headers
95 protected abstract String getRequestMimeType(boolean multipleIds);
98 * Return the desired value for the Accept request header
101 * @see https://github.com/Ensembl/ensembl-rest/wiki/HTTP-Headers
103 protected abstract String getResponseMimeType();
106 * Tries to connect to Ensembl's REST 'ping' endpoint, and returns true if
107 * successful, else false
111 private boolean checkEnsembl()
115 URL ping = new URL(PING_URL);
116 HttpURLConnection conn = (HttpURLConnection) ping.openConnection();
117 int rc = conn.getResponseCode();
119 if (rc >= 200 && rc < 300)
123 } catch (Throwable t)
125 System.err.println("Error connecting to " + PING_URL + ": "
132 * returns a reader to a Fasta response from the Ensembl sequence endpoint
136 * @throws IOException
138 protected FileParse getSequenceReader(List<String> ids)
141 URL url = getUrl(ids);
143 BufferedReader reader = getHttpResponse(url, ids);
144 FileParse fp = new FileParse(reader, url.toString(), "HTTP_POST");
149 * Writes the HTTP request and gets the response as a reader.
153 * written as Json POST body if more than one
155 * @throws IOException
156 * if response code was not 200, or other I/O error
158 protected BufferedReader getHttpResponse(URL url, List<String> ids)
161 // long now = System.currentTimeMillis();
162 HttpURLConnection connection = (HttpURLConnection) url.openConnection();
165 * POST method allows multiple queries in one request; it is supported for
166 * sequence queries, but not for overlap
168 boolean multipleIds = ids.size() > 1;// useGetRequest();
169 connection.setRequestMethod(multipleIds ? HttpMethod.POST
171 connection.setRequestProperty("Content-Type",
172 getRequestMimeType(multipleIds));
173 connection.setRequestProperty("Accept", getResponseMimeType());
175 connection.setUseCaches(false);
176 connection.setDoInput(true);
177 connection.setDoOutput(multipleIds);
181 writePostBody(connection, ids);
184 InputStream response = connection.getInputStream();
185 int responseCode = connection.getResponseCode();
187 if (responseCode != 200)
190 * note: a GET request for an invalid id returns an error code e.g. 415
191 * but POST request returns 200 and an empty Fasta response
193 throw new IOException(
194 "Response code was not 200. Detected response was "
197 // System.out.println(getClass().getName() + " took "
198 // + (System.currentTimeMillis() - now) + "ms to fetch");
200 BufferedReader reader = null;
201 reader = new BufferedReader(new InputStreamReader(response, "UTF-8"));
206 * Rechecks if Ensembl is responding, unless the last check was successful and
207 * the retest interval has not yet elapsed. Returns true if Ensembl is up,
212 protected boolean isEnsemblAvailable()
214 long now = System.currentTimeMillis();
215 boolean retest = now - lastCheck > RETEST_INTERVAL;
216 if (ensemblRestAvailable && !retest)
220 ensemblRestAvailable = checkEnsembl();
222 return ensemblRestAvailable;
226 * Constructs, writes and flushes the POST body of the request, containing the
227 * query ids in JSON format
231 * @throws IOException
233 protected void writePostBody(HttpURLConnection connection,
234 List<String> ids) throws IOException
237 StringBuilder postBody = new StringBuilder(64);
238 postBody.append("{\"ids\":[");
240 for (String id : ids)
244 postBody.append(",");
247 postBody.append("\"");
248 postBody.append(id.trim());
249 postBody.append("\"");
251 postBody.append("]}");
252 byte[] thepostbody = postBody.toString().getBytes();
253 connection.setRequestProperty("Content-Length",
254 Integer.toString(thepostbody.length));
255 DataOutputStream wr = new DataOutputStream(connection.getOutputStream());
256 wr.write(thepostbody);