factored out reference to parent alignFrame (so all views can be closed whilst a...
[jalview.git] / src / jalview / ws / WSThread.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer
3  * Copyright (C) 2007 AM Waterhouse, J Procter, G Barton, M Clamp, S Searle
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License
7  * as published by the Free Software Foundation; either version 2
8  * of the License, or (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
18  */
19 package jalview.ws;
20
21 import javax.swing.*;
22
23 import jalview.bin.*;
24 import jalview.datamodel.*;
25 import jalview.gui.*;
26 import jalview.gui.FeatureRenderer.FeatureRendererSettings;
27
28 public abstract class WSThread
29     extends Thread
30 {
31   /**
32    * Generic properties for Web Service Client threads.
33    */
34   AlignmentI currentView = null;
35   FeatureRendererSettings featureSettings = null;
36   WebserviceInfo wsInfo = null;
37   AlignmentView input = null;
38   AlignedCodonFrame[] codonframe = null;
39   boolean jobComplete = false;
40   
41   abstract class WSJob
42   {
43     /**
44      * Generic properties for an individual job within a Web Service Client thread
45      */
46     int jobnum = 0; // WebServiceInfo pane for this job
47     String jobId; // ws job ticket
48     boolean cancelled = false;
49     int allowedServerExceptions = 3; // job dies if too many exceptions.
50     boolean submitted = false;
51     boolean subjobComplete = false;
52     /**
53      *
54      * @return true if job has completed and valid results are available
55      */
56     abstract boolean hasResults();
57
58     /**
59      *
60      * @return boolean true if job can be submitted.
61      */
62     abstract boolean hasValidInput();
63
64     vamsas.objects.simple.Result result;
65   }
66
67   class JobStateSummary
68   {
69     int running = 0;
70     int queuing = 0;
71     int finished = 0;
72     int error = 0;
73     int serror = 0;
74     int cancelled = 0;
75     int results = 0;
76     void updateJobPanelState(WebserviceInfo wsInfo, String OutputHeader,
77                              WSJob j)
78     {
79       if (j.result != null)
80       {
81         String progheader = "";
82         // Parse state of job[j]
83         if (j.result.isRunning())
84         {
85           running++;
86           wsInfo.setStatus(j.jobnum, WebserviceInfo.STATE_RUNNING);
87         }
88         else if (j.result.isQueued())
89         {
90           queuing++;
91           wsInfo.setStatus(j.jobnum, WebserviceInfo.STATE_QUEUING);
92         }
93         else if (j.result.isFinished())
94         {
95           finished++;
96           j.subjobComplete = true;
97           if (j.hasResults())
98           {
99             results++;
100           }
101           wsInfo.setStatus(j.jobnum, WebserviceInfo.STATE_STOPPED_OK);
102         }
103         else if (j.result.isFailed())
104         {
105           progheader += "Job failed.\n";
106           j.subjobComplete = true;
107           wsInfo.setStatus(j.jobnum, WebserviceInfo.STATE_STOPPED_ERROR);
108           error++;
109         }
110         else if (j.result.isServerError())
111         {
112           serror++;
113           j.subjobComplete = true;
114           wsInfo.setStatus(j.jobnum,
115                            WebserviceInfo.STATE_STOPPED_SERVERERROR);
116         }
117         else if (j.result.isBroken() || j.result.isFailed())
118         {
119           error++;
120           j.subjobComplete = true;
121           wsInfo.setStatus(j.jobnum, WebserviceInfo.STATE_STOPPED_ERROR);
122         }
123         // and pass on any sub-job messages to the user
124         wsInfo.setProgressText(j.jobnum, OutputHeader);
125         wsInfo.appendProgressText(j.jobnum, progheader);
126         if (j.result.getStatus() != null)
127         {
128           wsInfo.appendProgressText(j.jobnum, j.result.getStatus());
129         }
130       }
131       else
132       {
133         if (j.submitted && j.subjobComplete)
134         {
135           if (j.allowedServerExceptions == 0)
136           {
137             serror++;
138           }
139           else if (j.result == null)
140           {
141             error++;
142           }
143         }
144       }
145     }
146   }
147
148   WSJob jobs[] = null;
149   String WebServiceName = null;
150   String OutputHeader;
151   String WsUrl = null;
152   abstract void pollJob(WSJob job)
153       throws Exception;
154
155   public void run()
156   {
157     JobStateSummary jstate = null;
158     if (jobs == null)
159     {
160       jobComplete = true;
161     }
162     while (!jobComplete)
163     {
164       jstate = new JobStateSummary();
165       for (int j = 0; j < jobs.length; j++)
166       {
167
168         if (!jobs[j].submitted && jobs[j].hasValidInput())
169         {
170           StartJob(jobs[j]);
171         }
172
173         if (jobs[j].submitted && !jobs[j].subjobComplete)
174         {
175           try
176           {
177             pollJob(jobs[j]);
178             if (jobs[j].result == null)
179             {
180               throw (new Exception(
181                   "Timed out when communicating with server\nTry again later.\n"));
182             }
183             jalview.bin.Cache.log.debug("Job " + j + " Result state " +
184                                         jobs[j].result.getState()
185                                         + "(ServerError=" +
186                                         jobs[j].result.isServerError() + ")");
187           }
188           catch (Exception ex)
189           {
190             // Deal with Transaction exceptions
191             wsInfo.appendProgressText(jobs[j].jobnum, "\n" + WebServiceName
192                                       + " Server exception!\n" + ex.getMessage());
193             Cache.log.warn(WebServiceName + " job(" + jobs[j].jobnum
194                            + ") Server exception: " + ex.getMessage());
195
196             if (jobs[j].allowedServerExceptions > 0)
197             {
198               jobs[j].allowedServerExceptions--;
199               Cache.log.debug("Sleeping after a server exception.");
200               try
201               {
202                 Thread.sleep(5000);
203               }
204               catch (InterruptedException ex1)
205               {
206               }
207             }
208             else
209             {
210               Cache.log.warn("Dropping job " + j + " " + jobs[j].jobId);
211               jobs[j].subjobComplete = true;
212               wsInfo.setStatus(jobs[j].jobnum,
213                                WebserviceInfo.STATE_STOPPED_SERVERERROR);
214             }
215           }
216           catch (OutOfMemoryError er)
217           {
218             jobComplete = true;
219             jobs[j].subjobComplete = true;
220             jobs[j].result = null; // may contain out of date result object
221             wsInfo.setStatus(jobs[j].jobnum,
222                              WebserviceInfo.STATE_STOPPED_ERROR);
223             JOptionPane
224                 .showInternalMessageDialog(
225                     Desktop.desktop,
226                     "Out of memory handling result for job !!"
227                     +
228                     "\nSee help files for increasing Java Virtual Machine memory.",
229                     "Out of memory", JOptionPane.WARNING_MESSAGE);
230             Cache.log.error("Out of memory when retrieving Job " + j + " id:" +
231                             WsUrl + "/" + jobs[j].jobId, er);
232             System.gc();
233           }
234         }
235         jstate.updateJobPanelState(wsInfo, OutputHeader, jobs[j]);
236       }
237       // Decide on overall state based on collected jobs[] states
238       if (jstate.running > 0)
239       {
240         wsInfo.setStatus(WebserviceInfo.STATE_RUNNING);
241       }
242       else if (jstate.queuing > 0)
243       {
244         wsInfo.setStatus(WebserviceInfo.STATE_QUEUING);
245       }
246       else
247       {
248         jobComplete = true;
249         if (jstate.finished > 0)
250         {
251           wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_OK);
252         }
253         else if (jstate.error > 0)
254         {
255           wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_ERROR);
256         }
257         else if (jstate.serror > 0)
258         {
259           wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_SERVERERROR);
260         }
261       }
262       if (!jobComplete)
263       {
264         try
265         {
266           Thread.sleep(5000);
267         }
268         catch (InterruptedException e)
269         {
270           Cache.log.debug("Interrupted sleep waiting for next job poll.", e);
271         }
272         // System.out.println("I'm alive "+alTitle);
273       }
274     }
275     if (jobComplete && jobs != null)
276     {
277       parseResult(); // tidy up and make results available to user
278     }
279     else
280     {
281       Cache.log.debug("WebServiceJob poll loop finished with no jobs created.");
282     }
283   }
284
285   abstract void StartJob(WSJob job);
286
287   abstract void parseResult();
288
289   protected void propagateDatasetMappings(Alignment al)
290   {
291     if (codonframe!=null)
292     {
293       SequenceI[] alignment = al.getSequencesArray();
294       for (int sq = 0; sq<alignment.length; sq++)
295       {
296         for (int i=0; i<codonframe.length; i++)
297         {
298           if (codonframe[i]!=null &&
299                   codonframe[i].involvesSequence(alignment[sq]))
300           {
301             al.addCodonFrame(codonframe[i]);
302             codonframe[i] = null;
303             break;
304           }
305         }
306       }
307     }
308   }
309
310   /**
311    * 
312    * @param alignFrame reference for copying mappings across
313    * @param wsInfo gui attachment point
314    * @param input input data for the calculation
315    * @param webServiceName name of service
316    * @param wsUrl  url of the service being invoked
317    */
318   public WSThread(AlignFrame alignFrame, WebserviceInfo wsinfo,
319           AlignmentView input, String webServiceName,
320           String wsUrl)
321   {
322     this(alignFrame, wsinfo, input, wsUrl);
323     WebServiceName = webServiceName;
324   }
325   char defGapChar = '-';
326   /**
327    * 
328    * @return gap character to use for any alignment generation
329    */
330   public char getGapChar()
331   {
332     return defGapChar;
333   }
334
335   /**
336    * 
337    * @param alframe - reference for copying mappings and display styles across
338    * @param wsinfo2 - gui attachment point
339    * @param alview - input data for the calculation
340    * @param wsurl2 - url of the service being invoked
341    */
342   public WSThread(AlignFrame alframe, WebserviceInfo wsinfo2,
343           AlignmentView alview, String wsurl2)
344   {
345     super();
346     // this.alignFrame = alframe;
347     currentView = alframe.getCurrentView().getAlignment();
348     featureSettings = alframe.getFeatureRenderer().getSettings();
349     defGapChar = alframe.getViewport().getGapCharacter();
350     this.wsInfo = wsinfo2;
351     this.input = alview;
352     WsUrl = wsurl2;
353     if (alframe!=null)
354     {
355       AlignedCodonFrame[] cf = alframe.getViewport().getAlignment().getCodonFrames();
356       if (cf!=null)
357       {
358         codonframe = new AlignedCodonFrame[cf.length];
359         System.arraycopy(cf, 0, codonframe, 0, cf.length);
360       }
361     }
362   }
363 }