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) {
80 int allowedServerExceptions = 3; // thread dies if too many
83 boolean submitted=false;
84 boolean subjobComplete = false;
86 Hashtable SeqNames = new Hashtable();
87 Vector emptySeqs = new Vector();
88 private void prepareInput(SequenceI[] seqs) {
90 for (int i = 0; i < seqs.length; i++) {
91 if (seqs[i].getStart() < seqs[i].getEnd()) {
95 vamsas.objects.simple.Sequence[] seqarray =
97 ? new vamsas.objects.simple.Sequence[nseqs]
99 for (int i = 0, n = 0; i < seqs.length; i++) {
101 String newname = jalview.analysis.SeqsetUtils.unique_name(i); // same
105 SeqNames.put(newname, jalview.analysis.SeqsetUtils
106 .SeqCharacterHash(seqs[i]));
107 if (seqs[i].getStart() < seqs[i].getEnd()) {
108 seqarray[n] = new vamsas.objects.simple.Sequence();
109 seqarray[n].setId(newname);
110 seqarray[n++].setSeq((submitGaps) ? seqs[i].getSequence()
111 : AlignSeq.extractGaps(
112 jalview.util.Comparison.GapChars, seqs[i]
115 emptySeqs.add(newname);
118 this.seqs = new vamsas.objects.simple.SequenceSet();
119 this.seqs.setSeqs(seqarray);
123 * @return true if getAlignment will return a valid alignment result.
125 public boolean hasResults() {
126 if (subjobComplete && result!=null && jobs[0].result.isFinished())
130 public Object[] getAlignment() {
132 if (result!=null && result.isFinished()) {
133 SequenceI[] alseqs=null;
134 char alseq_gapchar='-';
136 if (result.getMsa() != null) {
137 alseqs = getVamsasAlignment(result.getMsa());
138 alseq_gapchar=result.getMsa().getGapchar().charAt(0);
139 alseq_l = alseqs.length;
141 if (emptySeqs.size()>0) {
142 SequenceI[] t_alseqs = new SequenceI[alseq_l+emptySeqs.size()];
146 for (i=0,w=alseqs[0].getLength(); i<alseq_l; i++) {
147 if (w<alseqs[i].getLength())
148 w=alseqs[i].getLength();
149 t_alseqs[i] = alseqs[i];
153 // make a gapped string.
154 StringBuffer insbuff=new StringBuffer(w);
156 insbuff.append(alseq_gapchar);
157 for (i=0, w=emptySeqs.size(); i<w; i++) {
158 t_alseqs[i+alseqs.length] = new jalview.datamodel.Sequence((String)emptySeqs.get(i), insbuff.toString());
162 AlignmentOrder msaorder = new AlignmentOrder(alseqs);
163 // always recover the order - makes parseResult()'s life easier.
164 jalview.analysis.AlignmentSorter.recoverOrder(alseqs);
165 // account for any missing sequences
166 jalview.analysis.SeqsetUtils.deuniquify(SeqNames, alseqs);
167 return new Object[] { alseqs, msaorder};
172 MsaWSJob jobs[] = null;
174 String alTitle; // name which will be used to form new alignment window.
176 boolean jobComplete = false;
178 Alignment dataset; // dataset to which the new alignment will be
182 ext.vamsas.MuscleWS server = null;
187 * set basic options for this (group) of Msa jobs
194 MsaWSThread(ext.vamsas.MuscleWS server, String wsUrl,
195 WebserviceInfo wsinfo, jalview.gui.AlignFrame alFrame, AlignmentView alview,
196 String wsname, boolean subgaps, boolean presorder) {
197 this.server = server;
199 this.wsInfo = wsinfo;
200 this.WebServiceName = wsname;
202 this.submitGaps = subgaps;
203 this.preserveOrder = presorder;
204 this.alignFrame = alFrame;
208 * create one or more Msa jobs to align visible seuqences in _msa
221 MsaWSThread(ext.vamsas.MuscleWS server, String wsUrl,
222 WebserviceInfo wsinfo, jalview.gui.AlignFrame alFrame,
223 String wsname, String title, AlignmentView _msa, boolean subgaps,
224 boolean presorder, Alignment seqset) {
225 this(server, wsUrl, wsinfo, alFrame, _msa, wsname, subgaps, presorder);
226 OutputHeader = wsInfo.getProgressText();
229 SeqCigar[] msa = _msa.getSequences();
230 int[] contigs = _msa.getContigs();
232 if (contigs != null && contigs.length > 0) {
235 int width = _msa.getWidth();
236 for (int contig = 0; contig < contigs.length; contig += 3) {
237 if ((contigs[contig+1] - start) > 0) {
240 width+=contigs[contig+2];// end up with full region width (including hidden regions)
241 start = contigs[contig+1] + contigs[contig + 2];
246 jobs = new MsaWSJob[njobs];
249 for (int contig = 0; contig < contigs.length; contig += 3) {
250 if (contigs[contig+1] - start > 0) {
251 SequenceI mseq[] = new SequenceI[msa.length];
252 for (int s = 0; s < mseq.length; s++) {
253 mseq[s] = msa[s].getSeq('-').getSubSequence(start,
257 jobs[j] = new MsaWSJob(wsinfo.addJobPane(), mseq);
259 jobs[j] = new MsaWSJob(0, mseq);
261 wsinfo.setProgressName("region "+jobs[j].jobnum,jobs[j].jobnum);
262 wsinfo.setProgressText(jobs[j].jobnum, OutputHeader);
265 start = contigs[contig+1] + contigs[contig + 2];
268 SequenceI mseq[] = new SequenceI[msa.length];
269 for (int s = 0; s < mseq.length; s++) {
270 mseq[s] = msa[s].getSeq('-').getSubSequence(start,
274 jobs[j] = new MsaWSJob(wsinfo.addJobPane(), mseq);
276 jobs[j] = new MsaWSJob(0, mseq);
278 wsinfo.setProgressName("region "+jobs[j].jobnum,jobs[j].jobnum);
279 wsinfo.setProgressText(jobs[j].jobnum, OutputHeader);
283 SequenceI mseq[] = new SequenceI[msa.length];
284 for (int s = 0; s < mseq.length; s++) {
285 mseq[s] = msa[s].getSeq('-');
287 jobs = new MsaWSJob[1];
288 wsinfo.setProgressText(OutputHeader); // ensures default text
289 jobs[0] = new MsaWSJob(0, mseq);
293 public boolean isCancellable() {
297 public void cancelJob() {
298 if (!jobComplete && jobs != null) {
299 boolean cancelled = true;
300 for (int job = 0; job < jobs.length; job++) {
301 if (jobs[job].submitted && !jobs[job].subjobComplete)
303 String cancelledMessage = "";
305 vamsas.objects.simple.WsJobId cancelledJob = server
306 .cancel(jobs[job].jobId);
307 if (cancelledJob.getStatus() == 2) {
309 cancelledMessage = "Job cancelled.";
310 jobs[job].subjobComplete = true;
311 jobs[job].result = null;
312 wsInfo.setStatus(jobs[job].jobnum, WebserviceInfo.STATE_CANCELLED_OK);
313 } else if (cancelledJob.getStatus() == 3) {
314 // VALID UNSTOPPABLE JOB
315 cancelledMessage += "Server cannot cancel this job. just close the window.\n";
317 wsInfo.setStatus(jobs[job].jobnum, WebserviceInfo.STATE_CANCELLED_OK);
320 if (cancelledJob.getJobId() != null) {
321 cancelledMessage += ("[" + cancelledJob.getJobId() + "]");
324 cancelledMessage += "\n";
325 } catch (Exception exc) {
326 cancelledMessage += ("\nProblems cancelling the job : Exception received...\n"
328 exc.printStackTrace();
330 wsInfo.setProgressText(jobs[job].jobnum, OutputHeader
331 + cancelledMessage + "\n");
335 wsInfo.setStatus(WebserviceInfo.STATE_CANCELLED_OK);
341 .setProgressText(OutputHeader
342 + "Server cannot cancel this job because it has not been submitted properly. just close the window.\n");
349 while (!jobComplete) {
355 for (int j=0; j<jobs.length; j++) {
357 if (!jobs[j].submitted && jobs[j].seqs.getSeqs()!=null)
360 if (jobs[j].submitted && !jobs[j].subjobComplete) {
362 if ((jobs[j].result = server.getResult(jobs[j].jobId)) == null) {
363 throw (new Exception(
364 "Timed out when communicating with server\nTry again later.\n"));
366 jalview.bin.Cache.log.debug("Job "+j+" Result state " + jobs[j].result.getState()
367 + "(ServerError=" + jobs[j].result.isServerError() + ")");
368 } catch (Exception ex) {
369 // Deal with Transaction exceptions
370 wsInfo.appendProgressText(jobs[j].jobnum, "\n" + WebServiceName
371 + " Server exception!\n" + ex.getMessage());
372 Cache.log.warn(WebServiceName + " job(" + jobs[j].jobnum
373 + ") Server exception: " + ex.getMessage());
375 if (jobs[j].allowedServerExceptions > 0) {
376 jobs[j].allowedServerExceptions--;
377 Cache.log.debug("Sleeping after a server exception.");
381 catch (InterruptedException ex1) {
384 Cache.log.warn("Dropping job "+j+" "+jobs[j].jobId);
385 jobs[j].subjobComplete=true;
386 wsInfo.setStatus(jobs[j].jobnum, WebserviceInfo.STATE_STOPPED_SERVERERROR);
389 catch (OutOfMemoryError er) {
391 jobs[j].subjobComplete=true;
392 jobs[j].result=null; // may contain out of date result object
393 wsInfo.setStatus(jobs[j].jobnum,
394 WebserviceInfo.STATE_STOPPED_ERROR);
396 .showInternalMessageDialog(
398 "Out of memory handling result for job !!"
399 + "\nSee help files for increasing Java Virtual Machine memory.",
400 "Out of memory", JOptionPane.WARNING_MESSAGE);
401 Cache.log.error("Out of memory when retrieving Job "+j+" id:" + WsUrl+"/"+jobs[j].jobId, er);
405 if (jobs[j].result!=null) {
406 String progheader="";
407 // Parse state of job[j]
408 if (jobs[j].result.isRunning()) {
410 wsInfo.setStatus(jobs[j].jobnum,WebserviceInfo.STATE_RUNNING);
411 } else if (jobs[j].result.isQueued()) {
413 wsInfo.setStatus(jobs[j].jobnum,WebserviceInfo.STATE_QUEUING);
414 } else if (jobs[j].result.isFinished()) {
416 jobs[j].subjobComplete = true;
417 wsInfo.setStatus(jobs[j].jobnum,WebserviceInfo.STATE_STOPPED_OK);
418 } else if (jobs[j].result.isFailed()) {
419 progheader += "Job failed.\n";
420 jobs[j].subjobComplete=true;
421 wsInfo.setStatus(jobs[j].jobnum,WebserviceInfo.STATE_STOPPED_ERROR);
423 } else if (jobs[j].result.isServerError()) {
425 jobs[j].subjobComplete = true;
426 wsInfo.setStatus(jobs[j].jobnum,WebserviceInfo.STATE_STOPPED_SERVERERROR);
427 } else if (jobs[j].result.isBroken() || jobs[j].result.isFailed()) {
429 jobs[j].subjobComplete=true;
430 wsInfo.setStatus(jobs[j].jobnum,WebserviceInfo.STATE_STOPPED_ERROR);
432 // and pass on any sub-job messages to the user
433 wsInfo.setProgressText(jobs[j].jobnum, OutputHeader);
434 wsInfo.appendProgressText(jobs[j].jobnum, progheader);
435 if (jobs[j].result.getStatus() != null) {
436 wsInfo.appendProgressText(jobs[j].jobnum, jobs[j].result.getStatus());
439 if (jobs[j].submitted && jobs[j].subjobComplete) {
440 if (jobs[j].allowedServerExceptions==0) {
442 } else if (jobs[j].result==null) {
448 // Decide on overall state based on collected jobs[] states
450 wsInfo.setStatus(WebserviceInfo.STATE_RUNNING);
451 } else if (queuing>0) {
452 wsInfo.setStatus(WebserviceInfo.STATE_QUEUING);
456 wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_OK);
457 } else if (error>0) {
458 wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_ERROR);
459 } else if (serror>0) {
460 wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_SERVERERROR);
467 catch (InterruptedException e) {
468 Cache.log.debug("Interrupted sleep waiting for next job poll.",e);
470 // System.out.println("I'm alive "+alTitle);
474 parseResult(); // tidy up and make results available to user
478 void StartJob(MsaWSJob j) {
480 if (Cache.log.isDebugEnabled()) {
481 Cache.log.debug("Tried to submit an already submitted job "+j.jobId);
485 if (j.seqs.getSeqs()==null) {
486 // special case - selection consisted entirely of empty sequences...
488 j.result=new MsaResult();
489 j.result.setFinished(true);
490 j.result.setStatus("Empty Alignment Job");
491 j.result.setMsa(null);
494 vamsas.objects.simple.WsJobId jobsubmit = server.align(j.seqs);
496 if ((jobsubmit != null) && (jobsubmit.getStatus() == 1)) {
497 j.jobId = jobsubmit.getJobId();
499 j.subjobComplete = false;
500 // System.out.println(WsURL + " Job Id '" + jobId + "'");
502 if (jobsubmit == null) {
506 + " returned null object, it probably cannot be contacted. Try again later ?");
509 throw new Exception(jobsubmit.getJobId());
511 } catch (Exception e) {
512 // TODO: JBPNote catch timeout or other fault types explicitly
513 // For unexpected errors
515 .println(WebServiceName
516 + "Client: Failed to submit the sequences for alignment (probably a server side problem)\n"
517 + "When contacting Server:" + WsUrl + "\n"
518 + e.toString() + "\n");
519 j.allowedServerExceptions = 0;
520 wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_SERVERERROR);
521 wsInfo.setStatus(j.jobnum, WebserviceInfo.STATE_STOPPED_SERVERERROR);
525 "Failed to submit sequences for alignment.\n"
526 + "It is most likely that there is a problem with the server.\n"
527 + "Just close the window\n");
529 // e.printStackTrace(); // TODO: JBPNote DEBUG
533 private jalview.datamodel.Sequence[] getVamsasAlignment(
534 vamsas.objects.simple.Alignment valign) {
535 vamsas.objects.simple.Sequence[] seqs = valign.getSeqs().getSeqs();
536 jalview.datamodel.Sequence[] msa = new jalview.datamodel.Sequence[seqs.length];
538 for (int i = 0, j = seqs.length; i < j; i++) {
539 msa[i] = new jalview.datamodel.Sequence(seqs[i].getId(), seqs[i]
548 for (int j=0; j<jobs.length; j++) {
549 if (jobs[j].submitted && jobs[j].subjobComplete) {
550 if (jobs[j].result!=null) {
551 vamsas.objects.simple.Alignment valign = jobs[j].result.getMsa();
553 wsInfo.appendProgressText(jobs[j].jobnum,"\nAlignment Object Method Notes\n");
554 String[] lines = valign.getMethod();
555 for (int line = 0; line < lines.length; line++) {
556 wsInfo.appendProgressText(jobs[j].jobnum, lines[line] + "\n");
558 // JBPNote The returned files from a webservice could be
559 // hidden behind icons in the monitor window that,
560 // when clicked, pop up their corresponding data
566 catch (Exception ex) {
568 Cache.log.error("Unexpected exception when processing results for "+alTitle,ex);
569 wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_ERROR);
572 wsInfo.showResultsNewFrame
573 .addActionListener(new java.awt.event.ActionListener() {
574 public void actionPerformed(
575 java.awt.event.ActionEvent evt) {
576 displayResults(true);
580 .addActionListener(new java.awt.event.ActionListener() {
581 public void actionPerformed(
582 java.awt.event.ActionEvent evt) {
583 displayResults(false);
586 wsInfo.setResultsReady();
588 void displayResults(boolean newFrame) {
589 int contigs[] = input.getContigs();
590 SeqCigar[] seqs = input.getSequences();
591 SequenceI[] alignment = new SequenceI[seqs.length];
592 ColumnSelection columnselection = new ColumnSelection();
593 Vector alorders=new Vector();
594 if (contigs != null && contigs.length > 0) {
599 for (int contig = 0; contig < contigs.length; contig += 3) {
600 if (cshift+contigs[contig] - start > 0) {
601 Object[] subalg = jobs[j++].getAlignment();
602 alorders.add(subalg[1]);
603 SequenceI mseq[] = (SequenceI[]) subalg[0];
604 int width = mseq[0].getLength();
605 for (int s = 0; s < mseq.length; s++) {
606 if (alignment[s]==null) {
607 alignment[s] = mseq[s];
609 alignment[s].setSequence(alignment[s].getSequence()+mseq[s].getSequence());
610 if (alignment[s].getEnd()<mseq[s].getEnd())
611 alignment[s].setEnd(mseq[s].getEnd());
612 ((AlignmentOrder) subalg[1]).updateSequence(mseq[s], alignment[s]);
617 // advance to begining of visible region
618 start = cshift+contigs[contig] + contigs[contig + 2];
619 // add hidden segment to right of next region
620 for (int s=0; s<seqs.length; s++) {
621 SequenceI hseq = seqs[s].getSeq('-').getSubSequence(cshift+contigs[contig], start);
622 if (alignment[s]==null) {
625 alignment[s].setSequence(alignment[s].getSequence()+hseq.getSequence());
626 alignment[s].setEnd(hseq.getEnd());
629 // mark hidden segment as hidden in the new alignment
630 columnselection.hideColumns(nwidth, nwidth+contigs[contig+2]-1);
631 nwidth+=contigs[contig+2];
632 cshift+=contigs[contig+2];
634 // Do final job - if it exists
636 Object[] subalg = jobs[j].getAlignment();
637 alorders.add(subalg[1]);
638 SequenceI mseq[] = (SequenceI[]) subalg[0];
639 int width = mseq[0].getLength();
640 for (int s = 0; s < mseq.length; s++) {
641 if (alignment[s]==null) {
642 alignment[s] = mseq[s];
644 alignment[s].setSequence(alignment[s].getSequence()+mseq[s].getSequence());
645 alignment[s].setEnd(mseq[s].getEnd());
646 ((AlignmentOrder) subalg[1]).updateSequence(mseq[s], alignment[s]);
652 if (jobs[0].hasResults()) {
653 Object[] alg = jobs[0].getAlignment();
654 alignment = (SequenceI[]) alg[0];
655 alorders.add(alg[1]);
657 alignment = SeqCigar.createAlignmentSequences(seqs, '-', columnselection,null);
660 Alignment al = new Alignment(alignment);
661 if (dataset != null) {
662 al.setDataset(dataset);
666 // TODO: JBPNote Should also rename the query sequence
668 AlignFrame af = new AlignFrame(al,columnselection);
670 // >>>This is a fix for the moment, until a better solution is
672 af.getFeatureRenderer().transferSettings(
673 alignFrame.getFeatureRenderer());
674 if (alorders.size()>0) {
675 if (alorders.size()==1) {
676 af.addSortByOrderMenuItem(WebServiceName + " Ordering",
677 (AlignmentOrder) alorders.get(0));
679 // construct a non-redundant ordering set
680 Vector names=new Vector();
681 for (int i=0,l=alorders.size(); i<l; i++) {
682 String orderName = new String("Region "+i);
686 if (((AlignmentOrder) alorders.get(i)).equals(((AlignmentOrder) alorders.get(j)))) {
687 alorders.remove(j); l--;
694 names.add(new String(""));
696 names.add(orderName);
698 for (int i=0,l=alorders.size(); i<l; i++) {
699 af.addSortByOrderMenuItem(WebServiceName + ((String) names.get(i))+" Ordering",
700 (AlignmentOrder) alorders.get(i));
705 Desktop.addInternalFrame(af, alTitle,
706 AlignFrame.NEW_WINDOW_WIDTH,
707 AlignFrame.NEW_WINDOW_HEIGHT);
710 System.out.println("MERGE WITH OLD FRAME");