78269d8efbe2d251d1adb9be29d08cd2d7518f8a
[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   Jws2Instance service;
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     this.service = service;
61     aaservice = (SequenceAnnotation) service.service;
62
63   }
64
65   public WsParamSetI getPreset()
66   {
67     return preset;
68   }
69
70   public List<Argument> getArguments()
71   {
72     return arguments;
73   }
74
75   /**
76    * reconfigure and restart the AAConsClient. This method will spawn a new
77    * thread that will wait until any current jobs are finished, modify the
78    * parameters and restart the conservation calculation with the new values.
79    *
80    * @param newpreset
81    * @param newarguments
82    */
83   public void updateParameters(final WsParamSetI newpreset,
84           final List<Argument> newarguments)
85   {
86     if (false) // || calcMan.isWorking(this))
87     {
88       new Thread(new Runnable()
89       {
90         @Override
91         public void run()
92         {
93
94           try
95           {
96             Thread.sleep(200);
97           } catch (InterruptedException x)
98           {
99           }
100           ;
101           updateParameters(newpreset, newarguments);
102         }
103       }).start();
104     }
105     else
106     {
107       preset = newpreset;
108       arguments = newarguments;
109       calcMan.startWorker(this);
110     }
111   }
112
113   public List<Option> getJabaArguments()
114   {
115     List<Option> newargs = new ArrayList<Option>();
116     if (preset != null && preset instanceof JabaWsParamSet)
117     {
118       newargs.addAll(((JabaWsParamSet) preset).getjabaArguments());
119     }
120     if (arguments != null && arguments.size() > 0)
121     {
122       for (Argument rg : arguments)
123       {
124         if (Option.class.isAssignableFrom(rg.getClass()))
125         {
126           newargs.add((Option) rg);
127         }
128       }
129     }
130     return newargs;
131   }
132
133   @Override
134   public void run()
135   {
136     if (aaservice == null)
137     {
138       return;
139     }
140     long progressId = -1;
141
142     String rslt = "JOB NOT DEFINED";
143
144     try
145     {
146       if (checkDone())
147       {
148         return;
149       }
150       List<compbio.data.sequence.FastaSequence> seqs = getInputSequences(alignViewport
151               .getAlignment());
152
153       if (seqs == null)
154       {
155         calcMan.workerComplete(this);
156         return;
157       }
158
159       AlignmentAnnotation[] aa = alignViewport.getAlignment()
160               .getAlignmentAnnotation();
161       if (guiProgress != null)
162       {
163         guiProgress.setProgressBar("JABA " + getServiceActionText(),
164                 progressId = System.currentTimeMillis());
165       }
166       if (preset == null && arguments==null)
167       {
168         rslt = aaservice.analize(seqs);
169       }
170       else
171       {
172         try
173         {
174           rslt = aaservice.customAnalize(seqs, getJabaArguments());
175         } catch (WrongParameterException x)
176         {
177           throw new JobSubmissionException(
178                   "Invalid paremeter set. Check Jalview implementation.", x);
179
180         }
181       }
182       boolean finished = false;
183       long rpos = 0;
184       do
185       {
186         JobStatus status = aaservice.getJobStatus(rslt);
187         if (status.equals(JobStatus.FINISHED))
188         {
189           finished = true;
190         }
191         if (calcMan.isPending(this) && this instanceof AAConsClient)
192         {
193           finished = true;
194           // cancel this job and yield to the new job
195           try
196           {
197             if (aaservice.cancelJob(rslt))
198             {
199               System.err.println("Cancelled AACon job: " + rslt);
200             }
201             else
202             {
203               System.err.println("FAILED TO CANCELL AACon job: " + rslt);
204             }
205
206           } catch (Exception x)
207           {
208
209           }
210
211           return;
212         }
213         long cpos;
214         ChunkHolder stats;
215         do
216         {
217           cpos = rpos;
218           try
219           {
220             stats = aaservice.pullExecStatistics(rslt, rpos);
221           } catch (Exception x)
222           {
223
224             if (x.getMessage().contains(
225                     "Position in a file could not be negative!"))
226             {
227               // squash index out of bounds exception- seems to happen for
228               // disorder predictors which don't (apparently) produce any
229               // progress information and JABA server throws an exception
230               // because progress length is -1.
231               stats = null;
232             }
233             else
234             {
235               throw x;
236             }
237           }
238           if (stats != null)
239           {
240             System.out.print(stats.getChunk());
241             rpos = stats.getNextPosition();
242           }
243         } while (stats != null && rpos > cpos);
244
245         if (!finished && status.equals(JobStatus.FAILED))
246         {
247           try
248           {
249             Thread.sleep(200);
250           } catch (InterruptedException x)
251           {
252           }
253           ;
254         }
255
256       } while (!finished);
257       try
258       {
259         Thread.sleep(200);
260       } catch (InterruptedException x)
261       {
262       }
263       ;
264       scoremanager = aaservice.getAnnotation(rslt);
265       if (scoremanager != null)
266       {
267         updateResultAnnotation(true);
268       }
269     } catch (JobSubmissionException x)
270     {
271
272       System.err.println("submission error:");
273       x.printStackTrace();
274       calcMan.workerCannotRun(this);
275     } catch (ResultNotAvailableException x)
276     {
277       System.err.println("collection error:\nJob ID: " + rslt);
278       x.printStackTrace();
279       calcMan.workerCannotRun(this);
280
281     } catch (OutOfMemoryError error)
282     {
283       calcMan.workerCannotRun(this);
284
285       // consensus = null;
286       // hconsensus = null;
287       ap.raiseOOMWarning(getServiceActionText(), error);
288     } catch (Exception x)
289     {
290       calcMan.workerCannotRun(this);
291
292       // consensus = null;
293       // hconsensus = null;
294       System.err
295               .println("Blacklisting worker due to unexpected exception:");
296       x.printStackTrace();
297     } finally
298     {
299
300       calcMan.workerComplete(this);
301       if (ap != null)
302       {
303         calcMan.workerComplete(this);
304         if (guiProgress != null && progressId!=-1)
305         {
306           guiProgress.setProgressBar("", progressId);
307         }
308         ap.paintAlignment(true);
309       }
310     }
311
312   }
313
314   @Override
315   public void updateAnnotation()
316   {
317     updateResultAnnotation(false);
318   }
319
320   public abstract void updateResultAnnotation(boolean immediate);
321
322   public abstract String getServiceActionText();
323
324   boolean submitGaps = true;
325
326   boolean alignedSeqs = true;
327
328   boolean nucleotidesAllowed = false;
329
330   boolean proteinAllowed = false;
331
332   /**
333    * record sequences for mapping result back to afterwards
334    */
335   protected boolean bySequence = false;
336
337   Map<String, SequenceI> seqNames;
338   boolean[] gapMap;
339   int realw;
340   public List<FastaSequence> getInputSequences(AlignmentI alignment)
341   {
342     if (alignment == null || alignment.getWidth() <= 0
343             || alignment.getSequences() == null
344             // || (alignedSeqs && !alignment.isAligned() && !submitGaps)
345             || alignment.isNucleotide() ? !nucleotidesAllowed
346             : !proteinAllowed)
347     {
348       return null;
349     }
350     List<compbio.data.sequence.FastaSequence> seqs = new ArrayList<compbio.data.sequence.FastaSequence>();
351
352     int minlen = 10;
353     int ln = -1;
354     if (bySequence)
355     {
356       seqNames = new HashMap<String, SequenceI>();
357     }
358     gapMap=new boolean[0];
359     for (SequenceI sq : ((List<SequenceI>) alignment.getSequences()))
360     {
361       if (sq.getEnd() - sq.getStart() > minlen - 1)
362       {
363         String newname = SeqsetUtils.unique_name(seqs.size() + 1);
364         // make new input sequence with or without gaps
365         if (seqNames != null)
366         {
367           seqNames.put(newname, sq);
368         }
369         FastaSequence seq;
370         if (submitGaps)
371         {
372           seqs.add(seq = new compbio.data.sequence.FastaSequence(newname,sq.getSequenceAsString()));
373           if (gapMap==null || gapMap.length<seq.getSequence().length())
374           {
375             boolean[] tg=gapMap;
376             gapMap=new boolean[seq.getLength()];
377             System.arraycopy(tg, 0, gapMap, 0, tg.length);
378             for (int p=tg.length;p<gapMap.length;p++)
379             {
380               gapMap[p]=false; // init as a gap
381             }
382           }
383           for (int apos:sq.gapMap()) {
384             gapMap[apos]=true; // aligned.
385           }
386         } else {
387         seqs.add(seq = new compbio.data.sequence.FastaSequence(newname,
388                 AlignSeq
389                         .extractGaps(jalview.util.Comparison.GapChars,
390                                 sq.getSequenceAsString())));
391         }
392         if (seq.getSequence().length() > ln)
393         {
394           ln = seq.getSequence().length();
395         }
396       }
397     }
398     if (alignedSeqs && submitGaps)
399     {
400       realw = 0;
401       for (int i=0;i<gapMap.length;i++)
402       {
403         if (gapMap[i])
404         {
405           realw++;
406         }
407       }
408       // try real hard to return something submittable
409       // TODO: some of AAcons measures need a minimum of two or three amino
410       // acids at each position, and aacons doesn't gracefully degrade.
411       for (int p = 0; p < seqs.size(); p++)
412       {
413         FastaSequence sq = seqs.get(p);
414         int l = sq.getSequence().length();
415         // strip gapped columns
416         char[] padded = new char[realw],orig=sq.getSequence().toCharArray();
417         for (int i=0,pp=0;i<realw; pp++)
418         {
419           if (gapMap[pp])
420           {
421             if (orig.length>pp)
422             {
423               padded[i++]=orig[pp];
424             } else {
425               padded[i++]='-';
426             }       
427           }
428         }
429         seqs.set(p, new compbio.data.sequence.FastaSequence(sq.getId(),
430                   new String(padded)));
431       }
432     }
433     return seqs;
434   }
435
436   /**
437    * notify manager that we have started, and wait for a free calculation slot
438    *
439    * @return true if slot is obtained and work still valid, false if another
440    *         thread has done our work for us.
441    */
442   boolean checkDone()
443   {
444     calcMan.notifyStart(this);
445     ap.paintAlignment(false);
446     while (!calcMan.notifyWorking(this))
447     {
448       if (calcMan.isWorking(this))
449       {
450         return true;
451       }
452       try
453       {
454         if (ap != null)
455         {
456           ap.paintAlignment(false);
457         }
458
459         Thread.sleep(200);
460       } catch (Exception ex)
461       {
462         ex.printStackTrace();
463       }
464     }
465     if (alignViewport.isClosed())
466     {
467       abortAndDestroy();
468       return true;
469     }
470     return false;
471   }
472
473 }