94e19e794c1efd0d1f4a5d2fb29514a5f94d5d05
[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 org.apache.axis.client.*;\r
30 \r
31 import vamsas.objects.*;\r
32 \r
33 import java.awt.*;\r
34 \r
35 import java.util.*;\r
36 \r
37 import javax.swing.*;\r
38 \r
39 import javax.xml.namespace.QName;\r
40 \r
41 \r
42 public class MsaWSClient extends WSClient {\r
43     /**\r
44  * server is a WSDL2Java generated stub for an archetypal MsaWSI service.\r
45  */\r
46     ext.vamsas.MuscleWS server;\r
47 \r
48     public MsaWSClient(String MsaWSName, String altitle, SequenceI[] msa,\r
49         boolean submitGaps, boolean preserveOrder) {\r
50         if (setWebService(MsaWSName) == false) {\r
51             JOptionPane.showMessageDialog(Desktop.desktop,\r
52                 "The Multiple Sequence Alignment Service named " + MsaWSName +\r
53                 " is unknown", "Internal Jalview Error",\r
54                 JOptionPane.WARNING_MESSAGE);\r
55 \r
56             return;\r
57         }\r
58 \r
59         wsInfo = new jalview.gui.WebserviceInfo(WebServiceJobTitle,\r
60                 WebServiceReference);\r
61 \r
62         if (!locateWebService()) {\r
63             return;\r
64         }\r
65 \r
66         wsInfo.setProgressText(((submitGaps) ? "Re-alignment" : "Alignment") +\r
67             " of " + altitle + "\nJob details\n");\r
68 \r
69         MsaWSThread musclethread = new MsaWSThread(WebServiceName +\r
70                 " alignment of " + altitle, msa, submitGaps, preserveOrder);\r
71         wsInfo.setthisService(musclethread);\r
72         musclethread.start();\r
73     }\r
74 \r
75     // JBPNote Nasty object-global state setting methods shouldn't be allowed\r
76     private boolean setWebService(String MsaWSName) {\r
77         if (MsaWServices.info.containsKey(MsaWSName)) {\r
78             WebServiceName = MsaWSName;\r
79 \r
80             String[] wsinfo = (String[]) MsaWServices.info.get(MsaWSName);\r
81             WsURL = wsinfo[0];\r
82             WebServiceJobTitle = wsinfo[1];\r
83             WebServiceReference = wsinfo[2];\r
84 \r
85             return true;\r
86         } else {\r
87             return false;\r
88         }\r
89     }\r
90 \r
91     private boolean locateWebService() {\r
92         // TODO: MuscleWS transmuted to generic MsaWS client\r
93         MuscleWSServiceLocator loc = new MuscleWSServiceLocator(); // Default\r
94 \r
95         try {\r
96             this.server = (MuscleWS) loc.getMuscleWS(new java.net.URL(WsURL));\r
97             ((MuscleWSSoapBindingStub) this.server).setTimeout(60000); // One minute timeout\r
98         } catch (Exception ex) {\r
99             wsInfo.setProgressText("Serious! " + WebServiceName +\r
100                 " Service location failed\nfor URL :" + WsURL + "\n" +\r
101                 ex.getMessage());\r
102             wsInfo.setStatus(wsInfo.ERROR);\r
103             ex.printStackTrace();\r
104 \r
105             return false;\r
106         }\r
107 \r
108         loc.getEngine().setOption("axis", "1");\r
109 \r
110         return true;\r
111     }\r
112 \r
113     protected class MsaWSThread extends Thread implements WSClientI {\r
114         String ServiceName = WebServiceName;\r
115         String OutputHeader;\r
116         vamsas.objects.simple.MsaResult result = null;\r
117         vamsas.objects.simple.SequenceSet seqs = new vamsas.objects.simple.SequenceSet();\r
118         Hashtable SeqNames = null;\r
119         boolean submitGaps = false; // and always store and recover sequence order\r
120         boolean preserveOrder = true; // and always store and recover sequence order\r
121         String jobId;\r
122         String alTitle; // name which will be used to form new alignment window.\r
123         int allowedServerExceptions = 3; // thread dies if too many exceptions.\r
124         boolean jobComplete = false;\r
125 \r
126         MsaWSThread(String title, SequenceI[] msa, boolean subgaps,\r
127             boolean presorder) {\r
128             alTitle = title;\r
129             submitGaps = subgaps;\r
130             preserveOrder = presorder;\r
131 \r
132             OutputHeader = wsInfo.getProgressText();\r
133             SeqNames = new Hashtable();\r
134 \r
135             vamsas.objects.simple.Sequence[] seqarray = new vamsas.objects.simple.Sequence[msa.length];\r
136 \r
137             for (int i = 0; i < msa.length; i++) {\r
138                 String newname = jalview.analysis.SeqsetUtils.unique_name(i);\r
139 \r
140                 // uniquify as we go\r
141                 // TODO: JBPNote: this is a ubiquitous transformation - set of jalview seq objects to vamsas sequences with name preservation\r
142                 SeqNames.put(newname,\r
143                     jalview.analysis.SeqsetUtils.SeqCharacterHash(msa[i]));\r
144                 seqarray[i] = new vamsas.objects.simple.Sequence();\r
145                 seqarray[i].setId(newname);\r
146                 seqarray[i].setSeq((submitGaps) ? msa[i].getSequence()\r
147                                                 : AlignSeq.extractGaps(\r
148                         jalview.util.Comparison.GapChars, msa[i].getSequence()));\r
149             }\r
150 \r
151             this.seqs = new vamsas.objects.simple.SequenceSet();\r
152             this.seqs.setSeqs(seqarray);\r
153         }\r
154 \r
155         public boolean isCancellable() {\r
156             return true;\r
157         }\r
158 \r
159         public void cancelJob() {\r
160             if ((jobId != null) && !jobId.equals("") && !jobComplete) {\r
161                 String cancelledMessage = "";\r
162 \r
163                 try {\r
164                     vamsas.objects.simple.WsJobId cancelledJob = server.cancel(jobId);\r
165 \r
166                     if (cancelledJob.getStatus() == 2) {\r
167                         // CANCELLED_JOB\r
168                         cancelledMessage = "Job cancelled.";\r
169                         wsInfo.setStatus(WebserviceInfo.STATE_CANCELLED_OK);\r
170                         jobComplete = true;\r
171                         jobsRunning--;\r
172                         result = null;\r
173                     } else if (cancelledJob.getStatus() == 3) {\r
174                         // VALID UNSTOPPABLE JOB\r
175                         cancelledMessage += "Server cannot cancel this job. just close the window.\n";\r
176                     }\r
177 \r
178                     if (cancelledJob.getJobId() != null) {\r
179                         cancelledMessage += ("[" + cancelledJob.getJobId() +\r
180                         "]");\r
181                     }\r
182 \r
183                     cancelledMessage += "\n";\r
184                 } catch (Exception exc) {\r
185                     cancelledMessage += ("\nProblems cancelling the job : Exception received...\n" +\r
186                     exc + "\n");\r
187                     exc.printStackTrace();\r
188                 }\r
189 \r
190                 wsInfo.setProgressText(OutputHeader + cancelledMessage + "\n");\r
191             } else {\r
192                 if (!jobComplete) {\r
193                     wsInfo.setProgressText(OutputHeader +\r
194                         "Server cannot cancel this job because it has not been submitted properly. just close the window.\n");\r
195                 }\r
196             }\r
197         }\r
198 \r
199         public void run() {\r
200             StartJob();\r
201 \r
202             while (!jobComplete && (allowedServerExceptions > 0)) {\r
203                 try {\r
204                     if ((result = server.getResult(jobId)) == null) {\r
205                         throw (new Exception(\r
206                             "Timed out when communicating with server\nTry again later.\n"));\r
207                     }\r
208 \r
209                     if (result.isRunning()) {\r
210                         wsInfo.setStatus(WebserviceInfo.STATE_RUNNING);\r
211                     } else if (result.isQueued()) {\r
212                         wsInfo.setStatus(WebserviceInfo.STATE_QUEUING);\r
213                     }\r
214 \r
215                     if (result.isFinished()) {\r
216                         parseResult();\r
217                         jobComplete = true;\r
218                         jobsRunning--;\r
219                     } else {\r
220                         if (result.getStatus() != null) {\r
221                             wsInfo.setProgressText(OutputHeader + "\n" +\r
222                                 result.getStatus());\r
223                         }\r
224 \r
225                         if (!(result.isJobFailed() || result.isServerError())) {\r
226                             Thread.sleep(5000);\r
227 \r
228                             //  System.out.println("I'm alive "+seqid+" "+jobid);\r
229                         } else {\r
230                             break;\r
231                         }\r
232                     }\r
233                 } catch (Exception ex) {\r
234                     allowedServerExceptions--;\r
235                     wsInfo.appendProgressText("\n" + ServiceName +\r
236                         " Server exception!\n" + ex.getMessage());\r
237                     System.err.println(ServiceName + " Server exception: " +\r
238                         ex.getMessage());\r
239 \r
240                     //          ex.printStackTrace(); JBPNote Debug\r
241                     try {\r
242                         if (allowedServerExceptions > 0) {\r
243                             Thread.sleep(5000);\r
244                         }\r
245                     } catch (InterruptedException ex1) {\r
246                     }\r
247                 }\r
248             }\r
249 \r
250             if (allowedServerExceptions == 0) {\r
251                 wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_SERVERERROR);\r
252             } else {\r
253                 if (!((result != null) &&\r
254                         (result.isJobFailed() || result.isServerError()))) {\r
255                     wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_OK);\r
256                 } else {\r
257                     if (result.isFailed()) {\r
258                         wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_ERROR);\r
259                     }\r
260 \r
261                     if (result.isServerError()) {\r
262                         wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_SERVERERROR);\r
263                     }\r
264                 }\r
265             }\r
266         }\r
267 \r
268         void StartJob() {\r
269             try {\r
270                 vamsas.objects.simple.WsJobId jobsubmit = server.align(seqs);\r
271 \r
272                 if ((jobsubmit != null) && (jobsubmit.getStatus() == 1)) {\r
273                     jobId = jobsubmit.getJobId();\r
274                     System.out.println(WsURL + " Job Id '" + jobId + "'");\r
275                 } else {\r
276                     if (jobsubmit == null) {\r
277                         throw new Exception("Server at " + WsURL +\r
278                             " returned null object, it probably cannot be contacted. Try again later ?");\r
279                     }\r
280 \r
281                     throw new Exception(jobsubmit.getJobId());\r
282                 }\r
283             } catch (Exception e) {\r
284                 // TODO: JBPNote catch timeout or other fault types explicitly\r
285                 // For unexpected errors\r
286                 System.err.println(WebServiceName +\r
287                     "Client: Failed to submit the sequences for alignment (probably a server side problem)\n"\r
288                     + "When contacting Server:"+WsURL + "\n" + e.toString() + "\n");\r
289                 this.allowedServerExceptions = 0;\r
290                 wsInfo.setStatus(wsInfo.STATE_STOPPED_SERVERERROR);\r
291                 wsInfo.appendProgressText("Failed to submit sequences for alignment.\n"\r
292                     + "It is most likely that there is a problem with the server.\n"\r
293                     + "Just close the window\n");\r
294 \r
295                 // e.printStackTrace(); // TODO: JBPNote DEBUG\r
296             }\r
297         }\r
298 \r
299         private void addFloatAnnotations(Alignment al, int[] gapmap,\r
300             Vector values, String Symname, String Visname, float min,\r
301             float max, int winLength) {\r
302             Annotation[] annotations = new Annotation[al.getWidth()];\r
303 \r
304             for (int j = 0; j < values.size(); j++) {\r
305                 float value = Float.parseFloat(values.get(j).toString());\r
306                 annotations[gapmap[j]] = new Annotation("", value + "", ' ',\r
307                         value);\r
308             }\r
309 \r
310             al.addAnnotation(new AlignmentAnnotation(Symname, Visname,\r
311                     annotations, min, max, winLength));\r
312         }\r
313 \r
314         private jalview.datamodel.Sequence[] getVamsasAlignment(\r
315             vamsas.objects.simple.Alignment valign) {\r
316             vamsas.objects.simple.Sequence[] seqs = valign.getSeqs().getSeqs();\r
317             jalview.datamodel.Sequence[] msa = new jalview.datamodel.Sequence[seqs.length];\r
318 \r
319             for (int i = 0, j = seqs.length; i < j; i++)\r
320                 msa[i] = new jalview.datamodel.Sequence(seqs[i].getId(),\r
321                         seqs[i].getSeq());\r
322 \r
323             return msa;\r
324         }\r
325 \r
326         void parseResult() {\r
327             SequenceI[] seqs = null;\r
328 \r
329             try {\r
330                 // OutputHeader = output.getText();\r
331                 if (result.isFailed()) {\r
332                     OutputHeader += "Job failed.\n";\r
333                 }\r
334 \r
335                 if (result.getStatus() != null) {\r
336                     OutputHeader += ("\n" + result.getStatus());\r
337                 }\r
338 \r
339                 if (result.getMsa() != null) {\r
340                     OutputHeader += "\nAlignment Object Method Notes\n";\r
341 \r
342                     String[] lines = result.getMsa().getMethod();\r
343 \r
344                     for (int line = 0; line < lines.length; line++)\r
345                         OutputHeader += (lines[line] + "\n");\r
346 \r
347                     // 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
348                     seqs = getVamsasAlignment(result.getMsa());\r
349                 }\r
350 \r
351                 wsInfo.setProgressText(OutputHeader);\r
352 \r
353                 if (seqs != null) {\r
354                     AlignmentOrder msaorder = new AlignmentOrder(seqs);\r
355 \r
356                     if (preserveOrder) {\r
357                         jalview.analysis.AlignmentSorter.recoverOrder(seqs);\r
358                     }\r
359 \r
360                     jalview.analysis.SeqsetUtils.deuniquify(SeqNames, seqs);\r
361 \r
362                     Alignment al = new Alignment(seqs);\r
363 \r
364                     // TODO: JBPNote Should also rename the query sequence sometime...\r
365                     AlignFrame af = new AlignFrame(al);\r
366                     af.addSortByOrderMenuItem(ServiceName + " Ordering",\r
367                         msaorder);\r
368 \r
369                     Desktop.addInternalFrame(af, alTitle,\r
370                         AlignFrame.NEW_WINDOW_WIDTH,\r
371                         AlignFrame.NEW_WINDOW_HEIGHT);\r
372                 }\r
373             } catch (Exception ex) {\r
374                 ex.printStackTrace();\r
375             }\r
376         }\r
377     }\r
378 }\r