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