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