07a9df4b5cff7ecd6e8866fafd2b38b393ab863f
[jalview.git] / src / jalview / ws / ebi / EBIFetchClient.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.ws.ebi;
22
23 import jalview.datamodel.DBRefSource;
24 import jalview.util.MessageManager;
25
26 import java.io.BufferedInputStream;
27 import java.io.BufferedReader;
28 import java.io.File;
29 import java.io.FileOutputStream;
30 import java.io.InputStream;
31 import java.io.InputStreamReader;
32 import java.net.HttpURLConnection;
33 import java.net.URL;
34 import java.util.ArrayList;
35 import java.util.List;
36 import java.util.StringTokenizer;
37
38 /**
39  * DOCUMENT ME!
40  * 
41  * @author $author$
42  * @version $Revision$
43  */
44 public class EBIFetchClient
45 {
46
47   /**
48    * Creates a new EBIFetchClient object.
49    */
50   public EBIFetchClient()
51   {
52   }
53
54   /**
55    * DOCUMENT ME!
56    * 
57    * @return DOCUMENT ME!
58    */
59   public String[] getSupportedDBs()
60   {
61     // TODO - implement rest call for dbfetch getSupportedDBs
62     throw new Error(MessageManager.getString("error.not_yet_implemented"));
63   }
64
65   /**
66    * DOCUMENT ME!
67    * 
68    * @return DOCUMENT ME!
69    */
70   public String[] getSupportedFormats()
71   {
72     // TODO - implement rest call for dbfetch getSupportedFormats
73     throw new Error(MessageManager.getString("error.not_yet_implemented"));
74   }
75
76   /**
77    * DOCUMENT ME!
78    * 
79    * @return DOCUMENT ME!
80    */
81   public String[] getSupportedStyles()
82   {
83     // TODO - implement rest call for dbfetch getSupportedStyles
84     throw new Error(MessageManager.getString("error.not_yet_implemented"));
85   }
86
87   /**
88    * Send an HTTP fetch request to EBI and save the reply in a temporary file.
89    * 
90    * @param ids
91    *          the query formatted as db:query1;query2;query3
92    * @param format
93    *          the format wanted
94    * @param extension
95    *          for the temporary file to hold response (without separator)
96    * @return the file holding the response
97    * @throws OutOfMemoryError
98    */
99
100   public File fetchDataAsFile(String ids, String format, String ext)
101           throws OutOfMemoryError
102   {
103     File outFile = null;
104     try
105     {
106       outFile = File.createTempFile("jalview", "." + ext);
107       outFile.deleteOnExit();
108       fetchData(ids, format, outFile);
109       if (outFile.length() == 0)
110       {
111         outFile.delete();
112         return null;
113       }
114     } catch (Exception ex)
115     {
116     }
117     return outFile;
118   }
119
120   /**
121    * Fetches queries and either saves the response to a file or returns as
122    * string data
123    * 
124    * @param ids
125    * @param format
126    * @param outFile
127    * @return
128    * @throws OutOfMemoryError
129    */
130   String[] fetchData(String ids, String format, File outFile)
131           throws OutOfMemoryError
132   {
133     StringBuilder querystring = new StringBuilder(ids.length());
134     String database = parseIds(ids, querystring);
135     if (database == null)
136     {
137       System.err.println("Invalid Query string : '" + ids + "'");
138       System.err.println("Should be of form 'dbname:q1;q2;q3;q4'");
139       return null;
140     }
141
142     // note: outFile is currently always specified, so return value is null
143     String[] rslt = fetchBatch(querystring.toString(), database, format,
144             outFile);
145
146     return (rslt != null && rslt.length > 0 ? rslt : null);
147   }
148
149   /**
150    * Parses ids formatted as dbname:q1;q2;q3, returns the dbname and adds
151    * queries as comma-separated items to the querystring. dbname must be
152    * specified for at least one queryId. Returns null if a mixture of different
153    * dbnames is found (ignoring case).
154    * 
155    * @param ids
156    * @param queryString
157    * @return
158    */
159   static String parseIds(String ids, StringBuilder queryString)
160   {
161     String database = null;
162     StringTokenizer queries = new StringTokenizer(ids, ";");
163     boolean appending = queryString.length() > 0;
164     while (queries.hasMoreTokens())
165     {
166       String query = queries.nextToken();
167       int p = query.indexOf(':');
168       if (p > -1)
169       {
170         String db = query.substring(0, p);
171         if (database != null && !db.equalsIgnoreCase(database))
172         {
173           /*
174            * different databases mixed in together - invalid
175            */
176           return null;
177         }
178         database = db;
179         query = query.substring(p + 1);
180       }
181       queryString.append(appending ? "," : "");
182       queryString.append(query);
183       appending = true;
184     }
185     return database;
186   }
187
188   /**
189    * Fetches queries and either saves the response to a file or (if no file
190    * specified) returns as string data
191    * 
192    * @param ids
193    * @param database
194    * @param format
195    * @param outFile
196    * @return
197    * @throws OutOfMemoryError
198    */
199   String[] fetchBatch(String ids, String database, String format,
200           File outFile) throws OutOfMemoryError
201   {
202     // long time = System.currentTimeMillis();
203     String url = buildUrl(ids, database, format);
204
205     try
206     {
207       URL rcall = new URL(url);
208
209       HttpURLConnection conn = (HttpURLConnection) rcall.openConnection();
210       int responseCode = conn.getResponseCode();
211       if (responseCode != 200)
212       {
213         System.err.println("Warning: response code " + responseCode
214                 + " for " + url);
215       }
216       InputStream is = new BufferedInputStream(conn.getInputStream());
217       if (outFile != null)
218       {
219         FileOutputStream fio = new FileOutputStream(outFile);
220         byte[] bb = new byte[32 * 1024];
221         int l;
222         while ((l = is.read(bb)) > 0)
223         {
224           fio.write(bb, 0, l);
225         }
226         fio.close();
227         is.close();
228       }
229       else
230       {
231         BufferedReader br = new BufferedReader(new InputStreamReader(is));
232         String rtn;
233         List<String> arl = new ArrayList<String>();
234         while ((rtn = br.readLine()) != null)
235         {
236           arl.add(rtn);
237         }
238         return arl.toArray(new String[arl.size()]);
239       }
240     } catch (OutOfMemoryError er)
241     {
242       System.out.println("OUT OF MEMORY DOWNLOADING QUERY FROM " + database
243               + ":\n" + ids);
244       throw er;
245     } catch (Exception ex)
246     {
247       if (ex.getMessage().startsWith(
248               "uk.ac.ebi.jdbfetch.exceptions.DbfNoEntryFoundException"))
249       {
250         return null;
251       }
252       System.err.println("Unexpected exception when retrieving from "
253               + database + "\nQuery was : '" + ids + "'");
254       ex.printStackTrace(System.err);
255       return null;
256     } finally
257     {
258       // System.err.println("EBIFetch took " + (System.currentTimeMillis() -
259       // time) + " ms");
260     }
261     return null;
262   }
263
264   /**
265    * Constructs the URL to fetch from
266    * 
267    * @param ids
268    * @param database
269    * @param format
270    * @return
271    */
272   static String buildUrl(String ids, String database, String format)
273   {
274     String url;
275     if (database.equalsIgnoreCase(DBRefSource.EMBL)
276             || database.equalsIgnoreCase(DBRefSource.EMBLCDS))
277     {
278       url = "https://www.ebi.ac.uk/ena/data/view/" + ids.toLowerCase()
279               + (format != null ? "&" + format : "");
280     }
281     else
282     {
283       url = "https://www.ebi.ac.uk/Tools/dbfetch/dbfetch/"
284               + database.toLowerCase() + "/" + ids.toLowerCase()
285               + (format != null ? "/" + format : "");
286     }
287     return url;
288   }
289 }