bugfix for alignments without aligned codon frames
[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
27 public abstract class WSThread
28     extends Thread
29 {
30   /**
31    * Generic properties for Web Service Client threads.
32    */
33   AlignFrame alignFrame = null;
34   WebserviceInfo wsInfo = null;
35   AlignmentView input = null;
36   AlignedCodonFrame[] codonframe = null;
37   boolean jobComplete = false;
38   
39   abstract class WSJob
40   {
41     /**
42      * Generic properties for an individual job within a Web Service Client thread
43      */
44     int jobnum = 0; // WebServiceInfo pane for this job
45     String jobId; // ws job ticket
46     boolean cancelled = false;
47     int allowedServerExceptions = 3; // job dies if too many exceptions.
48     boolean submitted = false;
49     boolean subjobComplete = false;
50     /**
51      *
52      * @return true if job has completed and valid results are available
53      */
54     abstract boolean hasResults();
55
56     /**
57      *
58      * @return boolean true if job can be submitted.
59      */
60     abstract boolean hasValidInput();
61
62     vamsas.objects.simple.Result result;
63   }
64
65   class JobStateSummary
66   {
67     int running = 0;
68     int queuing = 0;
69     int finished = 0;
70     int error = 0;
71     int serror = 0;
72     int cancelled = 0;
73     int results = 0;
74     void updateJobPanelState(WebserviceInfo wsInfo, String OutputHeader,
75                              WSJob j)
76     {
77       if (j.result != null)
78       {
79         String progheader = "";
80         // Parse state of job[j]
81         if (j.result.isRunning())
82         {
83           running++;
84           wsInfo.setStatus(j.jobnum, WebserviceInfo.STATE_RUNNING);
85         }
86         else if (j.result.isQueued())
87         {
88           queuing++;
89           wsInfo.setStatus(j.jobnum, WebserviceInfo.STATE_QUEUING);
90         }
91         else if (j.result.isFinished())
92         {
93           finished++;
94           j.subjobComplete = true;
95           if (j.hasResults())
96           {
97             results++;
98           }
99           wsInfo.setStatus(j.jobnum, WebserviceInfo.STATE_STOPPED_OK);
100         }
101         else if (j.result.isFailed())
102         {
103           progheader += "Job failed.\n";
104           j.subjobComplete = true;
105           wsInfo.setStatus(j.jobnum, WebserviceInfo.STATE_STOPPED_ERROR);
106           error++;
107         }
108         else if (j.result.isServerError())
109         {
110           serror++;
111           j.subjobComplete = true;
112           wsInfo.setStatus(j.jobnum,
113                            WebserviceInfo.STATE_STOPPED_SERVERERROR);
114         }
115         else if (j.result.isBroken() || j.result.isFailed())
116         {
117           error++;
118           j.subjobComplete = true;
119           wsInfo.setStatus(j.jobnum, WebserviceInfo.STATE_STOPPED_ERROR);
120         }
121         // and pass on any sub-job messages to the user
122         wsInfo.setProgressText(j.jobnum, OutputHeader);
123         wsInfo.appendProgressText(j.jobnum, progheader);
124         if (j.result.getStatus() != null)
125         {
126           wsInfo.appendProgressText(j.jobnum, j.result.getStatus());
127         }
128       }
129       else
130       {
131         if (j.submitted && j.subjobComplete)
132         {
133           if (j.allowedServerExceptions == 0)
134           {
135             serror++;
136           }
137           else if (j.result == null)
138           {
139             error++;
140           }
141         }
142       }
143     }
144   }
145
146   WSJob jobs[] = null;
147   String WebServiceName = null;
148   String OutputHeader;
149   String WsUrl = null;
150   abstract void pollJob(WSJob job)
151       throws Exception;
152
153   public void run()
154   {
155     JobStateSummary jstate = null;
156     if (jobs == null)
157     {
158       jobComplete = true;
159     }
160     while (!jobComplete)
161     {
162       jstate = new JobStateSummary();
163       for (int j = 0; j < jobs.length; j++)
164       {
165
166         if (!jobs[j].submitted && jobs[j].hasValidInput())
167         {
168           StartJob(jobs[j]);
169         }
170
171         if (jobs[j].submitted && !jobs[j].subjobComplete)
172         {
173           try
174           {
175             pollJob(jobs[j]);
176             if (jobs[j].result == null)
177             {
178               throw (new Exception(
179                   "Timed out when communicating with server\nTry again later.\n"));
180             }
181             jalview.bin.Cache.log.debug("Job " + j + " Result state " +
182                                         jobs[j].result.getState()
183                                         + "(ServerError=" +
184                                         jobs[j].result.isServerError() + ")");
185           }
186           catch (Exception ex)
187           {
188             // Deal with Transaction exceptions
189             wsInfo.appendProgressText(jobs[j].jobnum, "\n" + WebServiceName
190                                       + " Server exception!\n" + ex.getMessage());
191             Cache.log.warn(WebServiceName + " job(" + jobs[j].jobnum
192                            + ") Server exception: " + ex.getMessage());
193
194             if (jobs[j].allowedServerExceptions > 0)
195             {
196               jobs[j].allowedServerExceptions--;
197               Cache.log.debug("Sleeping after a server exception.");
198               try
199               {
200                 Thread.sleep(5000);
201               }
202               catch (InterruptedException ex1)
203               {
204               }
205             }
206             else
207             {
208               Cache.log.warn("Dropping job " + j + " " + jobs[j].jobId);
209               jobs[j].subjobComplete = true;
210               wsInfo.setStatus(jobs[j].jobnum,
211                                WebserviceInfo.STATE_STOPPED_SERVERERROR);
212             }
213           }
214           catch (OutOfMemoryError er)
215           {
216             jobComplete = true;
217             jobs[j].subjobComplete = true;
218             jobs[j].result = null; // may contain out of date result object
219             wsInfo.setStatus(jobs[j].jobnum,
220                              WebserviceInfo.STATE_STOPPED_ERROR);
221             JOptionPane
222                 .showInternalMessageDialog(
223                     Desktop.desktop,
224                     "Out of memory handling result for job !!"
225                     +
226                     "\nSee help files for increasing Java Virtual Machine memory.",
227                     "Out of memory", JOptionPane.WARNING_MESSAGE);
228             Cache.log.error("Out of memory when retrieving Job " + j + " id:" +
229                             WsUrl + "/" + jobs[j].jobId, er);
230             System.gc();
231           }
232         }
233         jstate.updateJobPanelState(wsInfo, OutputHeader, jobs[j]);
234       }
235       // Decide on overall state based on collected jobs[] states
236       if (jstate.running > 0)
237       {
238         wsInfo.setStatus(WebserviceInfo.STATE_RUNNING);
239       }
240       else if (jstate.queuing > 0)
241       {
242         wsInfo.setStatus(WebserviceInfo.STATE_QUEUING);
243       }
244       else
245       {
246         jobComplete = true;
247         if (jstate.finished > 0)
248         {
249           wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_OK);
250         }
251         else if (jstate.error > 0)
252         {
253           wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_ERROR);
254         }
255         else if (jstate.serror > 0)
256         {
257           wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_SERVERERROR);
258         }
259       }
260       if (!jobComplete)
261       {
262         try
263         {
264           Thread.sleep(5000);
265         }
266         catch (InterruptedException e)
267         {
268           Cache.log.debug("Interrupted sleep waiting for next job poll.", e);
269         }
270         // System.out.println("I'm alive "+alTitle);
271       }
272     }
273     if (jobComplete && jobs != null)
274     {
275       parseResult(); // tidy up and make results available to user
276     }
277     else
278     {
279       Cache.log.debug("WebServiceJob poll loop finished with no jobs created.");
280     }
281   }
282
283   abstract void StartJob(WSJob job);
284
285   abstract void parseResult();
286
287   protected void propagateDatasetMappings(Alignment al)
288   {
289     if (codonframe!=null)
290     {
291       SequenceI[] alignment = al.getSequencesArray();
292       for (int sq = 0; sq<alignment.length; sq++)
293       {
294         for (int i=0; i<codonframe.length; i++)
295         {
296           if (codonframe[i]!=null &&
297                   codonframe[i].involvesSequence(alignment[sq]))
298           {
299             al.addCodonFrame(codonframe[i]);
300             codonframe[i] = null;
301             break;
302           }
303         }
304       }
305     }
306   }
307
308   /**
309    * 
310    * @param alignFrame reference for copying mappings across
311    * @param wsInfo gui attachment point
312    * @param input input data for the calculation
313    * @param webServiceName name of service
314    * @param wsUrl  url of the service being invoked
315    */
316   public WSThread(AlignFrame alignFrame, WebserviceInfo wsinfo,
317           AlignmentView input, String webServiceName,
318           String wsUrl)
319   {
320     this(alignFrame, wsinfo, input, wsUrl);
321     WebServiceName = webServiceName;
322   }
323
324   /**
325    * 
326    * @param alframe - reference for copying mappings across
327    * @param wsinfo2 - gui attachment point
328    * @param alview - input data for the calculation
329    * @param wsurl2 - url of the service being invoked
330    */
331   public WSThread(AlignFrame alframe, WebserviceInfo wsinfo2,
332           AlignmentView alview, String wsurl2)
333   {
334     super();
335     this.alignFrame = alframe;
336     this.wsInfo = wsinfo2;
337     this.input = alview;
338     WsUrl = wsurl2;
339     if (alignFrame!=null)
340     {
341       AlignedCodonFrame[] cf = alignFrame.getViewport().getAlignment().getCodonFrames();
342       if (cf!=null)
343       {
344         codonframe = new AlignedCodonFrame[cf.length];
345         System.arraycopy(cf, 0, codonframe, 0, cf.length);
346       }
347     }
348   }
349 }