JAL-1083 - graceful failover to single ID query mode for fetching features
[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   public AlignmentI getSequenceRecords(String queries) throws Exception\r
126   {\r
127     StringTokenizer st = new StringTokenizer(queries, "\t");\r
128     List<String> toks = new ArrayList<String>(), src = new ArrayList<String>(), acIds = new ArrayList<String>();\r
129     while (st.hasMoreTokens())\r
130     {\r
131       String t;\r
132       toks.add(t = st.nextToken());\r
133       acIds.add(t.replaceAll(":[0-9,]+", ""));\r
134     }\r
135     src.add(jsrc.getSourceURL());\r
136     Map<String, Map<List<String>, DasSequenceAdapter>> resultset = new HashMap<String, Map<List<String>, DasSequenceAdapter>>();\r
137     Map<String, Map<List<String>, Exception>> errors = new HashMap<String, Map<List<String>, Exception>>();\r
138     \r
139     // First try multiple sources\r
140     boolean multiple = true, retry = false;\r
141     do\r
142     {\r
143       if (!multiple)\r
144       {\r
145         retry = false;\r
146         // slow, fetch one at a time.\r
147         for (String sr : src)\r
148         {\r
149           System.err\r
150                   .println("Retrieving IDs individually from das source: "\r
151                           + sr);\r
152           // todo : get conn property provider\r
153           org.biodas.jdas.client.SequenceClient sq = new SequenceClient();\r
154           for (String q : toks)\r
155           {\r
156             List<String> qset = Arrays.asList(new String[]\r
157             { q });\r
158             try\r
159             {\r
160               DasSequenceAdapter s = sq.fetchData(sr, qset);\r
161               Map<List<String>, DasSequenceAdapter> dss = resultset.get(sr);\r
162               if (dss == null)\r
163               {\r
164                 resultset\r
165                         .put(sr,\r
166                                 dss = new HashMap<List<String>, DasSequenceAdapter>());\r
167               }\r
168               dss.put(qset, s);\r
169             } catch (Exception x)\r
170             {\r
171               Map<List<String>, Exception> ers = errors.get(sr);\r
172               if (ers == null)\r
173               {\r
174                 errors.put(sr, ers = new HashMap<List<String>, Exception>());\r
175               }\r
176               ers.put(qset, x);\r
177             }\r
178           }\r
179         }\r
180       }\r
181       else\r
182       {\r
183         SequenceClientMultipleSources sclient;\r
184         sclient = new SequenceClientMultipleSources();\r
185         sclient.fetchData(src, toks, resultset, errors);\r
186         sclient.shutDown();\r
187         while (!sclient.isTerminated())\r
188         {\r
189           try\r
190           {\r
191             Thread.sleep(200);\r
192 \r
193           } catch (InterruptedException x)\r
194           {\r
195           }\r
196         }\r
197         if (resultset.isEmpty() && !errors.isEmpty())\r
198         {\r
199           retry = true;\r
200           multiple = false;\r
201         }\r
202       }\r
203     } while (retry);\r
204 \r
205     if (resultset.isEmpty())\r
206     {\r
207       System.err.println("Sequence Query to " + jsrc.getTitle() + " with '"\r
208               + queries + "' returned no sequences.");\r
209       return null;\r
210     }\r
211     else\r
212     {\r
213       Vector<SequenceI> seqs = null;\r
214       for (Map.Entry<String, Map<List<String>, DasSequenceAdapter>> resset : resultset\r
215               .entrySet())\r
216       {\r
217         for (Map.Entry<List<String>, DasSequenceAdapter> result : resset\r
218                 .getValue().entrySet())\r
219         {\r
220           DasSequenceAdapter dasseqresp = result.getValue();\r
221           List<String> accessions = result.getKey();\r
222           for (SEQUENCE e : dasseqresp.getSequence())\r
223           {\r
224             String lbl = e.getId();\r
225 \r
226             if (acIds.indexOf(lbl) == -1)\r
227             {\r
228               System.err\r
229                       .println("Warning - received sequence event for strange accession code ("\r
230                               + lbl + ")");\r
231             }\r
232             else\r
233             {\r
234               if (seqs == null)\r
235               {\r
236                 if (e.getContent().length() == 0)\r
237                 {\r
238                   System.err\r
239                           .println("Empty sequence returned for accession code ("\r
240                                   + lbl\r
241                                   + ") from "\r
242                                   + resset.getKey()\r
243                                   + " (source is " + getDbName());\r
244                   continue;\r
245                 }\r
246               }\r
247               seqs = new java.util.Vector<SequenceI>();\r
248               // JDAS returns a sequence complete with any newlines and spaces\r
249               // in the XML\r
250               Sequence sq = new Sequence(lbl, e.getContent().replaceAll(\r
251                       "\\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         {\r
274           jalview.ws.DasSequenceFeatureFetcher dssf=new jalview.ws.DasSequenceFeatureFetcher(sqs, null, srcs, false,\r
275                   false, multiple);\r
276         } catch (Exception x)\r
277         {\r
278           Cache.log\r
279                   .error("Couldn't retrieve features for sequence from its source.",\r
280                           x);\r
281         }\r
282       }\r
283 \r
284       return al;\r
285     }\r
286   }\r
287 \r
288   public String getTestQuery()\r
289   {\r
290     return coordsys.getTestRange();\r
291   }\r
292 \r
293   public boolean isValidReference(String accession)\r
294   {\r
295     // TODO try to validate an accession against source\r
296     // We don't really know how to do this without querying source\r
297 \r
298     return true;\r
299   }\r
300 \r
301   /**\r
302    * @return the source\r
303    */\r
304   public SOURCE getSource()\r
305   {\r
306     return source;\r
307   }\r
308 \r
309   /**\r
310    * @return the coordsys\r
311    */\r
312   public COORDINATES getCoordsys()\r
313   {\r
314     return coordsys;\r
315   }\r
316 }\r