package jalview.ext.ensembl; import jalview.io.FileParse; import java.io.BufferedReader; import java.io.DataOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.URL; import java.util.List; import javax.ws.rs.HttpMethod; /** * Base class for Ensembl REST service clients * * @author gmcarstairs */ abstract class EnsemblRestClient extends EnsemblSequenceFetcher { protected final static String ENSEMBL_REST = "http://rest.ensembl.org"; protected static final String SEQUENCE_ID_URL = ENSEMBL_REST + "/sequence/id"; // @see https://github.com/Ensembl/ensembl-rest/wiki/Output-formats private static final String PING_URL = "http://rest.ensembl.org/info/ping.json"; private final static long RETEST_INTERVAL = 10000L; // 10 seconds private static boolean ensemblRestAvailable = false; private static long lastCheck = -1; protected volatile boolean inProgress = false; @Override public boolean queryInProgress() { return inProgress; } @Override public StringBuffer getRawRecords() { return null; } /** * Returns the URL for the client http request * * @param ids * @return * @throws MalformedURLException */ protected abstract URL getUrl(List ids) throws MalformedURLException; /** * Returns true if client uses GET method, false if it uses POST * * @return */ protected abstract boolean useGetRequest(); /** * Return the desired value for the Content-Type request header * * @return * @see https://github.com/Ensembl/ensembl-rest/wiki/HTTP-Headers */ protected abstract String getRequestMimeType(); /** * Return the desired value for the Accept request header * * @return * @see https://github.com/Ensembl/ensembl-rest/wiki/HTTP-Headers */ protected abstract String getResponseMimeType(); /** * Tries to connect to Ensembl's REST 'ping' endpoint, and returns true if * successful, else false * * @return */ private boolean checkEnsembl() { try { URL ping = new URL(PING_URL); HttpURLConnection conn = (HttpURLConnection) ping.openConnection(); int rc = conn.getResponseCode(); conn.disconnect(); if (rc >= 200 && rc < 300) { return true; } } catch (Throwable t) { System.err.println("Error connecting to " + PING_URL + ": " + t.getMessage()); } return false; } /** * returns a reader to a Fasta response from the Ensembl sequence endpoint * * @param ids * @return * @throws IOException */ protected FileParse getSequenceReader(List ids) throws IOException { URL url = getUrl(ids); HttpURLConnection connection = (HttpURLConnection) url.openConnection(); /* * POST method allows multiple queries in one request; it is supported for * sequence queries, but not for overlap */ connection.setRequestMethod(useGetRequest() ? HttpMethod.GET : HttpMethod.POST); connection.setRequestProperty("Content-Type", getRequestMimeType()); connection.setRequestProperty("Accept", getResponseMimeType()); connection.setUseCaches(false); connection.setDoInput(true); connection.setDoOutput(true); if (!useGetRequest()) { writePostBody(connection, ids); } InputStream response = connection.getInputStream(); int responseCode = connection.getResponseCode(); if (responseCode != 200) { throw new RuntimeException( "Response code was not 200. Detected response was " + responseCode); } BufferedReader reader = null; reader = new BufferedReader(new InputStreamReader(response, "UTF-8")); FileParse fp = new FileParse(reader, url.toString(), "HTTP_POST"); return fp; } /** * Rechecks if Ensembl is responding, unless the last check was successful and * the retest interval has not yet elapsed. Returns true if Ensembl is up, * else false. * * @return */ protected boolean isEnsemblAvailable() { long now = System.currentTimeMillis(); boolean retest = now - lastCheck > RETEST_INTERVAL; if (ensemblRestAvailable && !retest) { return true; } ensemblRestAvailable = checkEnsembl(); lastCheck = now; return ensemblRestAvailable; } /** * Constructs, writes and flushes the POST body of the request, containing the * query ids in JSON format * * @param connection * @param ids * @throws IOException */ protected void writePostBody(HttpURLConnection connection, List ids) throws IOException { boolean first; StringBuilder postBody = new StringBuilder(64); postBody.append("{\"ids\":["); first = true; for (String id : ids) { if (!first) { postBody.append(","); } first = false; postBody.append("\""); postBody.append(id.trim()); postBody.append("\""); } postBody.append("]}"); byte[] thepostbody = postBody.toString().getBytes(); connection.setRequestProperty("Content-Length", Integer.toString(thepostbody.length)); DataOutputStream wr = new DataOutputStream(connection.getOutputStream()); wr.write(thepostbody); wr.flush(); wr.close(); } }