After merge
[jalview.git] / src / jalview / ws / MsaWSClient.java
1 /*\r
2  * Jalview - A Sequence Alignment Editor and Viewer\r
3  * Copyright (C) 2005 AM Waterhouse, J Procter, G Barton, M Clamp, S Searle\r
4  *\r
5  * This program is free software; you can redistribute it and/or\r
6  * modify it under the terms of the GNU General Public License\r
7  * as published by the Free Software Foundation; either version 2\r
8  * of the License, or (at your option) any later version.\r
9  *\r
10  * This program is distributed in the hope that it will be useful,\r
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
13  * GNU General Public License for more details.\r
14  *\r
15  * You should have received a copy of the GNU General Public License\r
16  * along with this program; if not, write to the Free Software\r
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA\r
18  */\r
19 package jalview.ws;\r
20 \r
21 import ext.vamsas.*;\r
22 \r
23 import jalview.analysis.AlignSeq;\r
24 \r
25 import jalview.datamodel.*;\r
26 \r
27 import jalview.gui.*;\r
28 \r
29 import java.util.*;\r
30 \r
31 import javax.swing.*;\r
32 \r
33 /**\r
34  * DOCUMENT ME!\r
35  *\r
36  * @author $author$\r
37  * @version $Revision$\r
38  */\r
39 public class MsaWSClient\r
40     extends WSClient\r
41 {\r
42   /**\r
43    * server is a WSDL2Java generated stub for an archetypal MsaWSI service.\r
44    */\r
45   ext.vamsas.MuscleWS server;\r
46 \r
47 \r
48   /**\r
49    * Creates a new MsaWSClient object that uses a service\r
50    * given by an externally retrieved ServiceHandle\r
51    *\r
52    * @param sh service handle of type AbstractName(MsaWS)\r
53    * @param altitle DOCUMENT ME!\r
54    * @param msa DOCUMENT ME!\r
55    * @param submitGaps DOCUMENT ME!\r
56    * @param preserveOrder DOCUMENT ME!\r
57    */\r
58 \r
59   public MsaWSClient(ext.vamsas.ServiceHandle sh, String altitle, SequenceI[] msa,\r
60                      boolean submitGaps, boolean preserveOrder, Alignment seqdataset)\r
61   {\r
62 \r
63     if (!sh.getAbstractName().equals("MsaWS"))\r
64     {\r
65       JOptionPane.showMessageDialog(Desktop.desktop,\r
66                                     "The Service called \n" + sh.getName() +\r
67           "\nis not a \nMultiple Sequence Alignment Service !",\r
68                                     "Internal Jalview Error",\r
69                                     JOptionPane.WARNING_MESSAGE);\r
70 \r
71       return;\r
72     }\r
73 \r
74     if ((wsInfo = this.setWebService(sh))==null)\r
75      {\r
76        JOptionPane.showMessageDialog(Desktop.desktop,\r
77                                      "The Multiple Sequence Alignment Service named " +\r
78                                      sh.getName() +\r
79                                      " is unknown", "Internal Jalview Error",\r
80                                      JOptionPane.WARNING_MESSAGE);\r
81 \r
82        return;\r
83      }\r
84     startMsaWSClient(altitle, msa, submitGaps, preserveOrder, seqdataset);\r
85 \r
86   }\r
87 \r
88 \r
89   private void startMsaWSClient(String altitle, SequenceI[] msa,\r
90                      boolean submitGaps, boolean preserveOrder, Alignment seqdataset)\r
91   {\r
92     if (!locateWebService())\r
93     {\r
94       return;\r
95     }\r
96 \r
97     wsInfo.setProgressText( ( (submitGaps) ? "Re-alignment" : "Alignment") +\r
98                            " of " + altitle + "\nJob details\n");\r
99 \r
100     MsaWSThread musclethread = new MsaWSThread(WebServiceName +\r
101                                                " alignment of " + altitle, msa,\r
102                                                submitGaps, preserveOrder, seqdataset);\r
103     wsInfo.setthisService(musclethread);\r
104     musclethread.start();\r
105   }\r
106 \r
107   /**\r
108    * Initializes the server field with a valid service implementation.\r
109    *\r
110    * @return true if service was located.\r
111    */\r
112   private boolean locateWebService()\r
113   {\r
114     // TODO: MuscleWS transmuted to generic MsaWS client\r
115     MuscleWSServiceLocator loc = new MuscleWSServiceLocator(); // Default\r
116 \r
117     try\r
118     {\r
119       this.server = (MuscleWS) loc.getMuscleWS(new java.net.URL(WsURL));\r
120       ( (MuscleWSSoapBindingStub)this.server).setTimeout(60000); // One minute timeout\r
121     }\r
122     catch (Exception ex)\r
123     {\r
124       wsInfo.setProgressText("Serious! " + WebServiceName +\r
125                              " Service location failed\nfor URL :" + WsURL +\r
126                              "\n" +\r
127                              ex.getMessage());\r
128       wsInfo.setStatus(WebserviceInfo.ERROR);\r
129       ex.printStackTrace();\r
130 \r
131       return false;\r
132     }\r
133 \r
134     loc.getEngine().setOption("axis", "1");\r
135 \r
136     return true;\r
137   }\r
138 \r
139   protected class MsaWSThread\r
140       extends Thread implements WSClientI\r
141   {\r
142     String ServiceName = WebServiceName;\r
143     String OutputHeader;\r
144     vamsas.objects.simple.MsaResult result = null;\r
145     vamsas.objects.simple.SequenceSet seqs = new vamsas.objects.simple.\r
146         SequenceSet();\r
147     Hashtable SeqNames = null;\r
148     boolean submitGaps = false; // pass sequences including gaps to alignment service\r
149     boolean preserveOrder = true; // and always store and recover sequence order\r
150     String jobId;\r
151     String alTitle; // name which will be used to form new alignment window.\r
152     int allowedServerExceptions = 3; // thread dies if too many exceptions.\r
153     boolean jobComplete = false;\r
154 \r
155     Alignment dataset; // dataset to which the new alignment will be associated.\r
156 \r
157     MsaWSThread(String title, SequenceI[] msa, boolean subgaps,\r
158                 boolean presorder, Alignment seqset)\r
159     {\r
160       alTitle = title;\r
161       submitGaps = subgaps;\r
162       preserveOrder = presorder;\r
163       dataset = seqset;\r
164       OutputHeader = wsInfo.getProgressText();\r
165       SeqNames = new Hashtable();\r
166 \r
167       vamsas.objects.simple.Sequence[] seqarray = new vamsas.objects.simple.\r
168           Sequence[msa.length];\r
169 \r
170       for (int i = 0; i < msa.length; i++)\r
171       {\r
172         String newname = jalview.analysis.SeqsetUtils.unique_name(i);\r
173 \r
174         // uniquify as we go\r
175         // TODO: JBPNote: this is a ubiquitous transformation - set of jalview seq objects to vamsas sequences with name preservation\r
176         SeqNames.put(newname,\r
177                      jalview.analysis.SeqsetUtils.SeqCharacterHash(msa[i]));\r
178         seqarray[i] = new vamsas.objects.simple.Sequence();\r
179         seqarray[i].setId(newname);\r
180         seqarray[i].setSeq( (submitGaps) ? msa[i].getSequence()\r
181                            : AlignSeq.extractGaps(\r
182                                jalview.util.Comparison.GapChars,\r
183                                msa[i].getSequence()));\r
184       }\r
185 \r
186       this.seqs = new vamsas.objects.simple.SequenceSet();\r
187       this.seqs.setSeqs(seqarray);\r
188     }\r
189 \r
190     public boolean isCancellable()\r
191     {\r
192       return true;\r
193     }\r
194 \r
195     public void cancelJob()\r
196     {\r
197       if ( (jobId != null) && !jobId.equals("") && !jobComplete)\r
198       {\r
199         String cancelledMessage = "";\r
200 \r
201         try\r
202         {\r
203           vamsas.objects.simple.WsJobId cancelledJob = server.cancel(jobId);\r
204 \r
205           if (cancelledJob.getStatus() == 2)\r
206           {\r
207             // CANCELLED_JOB\r
208             cancelledMessage = "Job cancelled.";\r
209             wsInfo.setStatus(WebserviceInfo.STATE_CANCELLED_OK);\r
210             jobComplete = true;\r
211             jobsRunning--;\r
212             result = null;\r
213           }\r
214           else if (cancelledJob.getStatus() == 3)\r
215           {\r
216             // VALID UNSTOPPABLE JOB\r
217             cancelledMessage +=\r
218                 "Server cannot cancel this job. just close the window.\n";\r
219           }\r
220 \r
221           if (cancelledJob.getJobId() != null)\r
222           {\r
223             cancelledMessage += ("[" + cancelledJob.getJobId() +\r
224                                  "]");\r
225           }\r
226 \r
227           cancelledMessage += "\n";\r
228         }\r
229         catch (Exception exc)\r
230         {\r
231           cancelledMessage +=\r
232               ("\nProblems cancelling the job : Exception received...\n" +\r
233                exc + "\n");\r
234           exc.printStackTrace();\r
235         }\r
236 \r
237         wsInfo.setProgressText(OutputHeader + cancelledMessage + "\n");\r
238       }\r
239       else\r
240       {\r
241         if (!jobComplete)\r
242         {\r
243           wsInfo.setProgressText(OutputHeader +\r
244                                  "Server cannot cancel this job because it has not been submitted properly. just close the window.\n");\r
245         }\r
246       }\r
247     }\r
248 \r
249     public void run()\r
250     {\r
251       StartJob();\r
252 \r
253       while (!jobComplete && (allowedServerExceptions > 0))\r
254       {\r
255         try\r
256         {\r
257           if ( (result = server.getResult(jobId)) == null)\r
258           {\r
259             throw (new Exception(\r
260                 "Timed out when communicating with server\nTry again later.\n"));\r
261           }\r
262           jalview.bin.Cache.log.debug("Result state " + result.getState() +\r
263                                         "(ServerError=" + result.isServerError() +\r
264                                         ")");\r
265           if (result.isRunning())\r
266           {\r
267             wsInfo.setStatus(WebserviceInfo.STATE_RUNNING);\r
268           }\r
269           else if (result.isQueued())\r
270           {\r
271             wsInfo.setStatus(WebserviceInfo.STATE_QUEUING);\r
272           }\r
273 \r
274           if (result.isFinished())\r
275           {\r
276             wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_OK);\r
277             parseResult();\r
278             jobComplete = true;\r
279             jobsRunning--;\r
280           }\r
281           else\r
282           {\r
283             if (result.getStatus() != null)\r
284             {\r
285               wsInfo.setProgressText(OutputHeader + "\n"+   result.getStatus());\r
286             }\r
287             if (result.isServerError())\r
288             {\r
289               jobComplete = true;\r
290               jobsRunning--;\r
291 \r
292               break;\r
293             }\r
294             if (! (result.isJobFailed() || result.isServerError() ||\r
295                    result.isBroken() || result.isFailed()))\r
296             {\r
297               Thread.sleep(5000);\r
298 \r
299               //  System.out.println("I'm alive "+seqid+" "+jobid);\r
300             }\r
301             else\r
302             {\r
303               jobComplete = true;\r
304               jobsRunning--;\r
305               break;\r
306             }\r
307           }\r
308         }\r
309         catch (Exception ex)\r
310         {\r
311           allowedServerExceptions--;\r
312           wsInfo.appendProgressText("\n" + ServiceName +\r
313                                     " Server exception!\n" + ex.getMessage());\r
314           System.err.println(ServiceName + " Server exception: " +\r
315                              ex.getMessage());\r
316 \r
317           //          ex.printStackTrace(); JBPNote Debug\r
318           try\r
319           {\r
320             if (allowedServerExceptions > 0)\r
321             {\r
322               Thread.sleep(5000);\r
323             }\r
324           }\r
325           catch (InterruptedException ex1)\r
326           {\r
327           }\r
328         }\r
329         catch(OutOfMemoryError er)\r
330         {\r
331           jobComplete = true;\r
332           wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_ERROR);\r
333           JOptionPane.showInternalMessageDialog(Desktop.desktop,\r
334               "Out of memory handling result!!"\r
335              +"\nSee help files for increasing Java Virtual Machine memory."\r
336              ,"Out of memory", JOptionPane.WARNING_MESSAGE );\r
337           System.out.println("MsaWSClient: "+er);\r
338           System.gc();\r
339         }\r
340       }\r
341 \r
342       if (allowedServerExceptions == 0)\r
343       {\r
344         wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_SERVERERROR);\r
345       }\r
346       else\r
347       {\r
348         if (result != null)\r
349         {\r
350           if ( !(result.isJobFailed() || result.isServerError()))\r
351           {\r
352             wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_OK);\r
353           }\r
354 \r
355           if (result.isBroken() || result.isFailed())\r
356           {\r
357             wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_ERROR);\r
358           }\r
359 \r
360           if (result.isServerError())\r
361           {\r
362             wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_SERVERERROR);\r
363           }\r
364         }\r
365       }\r
366     }\r
367 \r
368     void StartJob()\r
369     {\r
370       try\r
371       {\r
372         vamsas.objects.simple.WsJobId jobsubmit = server.align(seqs);\r
373 \r
374         if ( (jobsubmit != null) && (jobsubmit.getStatus() == 1))\r
375         {\r
376           jobId = jobsubmit.getJobId();\r
377           System.out.println(WsURL + " Job Id '" + jobId + "'");\r
378         }\r
379         else\r
380         {\r
381           if (jobsubmit == null)\r
382           {\r
383             throw new Exception("Server at " + WsURL +\r
384                                 " returned null object, it probably cannot be contacted. Try again later ?");\r
385           }\r
386 \r
387           throw new Exception(jobsubmit.getJobId());\r
388         }\r
389       }\r
390       catch (Exception e)\r
391       {\r
392         // TODO: JBPNote catch timeout or other fault types explicitly\r
393         // For unexpected errors\r
394         System.err.println(WebServiceName +\r
395                            "Client: Failed to submit the sequences for alignment (probably a server side problem)\n" +\r
396                            "When contacting Server:" + WsURL + "\n" +\r
397                            e.toString() +\r
398                            "\n");\r
399         this.allowedServerExceptions = 0;\r
400         wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_SERVERERROR);\r
401         wsInfo.appendProgressText(\r
402             "Failed to submit sequences for alignment.\n" +\r
403             "It is most likely that there is a problem with the server.\n" +\r
404             "Just close the window\n");\r
405 \r
406         // e.printStackTrace(); // TODO: JBPNote DEBUG\r
407       }\r
408     }\r
409 \r
410     private jalview.datamodel.Sequence[] getVamsasAlignment(\r
411         vamsas.objects.simple.Alignment valign)\r
412     {\r
413       vamsas.objects.simple.Sequence[] seqs = valign.getSeqs().getSeqs();\r
414       jalview.datamodel.Sequence[] msa = new jalview.datamodel.Sequence[seqs.\r
415           length];\r
416 \r
417       for (int i = 0, j = seqs.length; i < j; i++)\r
418       {\r
419         msa[i] = new jalview.datamodel.Sequence(seqs[i].getId(),\r
420                                                 seqs[i].getSeq());\r
421       }\r
422 \r
423       return msa;\r
424     }\r
425 \r
426     void parseResult()\r
427     {\r
428       SequenceI[] seqs = null;\r
429 \r
430       try\r
431       {\r
432         // OutputHeader = output.getText();\r
433         if (result.isFailed())\r
434         {\r
435           OutputHeader += "Job failed.\n";\r
436         }\r
437 \r
438         if (result.getStatus() != null)\r
439         {\r
440           OutputHeader += ("\n" + result.getStatus());\r
441         }\r
442 \r
443         if (result.getMsa() != null)\r
444         {\r
445           OutputHeader += "\nAlignment Object Method Notes\n";\r
446 \r
447           String[] lines = result.getMsa().getMethod();\r
448 \r
449           for (int line = 0; line < lines.length; line++)\r
450           {\r
451             OutputHeader += (lines[line] + "\n");\r
452           }\r
453 \r
454           // JBPNote The returned files from a webservice could be hidden behind icons in the monitor window that, when clicked, pop up their corresponding data\r
455           seqs = getVamsasAlignment(result.getMsa());\r
456         }\r
457 \r
458         wsInfo.setProgressText(OutputHeader);\r
459 \r
460         if (seqs != null)\r
461         {\r
462           AlignmentOrder msaorder = new AlignmentOrder(seqs);\r
463 \r
464           if (preserveOrder)\r
465           {\r
466             jalview.analysis.AlignmentSorter.recoverOrder(seqs);\r
467           }\r
468 \r
469           jalview.analysis.SeqsetUtils.deuniquify(SeqNames, seqs);\r
470 \r
471           Alignment al = new Alignment(seqs);\r
472           if (dataset!=null) {\r
473             al.setDataset(dataset);\r
474           }\r
475           // TODO: JBPNote Should also rename the query sequence sometime...\r
476           AlignFrame af = new AlignFrame(al);\r
477           af.addSortByOrderMenuItem(ServiceName + " Ordering",\r
478                                     msaorder);\r
479 \r
480           Desktop.addInternalFrame(af, alTitle,\r
481                                    AlignFrame.NEW_WINDOW_WIDTH,\r
482                                    AlignFrame.NEW_WINDOW_HEIGHT);\r
483         }\r
484       }\r
485       catch (Exception ex)\r
486       {\r
487         ex.printStackTrace();\r
488       }\r
489     }\r
490   }\r
491 }\r