Merge branch 'develop' into Jalview-JS/develop
authorgmungoc <g.m.carstairs@dundee.ac.uk>
Mon, 15 Apr 2019 08:47:50 +0000 (09:47 +0100)
committergmungoc <g.m.carstairs@dundee.ac.uk>
Mon, 15 Apr 2019 08:47:50 +0000 (09:47 +0100)
1  2 
src/jalview/ext/ensembl/EnsemblRestClient.java

   */
  package jalview.ext.ensembl;
  
 +import jalview.util.Platform;
  import jalview.util.StringUtils;
  
  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.ProtocolException;
@@@ -37,7 -37,9 +37,7 @@@ import java.util.Map
  
  import javax.ws.rs.HttpMethod;
  
 -import org.json.simple.JSONArray;
 -import org.json.simple.JSONObject;
 -import org.json.simple.parser.JSONParser;
 +import org.json.simple.parser.ParseException;
  
  /**
   * Base class for Ensembl REST service clients
   */
  abstract class EnsemblRestClient extends EnsemblSequenceFetcher
  {
 +
 +  static
 +  {
 +    Platform.addJ2SDirectDatabaseCall("http://rest.ensembl");
 +    Platform.addJ2SDirectDatabaseCall("https://rest.ensembl");
 +  }
 +
    private static final int DEFAULT_READ_TIMEOUT = 5 * 60 * 1000; // 5 minutes
  
    private static final int CONNECT_TIMEOUT_MS = 10 * 1000; // 10 seconds
@@@ -69,9 -64,9 +69,9 @@@
     * @see https://github.com/Ensembl/ensembl-rest/wiki/Change-log
     * @see http://rest.ensembl.org/info/rest?content-type=application/json
     */
-   private static final String LATEST_ENSEMBLGENOMES_REST_VERSION = "9.0";
+   private static final String LATEST_ENSEMBLGENOMES_REST_VERSION = "10.0";
  
-   private static final String LATEST_ENSEMBL_REST_VERSION = "9.0";
+   private static final String LATEST_ENSEMBL_REST_VERSION = "10.0";
  
    private static final String REST_CHANGE_LOG = "https://github.com/Ensembl/ensembl-rest/wiki/Change-log";
  
    static
    {
      domainData = new HashMap<>();
 -    domainData.put(DEFAULT_ENSEMBL_BASEURL,
 -            new EnsemblData(DEFAULT_ENSEMBL_BASEURL, LATEST_ENSEMBL_REST_VERSION));
 -    domainData.put(DEFAULT_ENSEMBL_GENOMES_BASEURL, new EnsemblData(
 -            DEFAULT_ENSEMBL_GENOMES_BASEURL, LATEST_ENSEMBLGENOMES_REST_VERSION));
 +    domainData.put(DEFAULT_ENSEMBL_BASEURL, new EnsemblData(
 +            DEFAULT_ENSEMBL_BASEURL, LATEST_ENSEMBL_REST_VERSION));
 +    domainData.put(DEFAULT_ENSEMBL_GENOMES_BASEURL,
 +            new EnsemblData(DEFAULT_ENSEMBL_GENOMES_BASEURL,
 +                    LATEST_ENSEMBLGENOMES_REST_VERSION));
    }
  
    protected volatile boolean inProgress = false;
     * @see http://rest.ensembl.org/documentation/info/ping
     * @return
     */
 +  @SuppressWarnings("unchecked")
    boolean checkEnsembl()
    {
      BufferedReader br = null;
      {
        // note this format works for both ensembl and ensemblgenomes
        // info/ping.json works for ensembl only (March 2016)
 -      URL ping = new URL(pingUrl);
  
        /*
         * expect {"ping":1} if ok
         * if ping takes more than 2 seconds to respond, treat as if unavailable
         */
 -      br = getHttpResponse(ping, null, 2 * 1000);
 -      if (br == null)
 +      Map<String, Object> val = (Map<String, Object>) getJSON(
 +              new URL(pingUrl), null, 2 * 1000, MODE_MAP, null);
 +      if (val == null)
        {
 -        // error reponse status
          return false;
        }
 -      JSONParser jp = new JSONParser();
 -      JSONObject val = (JSONObject) jp.parse(br);
        String pingString = val.get("ping").toString();
        return pingString != null;
      } catch (Throwable t)
      return false;
    }
  
 -  /**
 -   * Returns a reader to a (Json) response from the Ensembl sequence endpoint.
 -   * If the request failed the return value may be null.
 -   * 
 -   * @param ids
 -   * @return
 -   * @throws IOException
 -   */
 -  protected BufferedReader getSequenceReader(List<String> ids)
 -          throws IOException
 -  {
 -    URL url = getUrl(ids);
 -
 -    BufferedReader reader = getHttpResponse(url, ids);
 -    return reader;
 -  }
 -
 -  /**
 -   * Gets a reader to the HTTP response, using the default read timeout of 5
 -   * minutes
 -   * 
 -   * @param url
 -   * @param ids
 -   * @return
 -   * @throws IOException
 -   */
 -  protected BufferedReader getHttpResponse(URL url, List<String> ids)
 -          throws IOException
 -  {
 -    return getHttpResponse(url, ids, DEFAULT_READ_TIMEOUT);
 -  }
 +  protected final static int MODE_ARRAY = 0;
 +
 +  protected final static int MODE_MAP = 1;
 +
 +  protected final static int MODE_ITERATOR = 2;
 +
 +  // /**
 +  // * Returns a reader to a (Json) response from the Ensembl sequence endpoint.
 +  // * If the request failed the return value may be null.
 +  // *
 +  // * @param ids
 +  // * @return
 +  // * @throws IOException
 +  // * @throws ParseException
 +  // */
 +  // protected Object getSequenceJSON(List<String> ids, int mode)
 +  // throws IOException, ParseException
 +  // {
 +  // URL url = getUrl(ids);
 +  // return getJSON(url, ids, -1, mode);
 +  // }
 +  //
 +  // /**
 +  // * Gets a reader to the HTTP response, using the default read timeout of 5
 +  // * minutes
 +  // *
 +  // * @param url
 +  // * @param ids
 +  // * @return
 +  // * @throws IOException
 +  // */
 +  // protected BufferedReader getHttpResponse(URL url, List<String> ids)
 +  // throws IOException
 +  // {
 +  // return getHttpResponse(url, ids, DEFAULT_READ_TIMEOUT);
 +  // }
  
    /**
     * Sends the HTTP request and gets the response as a reader. Returns null if
     *          in milliseconds
     * @return
     * @throws IOException
 +   * @throws ParseException
     */
 -  protected BufferedReader getHttpResponse(URL url, List<String> ids,
 -          int readTimeout) throws IOException
 +  private Object getJSON(URL url, List<String> ids, int readTimeout)
 +          throws IOException, ParseException
    {
 +
 +    if (readTimeout < 0)
 +    {
 +      readTimeout = DEFAULT_READ_TIMEOUT;
 +    }
      int retriesLeft = MAX_RETRIES;
      HttpURLConnection connection = null;
      int responseCode = 0;
  
 +    Platform.setAjaxJSON(url);
 +
      while (retriesLeft > 0)
      {
        connection = tryConnection(url, ids, readTimeout);
         * note: a GET request for an invalid id returns an error code e.g. 415
         * but POST request returns 200 and an empty Fasta response 
         */
 -      System.err.println("Response code " + responseCode + " for " + url);
 +      System.err.println("Response code " + responseCode);// + " for " + url);
        return null;
      }
  
      InputStream response = connection.getInputStream();
  
 -    // System.out.println(getClass().getName() + " took "
 -    // + (System.currentTimeMillis() - now) + "ms to fetch");
 +    Platform.timeCheck(null, Platform.TIME_MARK);
 +    Object ret = Platform.parseJSON(response);
 +    Platform.timeCheck("EnsemblRestClient.getJSON " + url,
 +            Platform.TIME_MARK);
  
 -    BufferedReader reader = null;
 -    reader = new BufferedReader(new InputStreamReader(response, "UTF-8"));
 -    return reader;
 +    return ret;
    }
  
    /**
            int readTimeout) throws IOException, ProtocolException
    {
      // System.out.println(System.currentTimeMillis() + " " + url);
 +
      HttpURLConnection connection = (HttpURLConnection) url.openConnection();
  
      /*
      connection.setRequestProperty("Content-Type", getRequestMimeType());
      connection.setRequestProperty("Accept", getResponseMimeType());
  
      connection.setDoInput(true);
      connection.setDoOutput(multipleIds);
  
 +    connection.setUseCaches(false);
      connection.setConnectTimeout(CONNECT_TIMEOUT_MS);
      connection.setReadTimeout(readTimeout);
  
          int retrySecs = Integer.valueOf(retryDelay);
          if (retrySecs > 0 && retrySecs < 10)
          {
 -          System.err
 -                  .println("Ensembl REST service rate limit exceeded, waiting "
 +          System.err.println(
 +                  "Ensembl REST service rate limit exceeded, waiting "
                            + retryDelay + " seconds before retrying");
            Thread.sleep(1000 * retrySecs);
          }
      StringBuilder postBody = new StringBuilder(64);
      postBody.append("{\"ids\":[");
      first = true;
 -    for (String id : ids)
 +    for (int i = 0, n = ids.size(); i < n; i++)
      {
 +      String id = ids.get(i);
        if (!first)
        {
          postBody.append(",");
    }
  
    /**
 +   * Primary access point to parsed JSON data, including the call to retrieve
 +   * and parsing.
 +   * 
 +   * @param url
 +   *          request url; if null, getUrl(ids) will be used
 +   * @param ids
 +   *          optional; may be null
 +   * @param msDelay
 +   *          -1 for default delay
 +   * @param mode
 +   *          map, array, or array iterator
 +   * @param mapKey
 +   *          an optional key for an outer map
 +   * @return a Map, List, Iterator, or null
 +   * @throws IOException
 +   * @throws ParseException
 +   * 
 +   * @author Bob Hanson 2019
 +   */
 +  @SuppressWarnings("unchecked")
 +  protected Object getJSON(URL url, List<String> ids, int msDelay, int mode,
 +          String mapKey) throws IOException, ParseException
 +  {
 +    if (url == null)
 +    {
 +      url = getUrl(ids);
 +    }
 +
 +    Object json = (url == null ? null : getJSON(url, ids, msDelay));
 +
 +    if (json != null && mapKey != null)
 +    {
 +      json = ((Map<String, Object>) json).get(mapKey);
 +    }
 +    if (json == null)
 +    {
 +      return null;
 +    }
 +    switch (mode)
 +    {
 +    case MODE_ARRAY:
 +    case MODE_MAP:
 +      break;
 +    case MODE_ITERATOR:
 +      json = ((List<Object>) json).iterator();
 +      break;
 +    }
 +    return json;
 +  }
 +
 +  /**
     * Fetches and checks Ensembl's REST version number
     * 
     * @return
     */
 +  @SuppressWarnings("unchecked")
    private void checkEnsemblRestVersion()
    {
      EnsemblData info = domainData.get(getDomain());
  
 -    JSONParser jp = new JSONParser();
 -    URL url = null;
      try
      {
 -      url = new URL(getDomain() + "/info/rest" + CONTENT_TYPE_JSON);
 -      BufferedReader br = getHttpResponse(url, null);
 -      if (br == null)
 +      Map<String, Object> val = (Map<String, Object>) getJSON(
 +              new URL(getDomain() + "/info/rest" + CONTENT_TYPE_JSON), null,
 +              -1, MODE_MAP, null);
 +      if (val == null)
        {
          return;
        }
 -      JSONObject val = (JSONObject) jp.parse(br);
        String version = val.get("release").toString();
        String majorVersion = version.substring(0, version.indexOf("."));
        String expected = info.expectedRestVersion;
     * 
     * @return
     */
 +  @SuppressWarnings("unchecked")
    private void checkEnsemblDataVersion()
    {
 -    JSONParser jp = new JSONParser();
 -    URL url = null;
 -    BufferedReader br = null;
 -
 +    Map<String, Object> val;
      try
      {
 -      url = new URL(getDomain() + "/info/data" + CONTENT_TYPE_JSON);
 -      br = getHttpResponse(url, null);
 -      if (br != null)
 +      val = (Map<String, Object>) getJSON(
 +              new URL(getDomain() + "/info/data" + CONTENT_TYPE_JSON), null,
 +              -1, MODE_MAP, null);
 +      if (val == null)
        {
 -        JSONObject val = (JSONObject) jp.parse(br);
 -        JSONArray versions = (JSONArray) val.get("releases");
 -        domainData.get(getDomain()).dataVersion = versions.get(0)
 -                .toString();
 +        return;
        }
 -    } catch (Throwable t)
 -    {
 +      List<Object> versions = (List<Object>) val.get("releases");
 +      domainData.get(getDomain()).dataVersion = versions.get(0).toString();
 +    } catch (Throwable e)
 +    {// could be IOException | ParseException e) {
        System.err.println(
 -              "Error checking Ensembl data version: " + t.getMessage());
 -    } finally
 -    {
 -      if (br != null)
 -      {
 -        try
 -        {
 -          br.close();
 -        } catch (IOException e)
 -        {
 -          // ignore
 -        }
 -      }
 +              "Error checking Ensembl data version: " + e.getMessage());
      }
    }