JAL-2416 order score models by order of addition rather than name
[jalview.git] / src / jalview / ws / AWSThread.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3  * Copyright (C) $$Year-Rel$$ The Jalview Authors
4  * 
5  * This file is part of Jalview.
6  * 
7  * Jalview is free software: you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License 
9  * as published by the Free Software Foundation, either version 3
10  * of the License, or (at your option) any later version.
11  *  
12  * Jalview is distributed in the hope that it will be useful, but 
13  * WITHOUT ANY WARRANTY; without even the implied warranty 
14  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
15  * PURPOSE.  See the GNU General Public License for more details.
16  * 
17  * You should have received a copy of the GNU General Public License
18  * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
19  * The Jalview Authors are detailed in the 'AUTHORS' file.
20  */
21 package jalview.ws;
22
23 import jalview.bin.Cache;
24 import jalview.datamodel.AlignedCodonFrame;
25 import jalview.datamodel.Alignment;
26 import jalview.datamodel.AlignmentI;
27 import jalview.datamodel.AlignmentView;
28 import jalview.datamodel.SequenceI;
29 import jalview.gui.AlignFrame;
30 import jalview.gui.WebserviceInfo;
31 import jalview.util.MessageManager;
32 import jalview.viewmodel.seqfeatures.FeatureRendererSettings;
33
34 import java.util.ArrayList;
35 import java.util.List;
36
37 public abstract class AWSThread extends Thread
38 {
39
40   /**
41    * view that this job was associated with
42    */
43   protected AlignmentI currentView = null;
44
45   /**
46    * feature settings from view that job was associated with
47    */
48   protected FeatureRendererSettings featureSettings = null;
49
50   /**
51    * metadata about this web service
52    */
53   protected WebserviceInfo wsInfo = null;
54
55   /**
56    * original input data for this job
57    */
58   protected AlignmentView input = null;
59
60   /**
61    * dataset sequence relationships to be propagated onto new results
62    */
63   protected List<AlignedCodonFrame> codonframe = null;
64
65   /**
66    * are there jobs still running in this thread.
67    */
68   protected boolean jobComplete = false;
69
70   /**
71    * one or more jobs being managed by this thread.
72    */
73   protected AWsJob jobs[] = null;
74
75   /**
76    * full name of service
77    */
78   protected String WebServiceName = null;
79
80   protected char defGapChar = '-';
81
82   /**
83    * header prepended to all output from job
84    */
85   protected String OutputHeader;
86
87   /**
88    * only used when reporting a web service out of memory error - the job ID
89    * will be concatenated to the URL
90    */
91   protected String WsUrl = null;
92
93   /*
94    * The AlignFrame from which the service was requested.
95    */
96   private AlignFrame alignFrame;
97
98   /**
99    * generic web service job/subjob poll loop
100    */
101   @Override
102   public void run()
103   {
104     JobStateSummary jstate = null;
105     if (jobs == null)
106     {
107       jobComplete = true;
108     }
109     while (!jobComplete)
110     {
111       jstate = new JobStateSummary();
112       for (int j = 0; j < jobs.length; j++)
113       {
114
115         if (!jobs[j].submitted && jobs[j].hasValidInput())
116         {
117           StartJob(jobs[j]);
118         }
119
120         if (jobs[j].submitted && !jobs[j].subjobComplete)
121         {
122           try
123           {
124             pollJob(jobs[j]);
125             if (!jobs[j].hasResponse())
126             {
127               throw (new Exception(
128                       "Timed out when communicating with server\nTry again later.\n"));
129             }
130             jalview.bin.Cache.log.debug("Job " + j + " Result state "
131                     + jobs[j].getState() + "(ServerError="
132                     + jobs[j].isServerError() + ")");
133           } catch (Exception ex)
134           {
135             // Deal with Transaction exceptions
136             wsInfo.appendProgressText(jobs[j].jobnum, MessageManager
137                     .formatMessage("info.server_exception", new Object[] {
138                         WebServiceName, ex.getMessage() }));
139             // always output the exception's stack trace to the log
140             Cache.log.warn(WebServiceName + " job(" + jobs[j].jobnum
141                     + ") Server exception.");
142             // todo: could limit trace to cause if this is a SOAPFaultException.
143             ex.printStackTrace();
144
145             if (jobs[j].allowedServerExceptions > 0)
146             {
147               jobs[j].allowedServerExceptions--;
148               Cache.log.debug("Sleeping after a server exception.");
149               try
150               {
151                 Thread.sleep(5000);
152               } catch (InterruptedException ex1)
153               {
154               }
155             }
156             else
157             {
158               Cache.log.warn("Dropping job " + j + " " + jobs[j].jobId);
159               jobs[j].subjobComplete = true;
160               wsInfo.setStatus(jobs[j].jobnum,
161                       WebserviceInfo.STATE_STOPPED_SERVERERROR);
162             }
163           } catch (OutOfMemoryError er)
164           {
165             jobComplete = true;
166             jobs[j].subjobComplete = true;
167             jobs[j].clearResponse(); // may contain out of date result data
168             wsInfo.setStatus(jobs[j].jobnum,
169                     WebserviceInfo.STATE_STOPPED_ERROR);
170             Cache.log.error("Out of memory when retrieving Job " + j
171                     + " id:" + WsUrl + "/" + jobs[j].jobId, er);
172             new jalview.gui.OOMWarning("retrieving result for "
173                     + WebServiceName, er);
174             System.gc();
175           }
176         }
177         jstate.updateJobPanelState(wsInfo, OutputHeader, jobs[j]);
178       }
179       // Decide on overall state based on collected jobs[] states
180       updateGlobalStatus(jstate);
181       if (!jobComplete)
182       {
183         try
184         {
185           Thread.sleep(5000);
186         } catch (InterruptedException e)
187         {
188           Cache.log
189                   .debug("Interrupted sleep waiting for next job poll.", e);
190         }
191         // System.out.println("I'm alive "+alTitle);
192       }
193     }
194     if (jobComplete && jobs != null)
195     {
196       parseResult(); // tidy up and make results available to user
197     }
198     else
199     {
200       Cache.log
201               .debug("WebServiceJob poll loop finished with no jobs created.");
202       wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_ERROR);
203       wsInfo.appendProgressText(MessageManager
204               .getString("info.no_jobs_ran"));
205       wsInfo.setFinishedNoResults();
206     }
207   }
208
209   protected void updateGlobalStatus(JobStateSummary jstate)
210   {
211     if (jstate.running > 0)
212     {
213       wsInfo.setStatus(WebserviceInfo.STATE_RUNNING);
214     }
215     else if (jstate.queuing > 0)
216     {
217       wsInfo.setStatus(WebserviceInfo.STATE_QUEUING);
218     }
219     else
220     {
221       jobComplete = true;
222       if (jstate.finished > 0)
223       {
224         wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_OK);
225       }
226       else if (jstate.error > 0)
227       {
228         wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_ERROR);
229       }
230       else if (jstate.serror > 0)
231       {
232         wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_SERVERERROR);
233       }
234     }
235   }
236
237   public AWSThread()
238   {
239     super();
240   }
241
242   public AWSThread(Runnable target)
243   {
244     super(target);
245   }
246
247   public AWSThread(String name)
248   {
249     super(name);
250   }
251
252   public AWSThread(ThreadGroup group, Runnable target)
253   {
254     super(group, target);
255   }
256
257   public AWSThread(ThreadGroup group, String name)
258   {
259     super(group, name);
260   }
261
262   public AWSThread(Runnable target, String name)
263   {
264     super(target, name);
265   }
266
267   public AWSThread(ThreadGroup group, Runnable target, String name)
268   {
269     super(group, target, name);
270   }
271
272   /**
273    * query web service for status of job. on return, job.result must not be null
274    * - if it is then it will be assumed that the job status query timed out and
275    * a server exception will be logged.
276    * 
277    * @param job
278    * @throws Exception
279    *           will be logged as a server exception for this job
280    */
281   public abstract void pollJob(AWsJob job) throws Exception;
282
283   /**
284    * submit job to web service
285    * 
286    * @param job
287    */
288   public abstract void StartJob(AWsJob job);
289
290   /**
291    * process the set of AWsJob objects into a set of results, and tidy up.
292    */
293   public abstract void parseResult();
294
295   /**
296    * helper function to conserve dataset references to sequence objects returned
297    * from web services 1. Propagates AlCodonFrame data from
298    * <code>codonframe</code> to <code>al</code> TODO: refactor to datamodel
299    * 
300    * @param al
301    */
302   public void propagateDatasetMappings(Alignment al)
303   {
304     if (codonframe != null)
305     {
306       SequenceI[] alignment = al.getSequencesArray();
307       for (int sq = 0; sq < alignment.length; sq++)
308       {
309         for (AlignedCodonFrame acf : codonframe)
310         {
311           final SequenceI seq = alignment[sq];
312           if (acf != null && acf.involvesSequence(seq))
313           {
314             al.addCodonFrame(acf);
315             break;
316           }
317         }
318       }
319     }
320   }
321
322   public AWSThread(ThreadGroup group, Runnable target, String name,
323           long stackSize)
324   {
325     super(group, target, name, stackSize);
326   }
327
328   /**
329    * 
330    * @return gap character to use for any alignment generation
331    */
332   public char getGapChar()
333   {
334     return defGapChar;
335   }
336
337   /**
338    * 
339    * @param alignFrame
340    *          reference for copying mappings across
341    * @param wsInfo
342    *          gui attachment point
343    * @param input
344    *          input data for the calculation
345    * @param webServiceName
346    *          name of service
347    * @param wsUrl
348    *          url of the service being invoked
349    */
350   public AWSThread(AlignFrame alignFrame, WebserviceInfo wsinfo,
351           AlignmentView input, String webServiceName, String wsUrl)
352   {
353     this(alignFrame, wsinfo, input, wsUrl);
354     WebServiceName = webServiceName;
355   }
356
357   /**
358    * Extracts additional info from alignment view's context.
359    * 
360    * @param alframe
361    *          - reference for copying mappings and display styles across
362    * @param wsinfo2
363    *          - gui attachment point - may be null
364    * @param alview
365    *          - input data for the calculation
366    * @param wsurl2
367    *          - url of the service being invoked
368    */
369   public AWSThread(AlignFrame alframe, WebserviceInfo wsinfo2,
370           AlignmentView alview, String wsurl2)
371   {
372     super();
373     this.alignFrame = alframe;
374     currentView = alframe.getCurrentView().getAlignment();
375     featureSettings = alframe.getFeatureRenderer().getSettings();
376     defGapChar = alframe.getViewport().getGapCharacter();
377     this.wsInfo = wsinfo2;
378     this.input = alview;
379     WsUrl = wsurl2;
380     if (alframe != null)
381     {
382       List<AlignedCodonFrame> cf = alframe.getViewport().getAlignment()
383               .getCodonFrames();
384       if (cf != null)
385       {
386         codonframe = new ArrayList<AlignedCodonFrame>();
387         codonframe.addAll(cf);
388       }
389     }
390   }
391
392   protected AlignFrame getRequestingAlignFrame()
393   {
394     return this.alignFrame;
395   }
396 }