JAL-3066 Working custom parameters for clustalo
[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         Cache.log.debug("Job " + jobs[j] + " is " + (jobs[j].submitted ? "submitted" : "not submitted"));
120         if (jobs[j].submitted && !jobs[j].subjobComplete)
121         {
122           jalview.bin.Cache.log.debug(
123               "Polling Job " + j + " Result state was:" + jobs[j].getState() + "(ServerError=" + jobs[j].isServerError()
124                   + ")"
125           );
126           try
127           {
128             pollJob(jobs[j]);
129             if (!jobs[j].hasResponse())
130             {
131               throw (new Exception(
132                       "Timed out when communicating with server\nTry again later.\n"));
133             }
134             jalview.bin.Cache.log.debug("Job " + j + " Result state "
135                     + jobs[j].getState() + "(ServerError="
136                     + jobs[j].isServerError() + ")");
137           } catch (Exception ex)
138           {
139             // Deal with Transaction exceptions
140             wsInfo.appendProgressText(jobs[j].jobnum, MessageManager
141                     .formatMessage("info.server_exception", new Object[]
142                     { WebServiceName, ex.getMessage() }));
143             // always output the exception's stack trace to the log
144             Cache.log.warn(WebServiceName + " job(" + jobs[j].jobnum
145                     + ") Server exception.");
146             // todo: could limit trace to cause if this is a SOAPFaultException.
147             ex.printStackTrace();
148
149             if (jobs[j].allowedServerExceptions > 0)
150             {
151               jobs[j].allowedServerExceptions--;
152               Cache.log.debug("Sleeping after a server exception.");
153               try
154               {
155                 Thread.sleep(5000);
156               } catch (InterruptedException ex1)
157               {
158               }
159             }
160             else
161             {
162               Cache.log.warn("Dropping job " + j + " " + jobs[j].jobId);
163               jobs[j].subjobComplete = true;
164               wsInfo.setStatus(jobs[j].jobnum,
165                       WebserviceInfo.STATE_STOPPED_SERVERERROR);
166             }
167           } catch (OutOfMemoryError er)
168           {
169             jobComplete = true;
170             jobs[j].subjobComplete = true;
171             jobs[j].clearResponse(); // may contain out of date result data
172             wsInfo.setStatus(jobs[j].jobnum,
173                     WebserviceInfo.STATE_STOPPED_ERROR);
174             Cache.log.error("Out of memory when retrieving Job " + j
175                     + " id:" + WsUrl + "/" + jobs[j].jobId, er);
176             new jalview.gui.OOMWarning(
177                     "retrieving result for " + WebServiceName, er);
178             System.gc();
179           }
180         }
181         jstate.updateJobPanelState(wsInfo, OutputHeader, jobs[j]);
182       }
183       // Decide on overall state based on collected jobs[] states
184       updateGlobalStatus(jstate);
185       if (!jobComplete)
186       {
187         try
188         {
189           Thread.sleep(5000);
190         } catch (InterruptedException e)
191         {
192           Cache.log.debug("Interrupted sleep waiting for next job poll.",
193                   e);
194         }
195         // System.out.println("I'm alive ?");
196       }
197     }
198     if (jobComplete && jobs != null)
199     {
200       parseResult(); // tidy up and make results available to user
201     }
202     else
203     {
204       Cache.log.debug(
205               "WebServiceJob poll loop finished with no jobs created.");
206       wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_ERROR);
207       wsInfo.appendProgressText(
208               MessageManager.getString("info.no_jobs_ran"));
209       wsInfo.setFinishedNoResults();
210     }
211   }
212
213   protected void updateGlobalStatus(JobStateSummary jstate)
214   {
215     if (jstate.running > 0)
216     {
217       wsInfo.setStatus(WebserviceInfo.STATE_RUNNING);
218     }
219     else if (jstate.queuing > 0)
220     {
221       wsInfo.setStatus(WebserviceInfo.STATE_QUEUING);
222     }
223     else
224     {
225       jobComplete = true;
226       if (jstate.finished > 0)
227       {
228         wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_OK);
229       }
230       else if (jstate.error > 0)
231       {
232         wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_ERROR);
233       }
234       else if (jstate.serror > 0)
235       {
236         wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_SERVERERROR);
237       }
238     }
239   }
240
241   public AWSThread()
242   {
243     super();
244   }
245
246   public AWSThread(Runnable target)
247   {
248     super(target);
249   }
250
251   public AWSThread(String name)
252   {
253     super(name);
254   }
255
256   public AWSThread(ThreadGroup group, Runnable target)
257   {
258     super(group, target);
259   }
260
261   public AWSThread(ThreadGroup group, String name)
262   {
263     super(group, name);
264   }
265
266   public AWSThread(Runnable target, String name)
267   {
268     super(target, name);
269   }
270
271   public AWSThread(ThreadGroup group, Runnable target, String name)
272   {
273     super(group, target, name);
274   }
275
276   /**
277    * query web service for status of job. on return, job.result must not be null
278    * - if it is then it will be assumed that the job status query timed out and
279    * a server exception will be logged.
280    * 
281    * @param job
282    * @throws Exception
283    *           will be logged as a server exception for this job
284    */
285   public abstract void pollJob(AWsJob job) throws Exception;
286
287   /**
288    * submit job to web service
289    * 
290    * @param job
291    */
292   public abstract void StartJob(AWsJob job);
293
294   /**
295    * process the set of AWsJob objects into a set of results, and tidy up.
296    */
297   public abstract void parseResult();
298
299   /**
300    * helper function to conserve dataset references to sequence objects returned
301    * from web services 1. Propagates AlCodonFrame data from
302    * <code>codonframe</code> to <code>al</code> TODO: refactor to datamodel
303    * 
304    * @param al
305    */
306   public void propagateDatasetMappings(Alignment al)
307   {
308     if (codonframe != null)
309     {
310       SequenceI[] alignment = al.getSequencesArray();
311       for (int sq = 0; sq < alignment.length; sq++)
312       {
313         for (AlignedCodonFrame acf : codonframe)
314         {
315           final SequenceI seq = alignment[sq];
316           if (acf != null && acf.involvesSequence(seq))
317           {
318             al.addCodonFrame(acf);
319             break;
320           }
321         }
322       }
323     }
324   }
325
326   public AWSThread(ThreadGroup group, Runnable target, String name,
327           long stackSize)
328   {
329     super(group, target, name, stackSize);
330   }
331
332   /**
333    * 
334    * @return gap character to use for any alignment generation
335    */
336   public char getGapChar()
337   {
338     return defGapChar;
339   }
340
341   /**
342    * 
343    * @param alignFrame
344    *          reference for copying mappings across
345    * @param wsInfo
346    *          gui attachment point
347    * @param input
348    *          input data for the calculation
349    * @param webServiceName
350    *          name of service
351    * @param wsUrl
352    *          url of the service being invoked
353    */
354   public AWSThread(AlignFrame alignFrame, WebserviceInfo wsinfo,
355           AlignmentView input, String webServiceName, String wsUrl)
356   {
357     this(alignFrame, wsinfo, input, wsUrl);
358     WebServiceName = webServiceName;
359   }
360
361   /**
362    * Extracts additional info from alignment view's context.
363    * 
364    * @param alframe
365    *          - reference for copying mappings and display styles across
366    * @param wsinfo2
367    *          - gui attachment point - may be null
368    * @param alview
369    *          - input data for the calculation
370    * @param wsurl2
371    *          - url of the service being invoked
372    */
373   public AWSThread(AlignFrame alframe, WebserviceInfo wsinfo2,
374           AlignmentView alview, String wsurl2)
375   {
376     super();
377     this.alignFrame = alframe;
378     currentView = alframe.getCurrentView().getAlignment();
379     featureSettings = alframe.getFeatureRenderer().getSettings();
380     defGapChar = alframe.getViewport().getGapCharacter();
381     this.wsInfo = wsinfo2;
382     this.input = alview;
383     WsUrl = wsurl2;
384     if (alframe != null)
385     {
386       List<AlignedCodonFrame> cf = alframe.getViewport().getAlignment()
387               .getCodonFrames();
388       if (cf != null)
389       {
390         codonframe = new ArrayList<>();
391         codonframe.addAll(cf);
392       }
393     }
394   }
395
396   protected AlignFrame getRequestingAlignFrame()
397   {
398     return this.alignFrame;
399   }
400 }