use OOMwarning to warn user when out of Memory occurs
[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             Cache.log.error("Out of memory when retrieving Job " + j + " id:" +\r
297                             WsUrl + "/" + jobs[j].jobId, er);\r
298             new jalview.gui.OOMWarning("retrieving result for "+WebServiceName,er);\r
299             System.gc();\r
300           }\r
301         }\r
302         jstate.updateJobPanelState(wsInfo, OutputHeader, jobs[j]);\r
303       }\r
304       // Decide on overall state based on collected jobs[] states\r
305       if (jstate.running > 0)\r
306       {\r
307         wsInfo.setStatus(WebserviceInfo.STATE_RUNNING);\r
308       }\r
309       else if (jstate.queuing > 0)\r
310       {\r
311         wsInfo.setStatus(WebserviceInfo.STATE_QUEUING);\r
312       }\r
313       else\r
314       {\r
315         jobComplete = true;\r
316         if (jstate.finished > 0)\r
317         {\r
318           wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_OK);\r
319         }\r
320         else if (jstate.error > 0)\r
321         {\r
322           wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_ERROR);\r
323         }\r
324         else if (jstate.serror > 0)\r
325         {\r
326           wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_SERVERERROR);\r
327         }\r
328       }\r
329       if (!jobComplete)\r
330       {\r
331         try\r
332         {\r
333           Thread.sleep(5000);\r
334         }\r
335         catch (InterruptedException e)\r
336         {\r
337           Cache.log.debug("Interrupted sleep waiting for next job poll.", e);\r
338         }\r
339         // System.out.println("I'm alive "+alTitle);\r
340       }\r
341     }\r
342     if (jobComplete && jobs != null)\r
343     {\r
344       parseResult(); // tidy up and make results available to user\r
345     }\r
346     else\r
347     {\r
348       Cache.log.debug("WebServiceJob poll loop finished with no jobs created.");\r
349       wsInfo.setFinishedNoResults();\r
350     }\r
351   }\r
352 \r
353   /**\r
354    * submit job to web service\r
355    * @param job\r
356    */\r
357   abstract void StartJob(WSJob job);\r
358 \r
359   /**\r
360    * process the set of WSJob objects into a set of results, and tidy up.\r
361    */\r
362   abstract void parseResult();\r
363 \r
364   /**\r
365    * helper function to conserve dataset references to sequence objects returned from web services\r
366    * 1. Propagates AlCodonFrame data from <code>codonframe</code> to <code>al</code>\r
367    * @param al\r
368    */\r
369   protected void propagateDatasetMappings(Alignment al)\r
370   {\r
371     if (codonframe!=null)\r
372     {\r
373       SequenceI[] alignment = al.getSequencesArray();\r
374       for (int sq = 0; sq<alignment.length; sq++)\r
375       {\r
376         for (int i=0; i<codonframe.length; i++)\r
377         {\r
378           if (codonframe[i]!=null &&\r
379                   codonframe[i].involvesSequence(alignment[sq]))\r
380           {\r
381             al.addCodonFrame(codonframe[i]);\r
382             codonframe[i] = null;\r
383             break;\r
384           }\r
385         }\r
386       }\r
387     }\r
388   }\r
389 \r
390   /**\r
391    * \r
392    * @param alignFrame reference for copying mappings across\r
393    * @param wsInfo gui attachment point\r
394    * @param input input data for the calculation\r
395    * @param webServiceName name of service\r
396    * @param wsUrl  url of the service being invoked\r
397    */\r
398   public WSThread(AlignFrame alignFrame, WebserviceInfo wsinfo,\r
399           AlignmentView input, String webServiceName,\r
400           String wsUrl)\r
401   {\r
402     this(alignFrame, wsinfo, input, wsUrl);\r
403     WebServiceName = webServiceName;\r
404   }\r
405   char defGapChar = '-';\r
406   /**\r
407    * \r
408    * @return gap character to use for any alignment generation\r
409    */\r
410   public char getGapChar()\r
411   {\r
412     return defGapChar;\r
413   }\r
414 \r
415   /**\r
416    * \r
417    * @param alframe - reference for copying mappings and display styles across\r
418    * @param wsinfo2 - gui attachment point\r
419    * @param alview - input data for the calculation\r
420    * @param wsurl2 - url of the service being invoked\r
421    */\r
422   public WSThread(AlignFrame alframe, WebserviceInfo wsinfo2,\r
423           AlignmentView alview, String wsurl2)\r
424   {\r
425     super();\r
426     // this.alignFrame = alframe;\r
427     currentView = alframe.getCurrentView().getAlignment();\r
428     featureSettings = alframe.getFeatureRenderer().getSettings();\r
429     defGapChar = alframe.getViewport().getGapCharacter();\r
430     this.wsInfo = wsinfo2;\r
431     this.input = alview;\r
432     WsUrl = wsurl2;\r
433     if (alframe!=null)\r
434     {\r
435       AlignedCodonFrame[] cf = alframe.getViewport().getAlignment().getCodonFrames();\r
436       if (cf!=null)\r
437       {\r
438         codonframe = new AlignedCodonFrame[cf.length];\r
439         System.arraycopy(cf, 0, codonframe, 0, cf.length);\r
440       }\r
441     }\r
442   }\r
443 }\r