JAL-1083 graceful failover to one ID per sequences query for sources that fail for...
[jalview.git] / src / jalview / ws / dbsources / das / datamodel / DasSequenceSource.java
1 /*\r
2  * Jalview - A Sequence Alignment Editor and Viewer (Version 2.7)\r
3  * Copyright (C) 2011 J Procter, AM Waterhouse, G Barton, M Clamp, S Searle\r
4  * \r
5  * This file is part of Jalview.\r
6  * \r
7  * Jalview is free software: you can redistribute it and/or\r
8  * modify it under the terms of the GNU General Public License \r
9  * as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.\r
10  * \r
11  * Jalview is distributed in the hope that it will be useful, but \r
12  * WITHOUT ANY WARRANTY; without even the implied warranty \r
13  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR \r
14  * PURPOSE.  See the GNU General Public License for more details.\r
15  * \r
16  * You should have received a copy of the GNU General Public License along with Jalview.  If not, see <http://www.gnu.org/licenses/>.\r
17  */\r
18 package jalview.ws.dbsources.das.datamodel;\r
19 \r
20 import java.util.ArrayList;\r
21 import java.util.Arrays;\r
22 import java.util.HashMap;\r
23 import java.util.List;\r
24 import java.util.Map;\r
25 import java.util.StringTokenizer;\r
26 import java.util.Vector;\r
27 \r
28 import org.biodas.jdas.client.SequenceClient;\r
29 import org.biodas.jdas.client.adapters.sequence.DasSequenceAdapter;\r
30 import org.biodas.jdas.client.threads.SequenceClientMultipleSources;\r
31 import org.biodas.jdas.schema.sequence.SEQUENCE;\r
32 import org.biodas.jdas.schema.sources.COORDINATES;\r
33 import org.biodas.jdas.schema.sources.SOURCE;\r
34 import org.biodas.jdas.schema.sources.VERSION;\r
35 \r
36 import com.stevesoft.pat.Regex;\r
37 \r
38 import jalview.ws.dbsources.das.api.jalviewSourceI;\r
39 import jalview.ws.seqfetcher.*;\r
40 import jalview.bin.Cache;\r
41 import jalview.datamodel.Alignment;\r
42 import jalview.datamodel.AlignmentI;\r
43 import jalview.datamodel.DBRefEntry;\r
44 import jalview.datamodel.Sequence;\r
45 import jalview.datamodel.SequenceI;\r
46 \r
47 /**\r
48  * an instance of this class is created for each unique DAS Sequence source (ie\r
49  * one capable of handling the 'sequence' for a particular MapMaster)\r
50  * \r
51  * @author JimP\r
52  * \r
53  */\r
54 public class DasSequenceSource extends DbSourceProxyImpl implements\r
55         DbSourceProxy\r
56 {\r
57   private jalviewSourceI jsrc;\r
58 \r
59   protected SOURCE source = null;\r
60 \r
61   protected VERSION version = null;\r
62 \r
63   protected COORDINATES coordsys = null;\r
64 \r
65   protected String dbname = "DASCS";\r
66 \r
67   protected String dbrefname = "das:source";\r
68 \r
69   /**\r
70    * create a new DbSource proxy for a DAS 1 source\r
71    * \r
72    * @param dbnbame\r
73    *          Human Readable Name to use when fetching from this source\r
74    * @param dbrefname\r
75    *          DbRefName for DbRefs attached to sequences retrieved from this\r
76    *          source\r
77    * @param source\r
78    *          Das1Source\r
79    * @param coordsys\r
80    *          specific coordinate system to use for this source\r
81    * @throws Exception\r
82    *           if source is not capable of the 'sequence' command\r
83    */\r
84   public DasSequenceSource(String dbname, String dbrefname, SOURCE source,\r
85           VERSION version, COORDINATES coordsys) throws Exception\r
86   {\r
87     if (!(jsrc = new JalviewSource(source, false)).isSequenceSource())\r
88     {\r
89       throw new Exception("Source " + source.getTitle()\r
90               + " does not support the sequence command.");\r
91     }\r
92     this.source = source;\r
93     this.dbname = dbname;\r
94     this.dbrefname = dbrefname;\r
95     this.coordsys = coordsys;\r
96   }\r
97 \r
98   public String getAccessionSeparator()\r
99   {\r
100     return "\t";\r
101   }\r
102 \r
103   public Regex getAccessionValidator()\r
104   {\r
105     /** ? * */\r
106     return Regex.perlCode("\\S+");\r
107   }\r
108 \r
109   public String getDbName()\r
110   {\r
111     // TODO: map to\r
112     return dbname + " (DAS)";\r
113   }\r
114 \r
115   public String getDbSource()\r
116   {\r
117     return dbrefname;\r
118   }\r
119 \r
120   public String getDbVersion()\r
121   {\r
122     return coordsys.getVersion();\r
123   }\r
124 \r
125 \r
126   public AlignmentI getSequenceRecords(String queries) throws Exception\r
127   {\r
128     StringTokenizer st = new StringTokenizer(queries, "\t");\r
129     List<String> toks = new ArrayList<String>(), src = new ArrayList<String>(), acIds = new ArrayList<String>();\r
130     while (st.hasMoreTokens())\r
131     {\r
132       String t;\r
133       toks.add(t = st.nextToken());\r
134       acIds.add(t.replaceAll(":[0-9,]+", ""));\r
135     }\r
136     src.add(jsrc.getSourceURL());\r
137     Map<String, Map<List<String>, DasSequenceAdapter>> resultset = new HashMap<String, Map<List<String>, DasSequenceAdapter>>();\r
138     Map<String, Map<List<String>, Exception>> errors = new HashMap<String, Map<List<String>, Exception>>();\r
139     \r
140     // First try multiple sources\r
141     boolean multiple = true, retry = false;\r
142     do\r
143     {\r
144       if (!multiple)\r
145       {\r
146         retry = false;\r
147         // slow, fetch one at a time.\r
148         for (String sr : src)\r
149         {\r
150           System.err\r
151                   .println("Retrieving IDs individually from das source: "\r
152                           + sr);\r
153           // todo : get conn property provider\r
154           org.biodas.jdas.client.SequenceClient sq = new SequenceClient();\r
155           for (String q : toks)\r
156           {\r
157             List<String> qset = Arrays.asList(new String[]\r
158             { q });\r
159             try\r
160             {\r
161               DasSequenceAdapter s = sq.fetchData(sr, qset);\r
162               Map<List<String>, DasSequenceAdapter> dss = resultset.get(sr);\r
163               if (dss == null)\r
164               {\r
165                 resultset\r
166                         .put(sr,\r
167                                 dss = new HashMap<List<String>, DasSequenceAdapter>());\r
168               }\r
169               dss.put(qset, s);\r
170             } catch (Exception x)\r
171             {\r
172               Map<List<String>, Exception> ers = errors.get(sr);\r
173               if (ers == null)\r
174               {\r
175                 errors.put(sr, ers = new HashMap<List<String>, Exception>());\r
176               }\r
177               ers.put(qset, x);\r
178             }\r
179           }\r
180         }\r
181       }\r
182       else\r
183       {\r
184         SequenceClientMultipleSources sclient;\r
185         sclient = new SequenceClientMultipleSources();\r
186         sclient.fetchData(src, toks, resultset, errors);\r
187         sclient.shutDown();\r
188         while (!sclient.isTerminated())\r
189         {\r
190           try\r
191           {\r
192             Thread.sleep(200);\r
193 \r
194           } catch (InterruptedException x)\r
195           {\r
196           }\r
197         }\r
198         if (resultset.isEmpty() && !errors.isEmpty())\r
199         {\r
200           retry = true;\r
201           multiple = false;\r
202         }\r
203       }\r
204     } while (retry);\r
205 \r
206     if (resultset.isEmpty())\r
207     {\r
208       System.err.println("Sequence Query to " + jsrc.getTitle() + " with '"\r
209               + queries + "' returned no sequences.");\r
210       return null;\r
211     }\r
212     else\r
213     {\r
214       Vector<SequenceI> seqs=null;\r
215       for (Map.Entry<String, Map<List<String>, DasSequenceAdapter>> resset : resultset\r
216               .entrySet())\r
217       {\r
218         for (Map.Entry<List<String>, DasSequenceAdapter> result : resset\r
219                 .getValue().entrySet())\r
220         {\r
221           DasSequenceAdapter dasseqresp = result.getValue();\r
222           List<String> accessions = result.getKey();\r
223           for (SEQUENCE e : dasseqresp.getSequence())\r
224           {\r
225             String lbl = e.getId();\r
226 \r
227             if (acIds.indexOf(lbl) == -1)\r
228             {\r
229               System.err\r
230                       .println("Warning - received sequence event for strange accession code ("\r
231                               + lbl + ")");\r
232             }\r
233             else\r
234             {\r
235               if (seqs == null)\r
236               {\r
237                 if (e.getContent().length() == 0)\r
238                 {\r
239                   System.err\r
240                           .println("Empty sequence returned for accession code ("\r
241                                   + lbl\r
242                                   + ") from "\r
243                                   + resset.getKey()\r
244                                   + " (source is "\r
245                                   + getDbName());\r
246                   continue;\r
247                 }\r
248               }\r
249               seqs = new java.util.Vector<SequenceI>();\r
250               // JDAS returns a sequence complete with any newlines and spaces in the XML\r
251               Sequence sq = new Sequence(lbl, e.getContent().replaceAll("\\s+", ""));\r
252               sq.addDBRef(new DBRefEntry(getDbSource(), getDbVersion()\r
253                       + ":" + e.getVersion(), lbl));\r
254               seqs.addElement(sq);\r
255             }\r
256           }\r
257         }\r
258       }\r
259 \r
260       if (seqs == null || seqs.size() == 0)\r
261         return null;\r
262       SequenceI[] sqs = new SequenceI[seqs.size()];\r
263       for (int i = 0, iSize = seqs.size(); i < iSize; i++)\r
264       {\r
265         sqs[i] = (SequenceI) seqs.elementAt(i);\r
266       }\r
267       Alignment al = new Alignment(sqs);\r
268       if (jsrc.isFeatureSource())\r
269       {\r
270         java.util.Vector<jalviewSourceI> srcs = new java.util.Vector<jalviewSourceI>();\r
271         srcs.addElement(jsrc);\r
272         try {\r
273           new jalview.ws.DasSequenceFeatureFetcher(sqs, null, srcs, false,\r
274                 false);\r
275         } catch (Exception x)\r
276         {\r
277           Cache.log.error("Couldn't retrieve features for sequence from its source.",x);\r
278         }\r
279       }\r
280 \r
281       return al;\r
282     }\r
283   }\r
284 \r
285   public String getTestQuery()\r
286   {\r
287     return coordsys.getTestRange();\r
288   }\r
289 \r
290   public boolean isValidReference(String accession)\r
291   {\r
292     // TODO try to validate an accession against source\r
293     // We don't really know how to do this without querying source\r
294 \r
295     return true;\r
296   }\r
297 \r
298   /**\r
299    * @return the source\r
300    */\r
301   public SOURCE getSource()\r
302   {\r
303     return source;\r
304   }\r
305 \r
306   /**\r
307    * @return the coordsys\r
308    */\r
309   public COORDINATES getCoordsys()\r
310   {\r
311     return coordsys;\r
312   }\r
313 }\r