JAL-1003 visual delay when running a sequence or alignment annotation job
[jalview.git] / src / jalview / ws / jws2 / JabawsAlignCalcWorker.java
1 package jalview.ws.jws2;
2
3 import jalview.analysis.AlignSeq;
4 import jalview.analysis.SeqsetUtils;
5 import jalview.api.AlignViewportI;
6 import jalview.api.AlignmentViewPanel;
7 import jalview.datamodel.AlignmentAnnotation;
8 import jalview.datamodel.AlignmentI;
9 import jalview.datamodel.SequenceI;
10 import jalview.gui.AlignFrame;
11 import jalview.gui.IProgressIndicator;
12 import jalview.workers.AlignCalcWorker;
13 import jalview.ws.jws2.dm.JabaWsParamSet;
14 import jalview.ws.jws2.jabaws2.Jws2Instance;
15 import jalview.ws.params.WsParamSetI;
16
17 import java.util.ArrayList;
18 import java.util.HashMap;
19 import java.util.List;
20 import java.util.Map;
21
22 import compbio.data.msa.SequenceAnnotation;
23 import compbio.data.sequence.FastaSequence;
24 import compbio.data.sequence.ScoreManager;
25 import compbio.metadata.Argument;
26 import compbio.metadata.ChunkHolder;
27 import compbio.metadata.JobStatus;
28 import compbio.metadata.JobSubmissionException;
29 import compbio.metadata.Option;
30 import compbio.metadata.ResultNotAvailableException;
31 import compbio.metadata.WrongParameterException;
32
33 public abstract class JabawsAlignCalcWorker extends AlignCalcWorker
34 {
35
36   @SuppressWarnings("unchecked")
37   protected SequenceAnnotation aaservice;
38
39   protected ScoreManager scoremanager;
40
41   protected WsParamSetI preset;
42
43   protected List<Argument> arguments;
44
45   public JabawsAlignCalcWorker(AlignViewportI alignViewport,
46           AlignmentViewPanel alignPanel)
47   {
48     super(alignViewport, alignPanel);
49   }
50
51   IProgressIndicator guiProgress;
52
53   public JabawsAlignCalcWorker(Jws2Instance service, AlignFrame alignFrame,
54           WsParamSetI preset, List<Argument> paramset)
55   {
56     this(alignFrame.getCurrentView(), alignFrame.alignPanel);
57     this.guiProgress = alignFrame;
58     this.preset = preset;
59     this.arguments = paramset;
60     aaservice = (SequenceAnnotation) service.service;
61
62   }
63
64   public WsParamSetI getPreset()
65   {
66     return preset;
67   }
68
69   public List<Argument> getArguments()
70   {
71     return arguments;
72   }
73
74   /**
75    * reconfigure and restart the AAConsClient. This method will spawn a new
76    * thread that will wait until any current jobs are finished, modify the
77    * parameters and restart the conservation calculation with the new values.
78    *
79    * @param newpreset
80    * @param newarguments
81    */
82   public void updateParameters(final WsParamSetI newpreset,
83           final List<Argument> newarguments)
84   {
85     if (calcMan.isWorking(this))
86     {
87       new Thread(new Runnable()
88       {
89         @Override
90         public void run()
91         {
92
93           try
94           {
95             Thread.sleep(200);
96           } catch (InterruptedException x)
97           {
98           }
99           ;
100           updateParameters(newpreset, newarguments);
101         }
102       }).start();
103     }
104     else
105     {
106       preset = newpreset;
107       arguments = newarguments;
108       calcMan.startWorker(this);
109     }
110   }
111
112   public List<Option> getJabaArguments()
113   {
114     List<Option> newargs = new ArrayList<Option>();
115     if (preset != null && preset instanceof JabaWsParamSet)
116     {
117       newargs.addAll(((JabaWsParamSet) preset).getjabaArguments());
118     }
119     if (arguments != null && arguments.size() > 0)
120     {
121       for (Argument rg : arguments)
122       {
123         if (Option.class.isAssignableFrom(rg.getClass()))
124         {
125           newargs.add((Option) rg);
126         }
127       }
128     }
129     return newargs;
130   }
131
132   @Override
133   public void run()
134   {
135     if (aaservice == null)
136     {
137       return;
138     }
139     long progressId = -1;
140
141     String rslt = "JOB NOT DEFINED";
142
143     try
144     {
145       if (checkDone())
146       {
147         return;
148       }
149       List<compbio.data.sequence.FastaSequence> seqs = getInputSequences(alignViewport
150               .getAlignment());
151
152       if (seqs == null)
153       {
154         calcMan.workerComplete(this);
155         return;
156       }
157
158       AlignmentAnnotation[] aa = alignViewport.getAlignment()
159               .getAlignmentAnnotation();
160       if (guiProgress != null)
161       {
162         guiProgress.setProgressBar("JABA " + getServiceActionText(),
163                 progressId = System.currentTimeMillis());
164       }
165       if (preset == null)
166       {
167         rslt = aaservice.analize(seqs);
168       }
169       else
170       {
171         try
172         {
173           rslt = aaservice.customAnalize(seqs, getJabaArguments());
174         } catch (WrongParameterException x)
175         {
176           throw new JobSubmissionException(
177                   "Invalid paremeter set. Check Jalview implementation.", x);
178
179         }
180       }
181       boolean finished = false;
182       long rpos = 0;
183       do
184       {
185         JobStatus status = aaservice.getJobStatus(rslt);
186         if (status.equals(JobStatus.FINISHED))
187         {
188           finished = true;
189         }
190         long cpos;
191         ChunkHolder stats;
192         do
193         {
194           cpos = rpos;
195           try
196           {
197             stats = aaservice.pullExecStatistics(rslt, rpos);
198           } catch (Exception x)
199           {
200
201             if (x.getMessage().contains(
202                     "Position in a file could not be negative!"))
203             {
204               // squash index out of bounds exception- seems to happen for
205               // disorder predictors which don't (apparently) produce any
206               // progress information and JABA server throws an exception
207               // because progress length is -1.
208               stats = null;
209             }
210             else
211             {
212               throw x;
213             }
214           }
215           if (stats != null)
216           {
217             System.out.print(stats.getChunk());
218             rpos = stats.getNextPosition();
219           }
220         } while (stats != null && rpos > cpos);
221
222         if (!finished && status.equals(JobStatus.FAILED))
223         {
224           try
225           {
226             Thread.sleep(200);
227           } catch (InterruptedException x)
228           {
229           }
230           ;
231         }
232
233       } while (!finished);
234       try
235       {
236         Thread.sleep(200);
237       } catch (InterruptedException x)
238       {
239       }
240       ;
241       scoremanager = aaservice.getAnnotation(rslt);
242       if (scoremanager != null)
243       {
244         updateResultAnnotation(true);
245       }
246     } catch (JobSubmissionException x)
247     {
248
249       System.err.println("submission error:");
250       x.printStackTrace();
251       calcMan.workerCannotRun(this);
252     } catch (ResultNotAvailableException x)
253     {
254       System.err.println("collection error:\nJob ID: " + rslt);
255       x.printStackTrace();
256       calcMan.workerCannotRun(this);
257
258     } catch (OutOfMemoryError error)
259     {
260       calcMan.workerCannotRun(this);
261
262       // consensus = null;
263       // hconsensus = null;
264       ap.raiseOOMWarning(getServiceActionText(), error);
265     } catch (Exception x)
266     {
267       calcMan.workerCannotRun(this);
268
269       // consensus = null;
270       // hconsensus = null;
271       System.err
272               .println("Blacklisting worker due to unexpected exception:");
273       x.printStackTrace();
274     } finally
275     {
276
277       calcMan.workerComplete(this);
278       if (ap != null)
279       {
280         calcMan.workerComplete(this);
281         if (guiProgress != null)
282         {
283           guiProgress.setProgressBar("", progressId);
284         }
285         ap.paintAlignment(true);
286       }
287     }
288
289   }
290
291   @Override
292   public void updateAnnotation()
293   {
294     updateResultAnnotation(false);
295   }
296
297   public abstract void updateResultAnnotation(boolean immediate);
298
299   public abstract String getServiceActionText();
300
301   boolean submitGaps = true;
302
303   boolean alignedSeqs = true;
304
305   boolean nucleotidesAllowed = false;
306
307   boolean proteinAllowed = false;
308
309   /**
310    * record sequences for mapping result back to afterwards
311    */
312   protected boolean bySequence = false;
313
314   Map<String, SequenceI> seqNames;
315
316   public List<FastaSequence> getInputSequences(AlignmentI alignment)
317   {
318
319     if (alignment == null || alignment.getWidth() <= 0
320             || alignment.getSequences() == null
321             // || (alignedSeqs && !alignment.isAligned() && !submitGaps)
322             || alignment.isNucleotide() ? !nucleotidesAllowed
323             : !proteinAllowed)
324     {
325       return null;
326     }
327     List<compbio.data.sequence.FastaSequence> seqs = new ArrayList<compbio.data.sequence.FastaSequence>();
328
329     int minlen = 10;
330     int ln = -1;
331     if (bySequence)
332     {
333       seqNames = new HashMap<String, SequenceI>();
334     }
335     for (SequenceI sq : ((List<SequenceI>) alignment.getSequences()))
336     {
337
338       if (sq.getEnd() - sq.getStart() > minlen - 1)
339       {
340         String newname = SeqsetUtils.unique_name(seqs.size() + 1);
341         // make new input sequence with or without gaps
342         if (seqNames != null)
343         {
344           seqNames.put(newname, sq);
345         }
346         FastaSequence seq;
347         seqs.add(seq = new compbio.data.sequence.FastaSequence(newname,
348                 (submitGaps) ? sq.getSequenceAsString() : AlignSeq
349                         .extractGaps(jalview.util.Comparison.GapChars,
350                                 sq.getSequenceAsString())));
351         if (seq.getSequence().length() > ln)
352         {
353           ln = seq.getSequence().length();
354         }
355       }
356     }
357     if (alignedSeqs && submitGaps)
358     {
359       // try real hard to return something submittable
360       // TODO: some of AAcons measures need a minimum of two or three amino
361       // acids at each position, and aacons doesn't gracefully degrade.
362       for (int p = 0; p < seqs.size(); p++)
363       {
364         FastaSequence sq = seqs.get(p);
365         int l = sq.getSequence().length();
366         if (l < ln)
367         {
368           char[] padded = new char[ln];
369           System.arraycopy(sq.getSequence().toCharArray(), 0, padded, 0, sq
370                   .getSequence().length());
371           while (l < ln)
372           {
373             padded[l++] = '-';
374           }
375           seqs.set(p, new compbio.data.sequence.FastaSequence(sq.getId(),
376                   new String(padded)));
377         }
378       }
379
380     }
381     return seqs;
382   }
383
384   /**
385    * notify manager that we have started, and wait for a free calculation slot
386    *
387    * @return true if slot is obtained and work still valid, false if another
388    *         thread has done our work for us.
389    */
390   boolean checkDone()
391   {
392     calcMan.notifyStart(this);
393     // ap.paintAlignment(false);
394     while (!calcMan.notifyWorking(this))
395     {
396       if (calcMan.isWorking(this))
397       {
398         return true;
399       }
400       try
401       {
402         if (ap != null)
403         {
404           ap.paintAlignment(false);
405         }
406
407         Thread.sleep(200);
408       } catch (Exception ex)
409       {
410         ex.printStackTrace();
411       }
412     }
413     if (alignViewport.isClosed())
414     {
415       abortAndDestroy();
416       return true;
417     }
418     return false;
419   }
420
421 }