JAL-1085 - ensure sequence fetching client waits around until feature retrieval has...
[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.ConnectionPropertyProviderI;\r
29 import org.biodas.jdas.client.SequenceClient;\r
30 import org.biodas.jdas.client.adapters.sequence.DasSequenceAdapter;\r
31 import org.biodas.jdas.client.threads.MultipleConnectionPropertyProviderI;\r
32 import org.biodas.jdas.client.threads.SequenceClientMultipleSources;\r
33 import org.biodas.jdas.schema.sequence.SEQUENCE;\r
34 import org.biodas.jdas.schema.sources.COORDINATES;\r
35 import org.biodas.jdas.schema.sources.SOURCE;\r
36 import org.biodas.jdas.schema.sources.VERSION;\r
37 \r
38 import com.stevesoft.pat.Regex;\r
39 \r
40 import jalview.ws.dbsources.das.api.jalviewSourceI;\r
41 import jalview.ws.seqfetcher.*;\r
42 import jalview.bin.Cache;\r
43 import jalview.datamodel.Alignment;\r
44 import jalview.datamodel.AlignmentI;\r
45 import jalview.datamodel.DBRefEntry;\r
46 import jalview.datamodel.Sequence;\r
47 import jalview.datamodel.SequenceI;\r
48 \r
49 /**\r
50  * an instance of this class is created for each unique DAS Sequence source (ie\r
51  * one capable of handling the 'sequence' for a particular MapMaster)\r
52  * \r
53  * @author JimP\r
54  * \r
55  */\r
56 public class DasSequenceSource extends DbSourceProxyImpl implements\r
57         DbSourceProxy\r
58 {\r
59   private jalviewSourceI jsrc;\r
60 \r
61   protected SOURCE source = null;\r
62 \r
63   protected VERSION version = null;\r
64 \r
65   protected COORDINATES coordsys = null;\r
66 \r
67   protected String dbname = "DASCS";\r
68 \r
69   protected String dbrefname = "das:source";\r
70   protected MultipleConnectionPropertyProviderI connprops=null;\r
71   /**\r
72    * create a new DbSource proxy for a DAS 1 source\r
73    * \r
74    * @param dbnbame\r
75    *          Human Readable Name to use when fetching from this source\r
76    * @param dbrefname\r
77    *          DbRefName for DbRefs attached to sequences retrieved from this\r
78    *          source\r
79    * @param source\r
80    *          Das1Source\r
81    * @param coordsys\r
82    *          specific coordinate system to use for this source\r
83    * @throws Exception\r
84    *           if source is not capable of the 'sequence' command\r
85    */\r
86   public DasSequenceSource(String dbname, String dbrefname, SOURCE source,\r
87           VERSION version, COORDINATES coordsys, MultipleConnectionPropertyProviderI connprops) throws Exception\r
88   {\r
89     if (!(jsrc = new JalviewSource(source, connprops, false)).isSequenceSource())\r
90     {\r
91       throw new Exception("Source " + source.getTitle()\r
92               + " does not support the sequence command.");\r
93     }\r
94     this.source = source;\r
95     this.dbname = dbname;\r
96     this.dbrefname = dbrefname;\r
97     this.coordsys = coordsys;\r
98     this.connprops=connprops;\r
99   }\r
100 \r
101   public String getAccessionSeparator()\r
102   {\r
103     return "\t";\r
104   }\r
105 \r
106   public Regex getAccessionValidator()\r
107   {\r
108     /** ? * */\r
109     return Regex.perlCode("\\S+");\r
110   }\r
111 \r
112   public String getDbName()\r
113   {\r
114     // TODO: map to\r
115     return dbname + " (DAS)";\r
116   }\r
117 \r
118   public String getDbSource()\r
119   {\r
120     return dbrefname;\r
121   }\r
122 \r
123   public String getDbVersion()\r
124   {\r
125     return coordsys.getVersion();\r
126   }\r
127 \r
128   public AlignmentI getSequenceRecords(String queries) throws Exception\r
129   {\r
130     StringTokenizer st = new StringTokenizer(queries, "\t");\r
131     List<String> toks = new ArrayList<String>(), src = new ArrayList<String>(), acIds = new ArrayList<String>();\r
132     while (st.hasMoreTokens())\r
133     {\r
134       String t;\r
135       toks.add(t = st.nextToken());\r
136       acIds.add(t.replaceAll(":[0-9,]+", ""));\r
137     }\r
138     src.add(jsrc.getSourceURL());\r
139     Map<String, Map<List<String>, DasSequenceAdapter>> resultset = new HashMap<String, Map<List<String>, DasSequenceAdapter>>();\r
140     Map<String, Map<List<String>, Exception>> errors = new HashMap<String, Map<List<String>, Exception>>();\r
141     \r
142     // First try multiple sources\r
143     boolean multiple = true, retry = false;\r
144     do\r
145     {\r
146       if (!multiple)\r
147       {\r
148         retry = false;\r
149         // slow, fetch one at a time.\r
150         for (String sr : src)\r
151         {\r
152           System.err\r
153                   .println("Retrieving IDs individually from das source: "\r
154                           + sr);\r
155           org.biodas.jdas.client.SequenceClient sq = new SequenceClient(connprops.getConnectionPropertyProviderFor(sr));\r
156           for (String q : toks)\r
157           {\r
158             List<String> qset = Arrays.asList(new String[]\r
159             { q });\r
160             try\r
161             {\r
162               DasSequenceAdapter s = sq.fetchData(sr, qset);\r
163               Map<List<String>, DasSequenceAdapter> dss = resultset.get(sr);\r
164               if (dss == null)\r
165               {\r
166                 resultset\r
167                         .put(sr,\r
168                                 dss = new HashMap<List<String>, DasSequenceAdapter>());\r
169               }\r
170               dss.put(qset, s);\r
171             } catch (Exception x)\r
172             {\r
173               Map<List<String>, Exception> ers = errors.get(sr);\r
174               if (ers == null)\r
175               {\r
176                 errors.put(sr, ers = new HashMap<List<String>, Exception>());\r
177               }\r
178               ers.put(qset, x);\r
179             }\r
180           }\r
181         }\r
182       }\r
183       else\r
184       {\r
185         SequenceClientMultipleSources sclient;\r
186         sclient = new SequenceClientMultipleSources();\r
187         sclient.fetchData(src, toks, resultset, errors);\r
188         sclient.shutDown();\r
189         while (!sclient.isTerminated())\r
190         {\r
191           try\r
192           {\r
193             Thread.sleep(200);\r
194 \r
195           } catch (InterruptedException x)\r
196           {\r
197           }\r
198         }\r
199         if (resultset.isEmpty() && !errors.isEmpty())\r
200         {\r
201           retry = true;\r
202           multiple = false;\r
203         }\r
204       }\r
205     } while (retry);\r
206 \r
207     if (resultset.isEmpty())\r
208     {\r
209       System.err.println("Sequence Query to " + jsrc.getTitle() + " with '"\r
210               + queries + "' returned no sequences.");\r
211       return null;\r
212     }\r
213     else\r
214     {\r
215       Vector<SequenceI> seqs = null;\r
216       for (Map.Entry<String, Map<List<String>, DasSequenceAdapter>> resset : resultset\r
217               .entrySet())\r
218       {\r
219         for (Map.Entry<List<String>, DasSequenceAdapter> result : resset\r
220                 .getValue().entrySet())\r
221         {\r
222           DasSequenceAdapter dasseqresp = result.getValue();\r
223           List<String> accessions = result.getKey();\r
224           for (SEQUENCE e : dasseqresp.getSequence())\r
225           {\r
226             String lbl = e.getId();\r
227 \r
228             if (acIds.indexOf(lbl) == -1)\r
229             {\r
230               System.err\r
231                       .println("Warning - received sequence event for strange accession code ("\r
232                               + lbl + ")");\r
233             }\r
234             else\r
235             {\r
236               if (seqs == null)\r
237               {\r
238                 if (e.getContent().length() == 0)\r
239                 {\r
240                   System.err\r
241                           .println("Empty sequence returned for accession code ("\r
242                                   + lbl\r
243                                   + ") from "\r
244                                   + resset.getKey()\r
245                                   + " (source is " + 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\r
251               // in the XML\r
252               Sequence sq = new Sequence(lbl, e.getContent().replaceAll(\r
253                       "\\s+", ""));\r
254               sq.addDBRef(new DBRefEntry(getDbSource(), getDbVersion()\r
255                       + ":" + e.getVersion(), lbl));\r
256               seqs.addElement(sq);\r
257             }\r
258           }\r
259         }\r
260       }\r
261 \r
262       if (seqs == null || seqs.size() == 0)\r
263         return null;\r
264       SequenceI[] sqs = new SequenceI[seqs.size()];\r
265       for (int i = 0, iSize = seqs.size(); i < iSize; i++)\r
266       {\r
267         sqs[i] = (SequenceI) seqs.elementAt(i);\r
268       }\r
269       Alignment al = new Alignment(sqs);\r
270       if (jsrc.isFeatureSource())\r
271       {\r
272         java.util.Vector<jalviewSourceI> srcs = new java.util.Vector<jalviewSourceI>();\r
273         srcs.addElement(jsrc);\r
274         try\r
275         {\r
276           jalview.ws.DasSequenceFeatureFetcher dssf=new jalview.ws.DasSequenceFeatureFetcher(sqs, null, srcs, false,\r
277                   false, multiple);\r
278           while (dssf.isRunning())\r
279           {\r
280             try {\r
281               Thread.sleep(200);\r
282             } catch (InterruptedException x)\r
283             {\r
284               \r
285             }\r
286           }\r
287           \r
288         } catch (Exception x)\r
289         {\r
290           Cache.log\r
291                   .error("Couldn't retrieve features for sequence from its source.",\r
292                           x);\r
293         }\r
294       }\r
295 \r
296       return al;\r
297     }\r
298   }\r
299 \r
300   public String getTestQuery()\r
301   {\r
302     return coordsys.getTestRange();\r
303   }\r
304 \r
305   public boolean isValidReference(String accession)\r
306   {\r
307     // TODO try to validate an accession against source\r
308     // We don't really know how to do this without querying source\r
309 \r
310     return true;\r
311   }\r
312 \r
313   /**\r
314    * @return the source\r
315    */\r
316   public SOURCE getSource()\r
317   {\r
318     return source;\r
319   }\r
320 \r
321   /**\r
322    * @return the coordsys\r
323    */\r
324   public COORDINATES getCoordsys()\r
325   {\r
326     return coordsys;\r
327   }\r
328 }\r