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