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