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