jalview 2.5 release banner
[jalview.git] / src / jalview / ws / WSThread.java
1 /*\r
2  * Jalview - A Sequence Alignment Editor and Viewer (Version 2.5)\r
3  * Copyright (C) 2010 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;\r
19 \r
20 import javax.swing.*;\r
21 \r
22 import jalview.bin.*;\r
23 import jalview.datamodel.*;\r
24 import jalview.gui.*;\r
25 import jalview.gui.FeatureRenderer.FeatureRendererSettings;\r
26 \r
27 public abstract class WSThread extends Thread\r
28 {\r
29   /**\r
30    * Generic properties for Web Service Client threads.\r
31    */\r
32   /**\r
33    * view that this job was associated with\r
34    */\r
35   AlignmentI currentView = null;\r
36 \r
37   /**\r
38    * feature settings from view that job was associated with\r
39    */\r
40   FeatureRendererSettings featureSettings = null;\r
41 \r
42   /**\r
43    * metadata about this web service\r
44    */\r
45   WebserviceInfo wsInfo = null;\r
46 \r
47   /**\r
48    * original input data for this job\r
49    */\r
50   AlignmentView input = null;\r
51 \r
52   /**\r
53    * dataset sequence relationships to be propagated onto new results\r
54    */\r
55   AlignedCodonFrame[] codonframe = null;\r
56 \r
57   /**\r
58    * are there jobs still running in this thread.\r
59    */\r
60   boolean jobComplete = false;\r
61 \r
62   abstract class WSJob\r
63   {\r
64     /**\r
65      * Generic properties for an individual job within a Web Service Client\r
66      * thread\r
67      */\r
68     int jobnum = 0; // WebServiceInfo pane for this job\r
69 \r
70     String jobId; // ws job ticket\r
71 \r
72     /**\r
73      * has job been cancelled\r
74      */\r
75     boolean cancelled = false;\r
76 \r
77     /**\r
78      * number of exceptions left before job dies\r
79      */\r
80     int allowedServerExceptions = 3;\r
81 \r
82     /**\r
83      * has job been submitted\r
84      */\r
85     boolean submitted = false;\r
86 \r
87     /**\r
88      * are all sub-jobs complete\r
89      */\r
90     boolean subjobComplete = false;\r
91 \r
92     /**\r
93      * \r
94      * @return true if job has completed and valid results are available\r
95      */\r
96     abstract boolean hasResults();\r
97 \r
98     /**\r
99      * \r
100      * @return boolean true if job can be submitted.\r
101      */\r
102     abstract boolean hasValidInput();\r
103 \r
104     /**\r
105      * The last result object returned by the service.\r
106      */\r
107     vamsas.objects.simple.Result result;\r
108   }\r
109 \r
110   class JobStateSummary\r
111   {\r
112     /**\r
113      * number of jobs running\r
114      */\r
115     int running = 0;\r
116 \r
117     /**\r
118      * number of jobs queued\r
119      */\r
120     int queuing = 0;\r
121 \r
122     /**\r
123      * number of jobs finished\r
124      */\r
125     int finished = 0;\r
126 \r
127     /**\r
128      * number of jobs failed\r
129      */\r
130     int error = 0;\r
131 \r
132     /**\r
133      * number of jobs stopped due to server error\r
134      */\r
135     int serror = 0;\r
136 \r
137     /**\r
138      * number of jobs cancelled\r
139      */\r
140     int cancelled = 0;\r
141 \r
142     /**\r
143      * number of jobs finished with results\r
144      */\r
145     int results = 0;\r
146 \r
147     /**\r
148      * processes WSJob and updates job status counters and WebService status\r
149      * displays\r
150      * \r
151      * @param wsInfo\r
152      * @param OutputHeader\r
153      * @param j\r
154      */\r
155     void updateJobPanelState(WebserviceInfo wsInfo, String OutputHeader,\r
156             WSJob j)\r
157     {\r
158       if (j.result != null)\r
159       {\r
160         String progheader = "";\r
161         // Parse state of job[j]\r
162         if (j.result.isRunning())\r
163         {\r
164           running++;\r
165           wsInfo.setStatus(j.jobnum, WebserviceInfo.STATE_RUNNING);\r
166         }\r
167         else if (j.result.isQueued())\r
168         {\r
169           queuing++;\r
170           wsInfo.setStatus(j.jobnum, WebserviceInfo.STATE_QUEUING);\r
171         }\r
172         else if (j.result.isFinished())\r
173         {\r
174           finished++;\r
175           j.subjobComplete = true;\r
176           if (j.hasResults())\r
177           {\r
178             results++;\r
179           }\r
180           wsInfo.setStatus(j.jobnum, WebserviceInfo.STATE_STOPPED_OK);\r
181         }\r
182         else if (j.result.isFailed())\r
183         {\r
184           progheader += "Job failed.\n";\r
185           j.subjobComplete = true;\r
186           wsInfo.setStatus(j.jobnum, WebserviceInfo.STATE_STOPPED_ERROR);\r
187           error++;\r
188         }\r
189         else if (j.result.isServerError())\r
190         {\r
191           serror++;\r
192           j.subjobComplete = true;\r
193           wsInfo.setStatus(j.jobnum,\r
194                   WebserviceInfo.STATE_STOPPED_SERVERERROR);\r
195         }\r
196         else if (j.result.isBroken() || j.result.isFailed())\r
197         {\r
198           error++;\r
199           j.subjobComplete = true;\r
200           wsInfo.setStatus(j.jobnum, WebserviceInfo.STATE_STOPPED_ERROR);\r
201         }\r
202         // and pass on any sub-job messages to the user\r
203         wsInfo.setProgressText(j.jobnum, OutputHeader);\r
204         wsInfo.appendProgressText(j.jobnum, progheader);\r
205         if (j.result.getStatus() != null)\r
206         {\r
207           wsInfo.appendProgressText(j.jobnum, j.result.getStatus());\r
208         }\r
209       }\r
210       else\r
211       {\r
212         if (j.submitted && j.subjobComplete)\r
213         {\r
214           if (j.allowedServerExceptions == 0)\r
215           {\r
216             serror++;\r
217           }\r
218           else if (j.result == null)\r
219           {\r
220             error++;\r
221           }\r
222         }\r
223       }\r
224     }\r
225   }\r
226 \r
227   /**\r
228    * one or more jobs being managed by this thread.\r
229    */\r
230   WSJob jobs[] = null;\r
231 \r
232   /**\r
233    * full name of service\r
234    */\r
235   String WebServiceName = null;\r
236 \r
237   String OutputHeader;\r
238 \r
239   String WsUrl = null;\r
240 \r
241   /**\r
242    * query web service for status of job. on return, job.result must not be null\r
243    * - if it is then it will be assumed that the job status query timed out and\r
244    * a server exception will be logged.\r
245    * \r
246    * @param job\r
247    * @throws Exception\r
248    *           will be logged as a server exception for this job\r
249    */\r
250   abstract void pollJob(WSJob job) throws Exception;\r
251 \r
252   public void run()\r
253   {\r
254     JobStateSummary jstate = null;\r
255     if (jobs == null)\r
256     {\r
257       jobComplete = true;\r
258     }\r
259     while (!jobComplete)\r
260     {\r
261       jstate = new JobStateSummary();\r
262       for (int j = 0; j < jobs.length; j++)\r
263       {\r
264 \r
265         if (!jobs[j].submitted && jobs[j].hasValidInput())\r
266         {\r
267           StartJob(jobs[j]);\r
268         }\r
269 \r
270         if (jobs[j].submitted && !jobs[j].subjobComplete)\r
271         {\r
272           try\r
273           {\r
274             pollJob(jobs[j]);\r
275             if (jobs[j].result == null)\r
276             {\r
277               throw (new Exception(\r
278                       "Timed out when communicating with server\nTry again later.\n"));\r
279             }\r
280             jalview.bin.Cache.log.debug("Job " + j + " Result state "\r
281                     + jobs[j].result.getState() + "(ServerError="\r
282                     + jobs[j].result.isServerError() + ")");\r
283           } catch (Exception ex)\r
284           {\r
285             // Deal with Transaction exceptions\r
286             wsInfo.appendProgressText(jobs[j].jobnum, "\n" + WebServiceName\r
287                     + " Server exception!\n" + ex.getMessage());\r
288             Cache.log.warn(WebServiceName + " job(" + jobs[j].jobnum\r
289                     + ") Server exception: " + ex.getMessage());\r
290 \r
291             if (jobs[j].allowedServerExceptions > 0)\r
292             {\r
293               jobs[j].allowedServerExceptions--;\r
294               Cache.log.debug("Sleeping after a server exception.");\r
295               try\r
296               {\r
297                 Thread.sleep(5000);\r
298               } catch (InterruptedException ex1)\r
299               {\r
300               }\r
301             }\r
302             else\r
303             {\r
304               Cache.log.warn("Dropping job " + j + " " + jobs[j].jobId);\r
305               jobs[j].subjobComplete = true;\r
306               wsInfo.setStatus(jobs[j].jobnum,\r
307                       WebserviceInfo.STATE_STOPPED_SERVERERROR);\r
308             }\r
309           } catch (OutOfMemoryError er)\r
310           {\r
311             jobComplete = true;\r
312             jobs[j].subjobComplete = true;\r
313             jobs[j].result = null; // may contain out of date result object\r
314             wsInfo.setStatus(jobs[j].jobnum,\r
315                     WebserviceInfo.STATE_STOPPED_ERROR);\r
316             Cache.log.error("Out of memory when retrieving Job " + j\r
317                     + " id:" + WsUrl + "/" + jobs[j].jobId, er);\r
318             new jalview.gui.OOMWarning("retrieving result for "\r
319                     + WebServiceName, er);\r
320             System.gc();\r
321           }\r
322         }\r
323         jstate.updateJobPanelState(wsInfo, OutputHeader, jobs[j]);\r
324       }\r
325       // Decide on overall state based on collected jobs[] states\r
326       if (jstate.running > 0)\r
327       {\r
328         wsInfo.setStatus(WebserviceInfo.STATE_RUNNING);\r
329       }\r
330       else if (jstate.queuing > 0)\r
331       {\r
332         wsInfo.setStatus(WebserviceInfo.STATE_QUEUING);\r
333       }\r
334       else\r
335       {\r
336         jobComplete = true;\r
337         if (jstate.finished > 0)\r
338         {\r
339           wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_OK);\r
340         }\r
341         else if (jstate.error > 0)\r
342         {\r
343           wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_ERROR);\r
344         }\r
345         else if (jstate.serror > 0)\r
346         {\r
347           wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_SERVERERROR);\r
348         }\r
349       }\r
350       if (!jobComplete)\r
351       {\r
352         try\r
353         {\r
354           Thread.sleep(5000);\r
355         } catch (InterruptedException e)\r
356         {\r
357           Cache.log\r
358                   .debug("Interrupted sleep waiting for next job poll.", e);\r
359         }\r
360         // System.out.println("I'm alive "+alTitle);\r
361       }\r
362     }\r
363     if (jobComplete && jobs != null)\r
364     {\r
365       parseResult(); // tidy up and make results available to user\r
366     }\r
367     else\r
368     {\r
369       Cache.log\r
370               .debug("WebServiceJob poll loop finished with no jobs created.");\r
371       wsInfo.setFinishedNoResults();\r
372     }\r
373   }\r
374 \r
375   /**\r
376    * submit job to web service\r
377    * \r
378    * @param job\r
379    */\r
380   abstract void StartJob(WSJob job);\r
381 \r
382   /**\r
383    * process the set of WSJob objects into a set of results, and tidy up.\r
384    */\r
385   abstract void parseResult();\r
386 \r
387   /**\r
388    * helper function to conserve dataset references to sequence objects returned\r
389    * from web services 1. Propagates AlCodonFrame data from\r
390    * <code>codonframe</code> to <code>al</code>\r
391    * \r
392    * @param al\r
393    */\r
394   protected void propagateDatasetMappings(Alignment al)\r
395   {\r
396     if (codonframe != null)\r
397     {\r
398       SequenceI[] alignment = al.getSequencesArray();\r
399       for (int sq = 0; sq < alignment.length; sq++)\r
400       {\r
401         for (int i = 0; i < codonframe.length; i++)\r
402         {\r
403           if (codonframe[i] != null\r
404                   && codonframe[i].involvesSequence(alignment[sq]))\r
405           {\r
406             al.addCodonFrame(codonframe[i]);\r
407             codonframe[i] = null;\r
408             break;\r
409           }\r
410         }\r
411       }\r
412     }\r
413   }\r
414 \r
415   /**\r
416    * \r
417    * @param alignFrame\r
418    *          reference for copying mappings across\r
419    * @param wsInfo\r
420    *          gui attachment point\r
421    * @param input\r
422    *          input data for the calculation\r
423    * @param webServiceName\r
424    *          name of service\r
425    * @param wsUrl\r
426    *          url of the service being invoked\r
427    */\r
428   public WSThread(AlignFrame alignFrame, WebserviceInfo wsinfo,\r
429           AlignmentView input, String webServiceName, String wsUrl)\r
430   {\r
431     this(alignFrame, wsinfo, input, wsUrl);\r
432     WebServiceName = webServiceName;\r
433   }\r
434 \r
435   char defGapChar = '-';\r
436 \r
437   /**\r
438    * \r
439    * @return gap character to use for any alignment generation\r
440    */\r
441   public char getGapChar()\r
442   {\r
443     return defGapChar;\r
444   }\r
445 \r
446   /**\r
447    * \r
448    * @param alframe\r
449    *          - reference for copying mappings and display styles across\r
450    * @param wsinfo2\r
451    *          - gui attachment point\r
452    * @param alview\r
453    *          - input data for the calculation\r
454    * @param wsurl2\r
455    *          - url of the service being invoked\r
456    */\r
457   public WSThread(AlignFrame alframe, WebserviceInfo wsinfo2,\r
458           AlignmentView alview, String wsurl2)\r
459   {\r
460     super();\r
461     // this.alignFrame = alframe;\r
462     currentView = alframe.getCurrentView().getAlignment();\r
463     featureSettings = alframe.getFeatureRenderer().getSettings();\r
464     defGapChar = alframe.getViewport().getGapCharacter();\r
465     this.wsInfo = wsinfo2;\r
466     this.input = alview;\r
467     WsUrl = wsurl2;\r
468     if (alframe != null)\r
469     {\r
470       AlignedCodonFrame[] cf = alframe.getViewport().getAlignment()\r
471               .getCodonFrames();\r
472       if (cf != null)\r
473       {\r
474         codonframe = new AlignedCodonFrame[cf.length];\r
475         System.arraycopy(cf, 0, codonframe, 0, cf.length);\r
476       }\r
477     }\r
478   }\r
479 }\r