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