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