3 import jalview.datamodel.AlignmentView;
4 import jalview.datamodel.AlignmentOrder;
5 import jalview.datamodel.ColumnSelection;
6 import jalview.gui.WebserviceInfo;
7 import jalview.analysis.AlignSeq;
8 import jalview.bin.Cache;
9 import jalview.gui.AlignFrame;
10 import javax.swing.JOptionPane;
12 import vamsas.objects.simple.MsaResult;
13 import vamsas.objects.simple.Result;
14 import jalview.datamodel.Alignment;
15 import jalview.datamodel.SeqCigar;
16 import jalview.gui.Desktop;
17 import jalview.datamodel.SequenceI;
18 import java.util.Hashtable;
19 import java.util.Vector;
31 * Copyright: Copyright (c) 2004
35 * Company: Dundee University
38 * @author not attributable
41 class MsaWSThread extends Thread implements WSClientI {
42 jalview.gui.AlignFrame alignFrame;
44 WebserviceInfo wsInfo = null;
46 String WebServiceName = null;
50 boolean submitGaps = false; // pass sequences including gaps to alignment
54 boolean preserveOrder = true; // and always store and recover sequence
59 int jobnum = 0; // WebServiceInfo pane for this job
61 String jobId; // ws job ticket
63 vamsas.objects.simple.MsaResult result = null;
65 vamsas.objects.simple.SequenceSet seqs = new vamsas.objects.simple.SequenceSet();
75 public MsaWSJob(int jobNum, SequenceI[] inSeqs) {
77 if (!prepareInput(inSeqs,2)) {
80 result = new MsaResult();
81 result.setFinished(true);
82 result.setStatus("Job never ran - input returned to user.");
87 int allowedServerExceptions = 3; // thread dies if too many
90 boolean submitted=false;
91 boolean subjobComplete = false;
93 Hashtable SeqNames = new Hashtable();
94 Vector emptySeqs = new Vector();
96 * prepare input sequences for MsaWS service
97 * @param seqs jalview sequences to be prepared
98 * @param minlen minimum number of residues required for this MsaWS service
99 * @return true if seqs contains sequences to be submitted to service.
101 private boolean prepareInput(SequenceI[] seqs, int minlen) {
104 throw new Error("Implementation error: minlen must be zero or more.");
105 for (int i = 0; i < seqs.length; i++) {
106 if (seqs[i].getEnd()-seqs[i].getStart()>minlen-1) {
110 boolean valid=nseqs>1; // need at least two seqs
111 vamsas.objects.simple.Sequence[] seqarray =
113 ? new vamsas.objects.simple.Sequence[nseqs]
115 for (int i = 0, n = 0; i < seqs.length; i++) {
117 String newname = jalview.analysis.SeqsetUtils.unique_name(i); // same
121 SeqNames.put(newname, jalview.analysis.SeqsetUtils
122 .SeqCharacterHash(seqs[i]));
123 if (valid && seqs[i].getEnd()-seqs[i].getStart()>minlen-1) {
124 seqarray[n] = new vamsas.objects.simple.Sequence();
125 seqarray[n].setId(newname);
126 seqarray[n++].setSeq((submitGaps) ? seqs[i].getSequence()
127 : AlignSeq.extractGaps(
128 jalview.util.Comparison.GapChars, seqs[i]
132 if (seqs[i].getEnd()>=seqs[i].getStart()) {
133 empty = (submitGaps) ? seqs[i].getSequence()
134 : AlignSeq.extractGaps(
135 jalview.util.Comparison.GapChars, seqs[i]
138 emptySeqs.add(new String[] { newname, empty});
141 this.seqs = new vamsas.objects.simple.SequenceSet();
142 this.seqs.setSeqs(seqarray);
147 * @return true if getAlignment will return a valid alignment result.
149 public boolean hasResults() {
150 if (subjobComplete && result!=null && jobs[0].result.isFinished())
154 public Object[] getAlignment() {
156 if (result!=null && result.isFinished()) {
157 SequenceI[] alseqs=null;
158 char alseq_gapchar='-';
160 if (result.getMsa() != null) {
161 alseqs = getVamsasAlignment(result.getMsa());
162 alseq_gapchar=result.getMsa().getGapchar().charAt(0);
163 alseq_l = alseqs.length;
165 if (emptySeqs.size()>0) {
166 SequenceI[] t_alseqs = new SequenceI[alseq_l+emptySeqs.size()];
170 for (i=0,w=alseqs[0].getLength(); i<alseq_l; i++) {
171 if (w<alseqs[i].getLength())
172 w=alseqs[i].getLength();
173 t_alseqs[i] = alseqs[i];
177 // check that aligned width is at least as wide as emptySeqs width.
179 for (i=0, w=emptySeqs.size(); i<w; i++) {
180 String[] es=(String[]) emptySeqs.get(i);
181 if (es!=null && es[1]!=null) {
182 int sw=es[1].length();
187 // make a gapped string.
188 StringBuffer insbuff=new StringBuffer(w);
190 insbuff.append(alseq_gapchar);
192 for (i=0; i<alseq_l; i++) {
193 int sw=t_alseqs[i].getLength();
196 alseqs[i].setSequence(t_alseqs[i].getSequence()+insbuff.substring(0,sw-nw));
200 for (i=0, w=emptySeqs.size(); i<w; i++) {
201 String[] es=(String[]) emptySeqs.get(i);
203 t_alseqs[i+alseq_l] = new jalview.datamodel.Sequence(es[0], insbuff.toString(),1,0);
205 if (es[1].length()<nw) {
206 t_alseqs[i+alseq_l] = new jalview.datamodel.Sequence(es[0], es[1]+insbuff.substring(0, nw-es[1].length()),1,1+es[1].length());
208 t_alseqs[i+alseq_l] = new jalview.datamodel.Sequence(es[0], es[1]);
214 AlignmentOrder msaorder = new AlignmentOrder(alseqs);
215 // always recover the order - makes parseResult()'s life easier.
216 jalview.analysis.AlignmentSorter.recoverOrder(alseqs);
217 // account for any missing sequences
218 jalview.analysis.SeqsetUtils.deuniquify(SeqNames, alseqs);
219 return new Object[] { alseqs, msaorder};
224 MsaWSJob jobs[] = null;
226 String alTitle; // name which will be used to form new alignment window.
228 boolean jobComplete = false;
230 Alignment dataset; // dataset to which the new alignment will be
234 ext.vamsas.MuscleWS server = null;
239 * set basic options for this (group) of Msa jobs
246 MsaWSThread(ext.vamsas.MuscleWS server, String wsUrl,
247 WebserviceInfo wsinfo, jalview.gui.AlignFrame alFrame, AlignmentView alview,
248 String wsname, boolean subgaps, boolean presorder) {
249 this.server = server;
251 this.wsInfo = wsinfo;
252 this.WebServiceName = wsname;
254 this.submitGaps = subgaps;
255 this.preserveOrder = presorder;
256 this.alignFrame = alFrame;
260 * create one or more Msa jobs to align visible seuqences in _msa
273 MsaWSThread(ext.vamsas.MuscleWS server, String wsUrl,
274 WebserviceInfo wsinfo, jalview.gui.AlignFrame alFrame,
275 String wsname, String title, AlignmentView _msa, boolean subgaps,
276 boolean presorder, Alignment seqset) {
277 this(server, wsUrl, wsinfo, alFrame, _msa, wsname, subgaps, presorder);
278 OutputHeader = wsInfo.getProgressText();
281 SeqCigar[] msa = _msa.getSequences();
282 int[] contigs = _msa.getContigs();
284 if (contigs != null && contigs.length > 0) {
287 int width = _msa.getWidth();
288 for (int contig = 0; contig < contigs.length; contig += 3) {
289 if ((contigs[contig+1] - start) > 0) {
292 width+=contigs[contig+2];// end up with full region width (including hidden regions)
293 start = contigs[contig+1] + contigs[contig + 2];
298 jobs = new MsaWSJob[njobs];
301 for (int contig = 0; contig < contigs.length; contig += 3) {
302 if (contigs[contig+1] - start > 0) {
303 SequenceI mseq[] = new SequenceI[msa.length];
304 for (int s = 0; s < mseq.length; s++) {
305 mseq[s] = msa[s].getSeq('-').getSubSequence(start,
309 jobs[j] = new MsaWSJob(wsinfo.addJobPane(), mseq);
311 jobs[j] = new MsaWSJob(0, mseq);
313 wsinfo.setProgressName("region "+jobs[j].jobnum,jobs[j].jobnum);
314 wsinfo.setProgressText(jobs[j].jobnum, OutputHeader);
317 start = contigs[contig+1] + contigs[contig + 2];
320 SequenceI mseq[] = new SequenceI[msa.length];
321 for (int s = 0; s < mseq.length; s++) {
322 mseq[s] = msa[s].getSeq('-').getSubSequence(start,
326 jobs[j] = new MsaWSJob(wsinfo.addJobPane(), mseq);
328 jobs[j] = new MsaWSJob(0, mseq);
330 wsinfo.setProgressName("region "+jobs[j].jobnum,jobs[j].jobnum);
331 wsinfo.setProgressText(jobs[j].jobnum, OutputHeader);
335 SequenceI mseq[] = new SequenceI[msa.length];
336 for (int s = 0; s < mseq.length; s++) {
337 mseq[s] = msa[s].getSeq('-');
339 jobs = new MsaWSJob[1];
340 wsinfo.setProgressText(OutputHeader); // ensures default text
341 jobs[0] = new MsaWSJob(0, mseq);
345 public boolean isCancellable() {
349 public void cancelJob() {
350 if (!jobComplete && jobs != null) {
351 boolean cancelled = true;
352 for (int job = 0; job < jobs.length; job++) {
353 if (jobs[job].submitted && !jobs[job].subjobComplete)
355 String cancelledMessage = "";
357 vamsas.objects.simple.WsJobId cancelledJob = server
358 .cancel(jobs[job].jobId);
359 if (cancelledJob.getStatus() == 2) {
361 cancelledMessage = "Job cancelled.";
362 jobs[job].subjobComplete = true;
363 jobs[job].result = null;
364 wsInfo.setStatus(jobs[job].jobnum, WebserviceInfo.STATE_CANCELLED_OK);
365 } else if (cancelledJob.getStatus() == 3) {
366 // VALID UNSTOPPABLE JOB
367 cancelledMessage += "Server cannot cancel this job. just close the window.\n";
369 wsInfo.setStatus(jobs[job].jobnum, WebserviceInfo.STATE_CANCELLED_OK);
372 if (cancelledJob.getJobId() != null) {
373 cancelledMessage += ("[" + cancelledJob.getJobId() + "]");
376 cancelledMessage += "\n";
377 } catch (Exception exc) {
378 cancelledMessage += ("\nProblems cancelling the job : Exception received...\n"
380 exc.printStackTrace();
382 wsInfo.setProgressText(jobs[job].jobnum, OutputHeader
383 + cancelledMessage + "\n");
387 wsInfo.setStatus(WebserviceInfo.STATE_CANCELLED_OK);
393 .setProgressText(OutputHeader
394 + "Server cannot cancel this job because it has not been submitted properly. just close the window.\n");
401 while (!jobComplete) {
407 for (int j=0; j<jobs.length; j++) {
409 if (!jobs[j].submitted && jobs[j].seqs.getSeqs()!=null)
412 if (jobs[j].submitted && !jobs[j].subjobComplete) {
414 if ((jobs[j].result = server.getResult(jobs[j].jobId)) == null) {
415 throw (new Exception(
416 "Timed out when communicating with server\nTry again later.\n"));
418 jalview.bin.Cache.log.debug("Job "+j+" Result state " + jobs[j].result.getState()
419 + "(ServerError=" + jobs[j].result.isServerError() + ")");
420 } catch (Exception ex) {
421 // Deal with Transaction exceptions
422 wsInfo.appendProgressText(jobs[j].jobnum, "\n" + WebServiceName
423 + " Server exception!\n" + ex.getMessage());
424 Cache.log.warn(WebServiceName + " job(" + jobs[j].jobnum
425 + ") Server exception: " + ex.getMessage());
427 if (jobs[j].allowedServerExceptions > 0) {
428 jobs[j].allowedServerExceptions--;
429 Cache.log.debug("Sleeping after a server exception.");
433 catch (InterruptedException ex1) {
436 Cache.log.warn("Dropping job "+j+" "+jobs[j].jobId);
437 jobs[j].subjobComplete=true;
438 wsInfo.setStatus(jobs[j].jobnum, WebserviceInfo.STATE_STOPPED_SERVERERROR);
441 catch (OutOfMemoryError er) {
443 jobs[j].subjobComplete=true;
444 jobs[j].result=null; // may contain out of date result object
445 wsInfo.setStatus(jobs[j].jobnum,
446 WebserviceInfo.STATE_STOPPED_ERROR);
448 .showInternalMessageDialog(
450 "Out of memory handling result for job !!"
451 + "\nSee help files for increasing Java Virtual Machine memory.",
452 "Out of memory", JOptionPane.WARNING_MESSAGE);
453 Cache.log.error("Out of memory when retrieving Job "+j+" id:" + WsUrl+"/"+jobs[j].jobId, er);
457 if (jobs[j].result!=null) {
458 String progheader="";
459 // Parse state of job[j]
460 if (jobs[j].result.isRunning()) {
462 wsInfo.setStatus(jobs[j].jobnum,WebserviceInfo.STATE_RUNNING);
463 } else if (jobs[j].result.isQueued()) {
465 wsInfo.setStatus(jobs[j].jobnum,WebserviceInfo.STATE_QUEUING);
466 } else if (jobs[j].result.isFinished()) {
468 jobs[j].subjobComplete = true;
469 wsInfo.setStatus(jobs[j].jobnum,WebserviceInfo.STATE_STOPPED_OK);
470 } else if (jobs[j].result.isFailed()) {
471 progheader += "Job failed.\n";
472 jobs[j].subjobComplete=true;
473 wsInfo.setStatus(jobs[j].jobnum,WebserviceInfo.STATE_STOPPED_ERROR);
475 } else if (jobs[j].result.isServerError()) {
477 jobs[j].subjobComplete = true;
478 wsInfo.setStatus(jobs[j].jobnum,WebserviceInfo.STATE_STOPPED_SERVERERROR);
479 } else if (jobs[j].result.isBroken() || jobs[j].result.isFailed()) {
481 jobs[j].subjobComplete=true;
482 wsInfo.setStatus(jobs[j].jobnum,WebserviceInfo.STATE_STOPPED_ERROR);
484 // and pass on any sub-job messages to the user
485 wsInfo.setProgressText(jobs[j].jobnum, OutputHeader);
486 wsInfo.appendProgressText(jobs[j].jobnum, progheader);
487 if (jobs[j].result.getStatus() != null) {
488 wsInfo.appendProgressText(jobs[j].jobnum, jobs[j].result.getStatus());
491 if (jobs[j].submitted && jobs[j].subjobComplete) {
492 if (jobs[j].allowedServerExceptions==0) {
494 } else if (jobs[j].result==null) {
500 // Decide on overall state based on collected jobs[] states
502 wsInfo.setStatus(WebserviceInfo.STATE_RUNNING);
503 } else if (queuing>0) {
504 wsInfo.setStatus(WebserviceInfo.STATE_QUEUING);
508 wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_OK);
509 } else if (error>0) {
510 wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_ERROR);
511 } else if (serror>0) {
512 wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_SERVERERROR);
519 catch (InterruptedException e) {
520 Cache.log.debug("Interrupted sleep waiting for next job poll.",e);
522 // System.out.println("I'm alive "+alTitle);
526 parseResult(); // tidy up and make results available to user
530 void StartJob(MsaWSJob j) {
532 if (Cache.log.isDebugEnabled()) {
533 Cache.log.debug("Tried to submit an already submitted job "+j.jobId);
537 if (j.seqs.getSeqs()==null) {
538 // special case - selection consisted entirely of empty sequences...
540 j.result=new MsaResult();
541 j.result.setFinished(true);
542 j.result.setStatus("Empty Alignment Job");
543 j.result.setMsa(null);
546 vamsas.objects.simple.WsJobId jobsubmit = server.align(j.seqs);
548 if ((jobsubmit != null) && (jobsubmit.getStatus() == 1)) {
549 j.jobId = jobsubmit.getJobId();
551 j.subjobComplete = false;
552 // System.out.println(WsURL + " Job Id '" + jobId + "'");
554 if (jobsubmit == null) {
558 + " returned null object, it probably cannot be contacted. Try again later ?");
561 throw new Exception(jobsubmit.getJobId());
563 } catch (Exception e) {
564 // TODO: JBPNote catch timeout or other fault types explicitly
565 // For unexpected errors
567 .println(WebServiceName
568 + "Client: Failed to submit the sequences for alignment (probably a server side problem)\n"
569 + "When contacting Server:" + WsUrl + "\n"
570 + e.toString() + "\n");
571 j.allowedServerExceptions = 0;
572 wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_SERVERERROR);
573 wsInfo.setStatus(j.jobnum, WebserviceInfo.STATE_STOPPED_SERVERERROR);
577 "Failed to submit sequences for alignment.\n"
578 + "It is most likely that there is a problem with the server.\n"
579 + "Just close the window\n");
581 // e.printStackTrace(); // TODO: JBPNote DEBUG
585 private jalview.datamodel.Sequence[] getVamsasAlignment(
586 vamsas.objects.simple.Alignment valign) {
587 vamsas.objects.simple.Sequence[] seqs = valign.getSeqs().getSeqs();
588 jalview.datamodel.Sequence[] msa = new jalview.datamodel.Sequence[seqs.length];
590 for (int i = 0, j = seqs.length; i < j; i++) {
591 msa[i] = new jalview.datamodel.Sequence(seqs[i].getId(), seqs[i]
600 for (int j=0; j<jobs.length; j++) {
601 if (jobs[j].submitted && jobs[j].subjobComplete) {
602 if (jobs[j].result!=null) {
603 vamsas.objects.simple.Alignment valign = jobs[j].result.getMsa();
605 wsInfo.appendProgressText(jobs[j].jobnum,"\nAlignment Object Method Notes\n");
606 String[] lines = valign.getMethod();
607 for (int line = 0; line < lines.length; line++) {
608 wsInfo.appendProgressText(jobs[j].jobnum, lines[line] + "\n");
610 // JBPNote The returned files from a webservice could be
611 // hidden behind icons in the monitor window that,
612 // when clicked, pop up their corresponding data
618 catch (Exception ex) {
620 Cache.log.error("Unexpected exception when processing results for "+alTitle,ex);
621 wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_ERROR);
624 wsInfo.showResultsNewFrame
625 .addActionListener(new java.awt.event.ActionListener() {
626 public void actionPerformed(
627 java.awt.event.ActionEvent evt) {
628 displayResults(true);
632 .addActionListener(new java.awt.event.ActionListener() {
633 public void actionPerformed(
634 java.awt.event.ActionEvent evt) {
635 displayResults(false);
638 wsInfo.setResultsReady();
640 void displayResults(boolean newFrame) {
641 int contigs[] = input.getContigs();
642 SeqCigar[] seqs = input.getSequences();
643 SequenceI[] alignment = new SequenceI[seqs.length];
644 ColumnSelection columnselection = new ColumnSelection();
645 Vector alorders=new Vector();
646 if (contigs != null && contigs.length > 0) {
651 for (int contig = 0; contig < contigs.length; contig += 3) {
652 if (cshift+contigs[contig] - start > 0) {
653 Object[] subalg = jobs[j++].getAlignment();
654 alorders.add(subalg[1]);
655 SequenceI mseq[] = (SequenceI[]) subalg[0];
656 int width = mseq[0].getLength();
657 for (int s = 0; s < mseq.length; s++) {
658 if (alignment[s]==null) {
659 alignment[s] = mseq[s];
661 alignment[s].setSequence(alignment[s].getSequence()+mseq[s].getSequence());
662 if (mseq[s].getStart()<=mseq[s].getEnd())
663 alignment[s].setEnd(mseq[s].getEnd());
664 ((AlignmentOrder) subalg[1]).updateSequence(mseq[s], alignment[s]);
669 // advance to begining of visible region
670 start = cshift+contigs[contig] + contigs[contig + 2];
671 // add hidden segment to right of next region
672 for (int s=0; s<seqs.length; s++) {
673 SequenceI hseq = seqs[s].getSeq('-').getSubSequence(cshift+contigs[contig], start);
674 if (alignment[s]==null) {
677 alignment[s].setSequence(alignment[s].getSequence()+hseq.getSequence());
678 if (hseq.getEnd()>=hseq.getStart())
679 alignment[s].setEnd(hseq.getEnd());
682 // mark hidden segment as hidden in the new alignment
683 columnselection.hideColumns(nwidth, nwidth+contigs[contig+2]-1);
684 nwidth+=contigs[contig+2];
685 cshift+=contigs[contig+2];
687 // Do final job - if it exists
688 if (j<jobs.length && jobs[j].hasResults()) {
689 Object[] subalg = jobs[j].getAlignment();
690 alorders.add(subalg[1]);
691 SequenceI mseq[] = (SequenceI[]) subalg[0];
692 int width = mseq[0].getLength();
693 for (int s = 0; s < mseq.length; s++) {
694 if (alignment[s]==null) {
695 alignment[s] = mseq[s];
697 alignment[s].setSequence(alignment[s].getSequence()+mseq[s].getSequence());
698 if (mseq[s].getEnd()>=mseq[s].getStart())
699 alignment[s].setEnd(mseq[s].getEnd());
700 ((AlignmentOrder) subalg[1]).updateSequence(mseq[s], alignment[s]);
706 if (jobs[0].hasResults()) {
707 Object[] alg = jobs[0].getAlignment();
708 alignment = (SequenceI[]) alg[0];
709 alorders.add(alg[1]);
711 alignment = SeqCigar.createAlignmentSequences(seqs, '-', columnselection,null);
714 Alignment al = new Alignment(alignment);
715 if (dataset != null) {
716 al.setDataset(dataset);
720 // TODO: JBPNote Should also rename the query sequence
722 AlignFrame af = new AlignFrame(al,columnselection);
724 // >>>This is a fix for the moment, until a better solution is
726 af.getFeatureRenderer().transferSettings(
727 alignFrame.getFeatureRenderer());
728 if (alorders.size()>0) {
729 if (alorders.size()==1) {
730 af.addSortByOrderMenuItem(WebServiceName + " Ordering",
731 (AlignmentOrder) alorders.get(0));
733 // construct a non-redundant ordering set
734 Vector names=new Vector();
735 for (int i=0,l=alorders.size(); i<l; i++) {
736 String orderName = new String("Region "+i);
740 if (((AlignmentOrder) alorders.get(i)).equals(((AlignmentOrder) alorders.get(j)))) {
741 alorders.remove(j); l--;
748 names.add(new String(""));
750 names.add(orderName);
752 for (int i=0,l=alorders.size(); i<l; i++) {
753 af.addSortByOrderMenuItem(WebServiceName + ((String) names.get(i))+" Ordering",
754 (AlignmentOrder) alorders.get(i));
759 Desktop.addInternalFrame(af, alTitle,
760 AlignFrame.NEW_WINDOW_WIDTH,
761 AlignFrame.NEW_WINDOW_HEIGHT);
764 System.out.println("MERGE WITH OLD FRAME");