7 import jalview.analysis.*;
9 import jalview.datamodel.*;
10 import jalview.datamodel.Alignment;
12 import vamsas.objects.simple.MsaResult;
24 * Copyright: Copyright (c) 2004
28 * Company: Dundee University
31 * @author not attributable
35 extends Thread implements WSClientI
37 jalview.gui.AlignFrame alignFrame;
39 WebserviceInfo wsInfo = null;
41 String WebServiceName = null;
45 boolean submitGaps = false; // pass sequences including gaps to alignment
49 boolean preserveOrder = true; // and always store and recover sequence
55 int jobnum = 0; // WebServiceInfo pane for this job
57 String jobId; // ws job ticket
59 vamsas.objects.simple.MsaResult result = null;
61 vamsas.objects.simple.SequenceSet seqs = new vamsas.objects.simple.
72 public MsaWSJob(int jobNum, SequenceI[] inSeqs)
75 if (!prepareInput(inSeqs, 2))
78 subjobComplete = true;
79 result = new MsaResult();
80 result.setFinished(true);
81 result.setStatus("Job never ran - input returned to user.");
86 int allowedServerExceptions = 3; // thread dies if too many
89 boolean submitted = false;
90 boolean subjobComplete = false;
92 Hashtable SeqNames = new Hashtable();
93 Vector emptySeqs = new Vector();
95 * prepare input sequences for MsaWS service
96 * @param seqs jalview sequences to be prepared
97 * @param minlen minimum number of residues required for this MsaWS service
98 * @return true if seqs contains sequences to be submitted to service.
100 private boolean prepareInput(SequenceI[] seqs, int minlen)
105 throw new Error("Implementation error: minlen must be zero or more.");
107 for (int i = 0; i < seqs.length; i++)
109 if (seqs[i].getEnd() - seqs[i].getStart() > minlen - 1)
114 boolean valid = nseqs > 1; // need at least two seqs
115 vamsas.objects.simple.Sequence[] seqarray =
117 ? new vamsas.objects.simple.Sequence[nseqs]
119 for (int i = 0, n = 0; i < seqs.length; i++)
122 String newname = jalview.analysis.SeqsetUtils.unique_name(i); // same
126 SeqNames.put(newname, jalview.analysis.SeqsetUtils
127 .SeqCharacterHash(seqs[i]));
128 if (valid && seqs[i].getEnd() - seqs[i].getStart() > minlen - 1)
130 seqarray[n] = new vamsas.objects.simple.Sequence();
131 seqarray[n].setId(newname);
132 seqarray[n++].setSeq( (submitGaps) ? seqs[i].getSequence()
133 : AlignSeq.extractGaps(
134 jalview.util.Comparison.GapChars, seqs[i]
140 if (seqs[i].getEnd() >= seqs[i].getStart())
142 empty = (submitGaps) ? seqs[i].getSequence()
143 : AlignSeq.extractGaps(
144 jalview.util.Comparison.GapChars, seqs[i]
147 emptySeqs.add(new String[]
151 this.seqs = new vamsas.objects.simple.SequenceSet();
152 this.seqs.setSeqs(seqarray);
158 * @return true if getAlignment will return a valid alignment result.
160 public boolean hasResults()
162 if (subjobComplete && result != null && result.isFinished()
163 && result.getMsa() != null && result.getMsa().getSeqs() != null)
170 public Object[] getAlignment()
173 if (result != null && result.isFinished())
175 SequenceI[] alseqs = null;
176 char alseq_gapchar = '-';
178 if (result.getMsa() != null)
180 alseqs = getVamsasAlignment(result.getMsa());
181 alseq_gapchar = result.getMsa().getGapchar().charAt(0);
182 alseq_l = alseqs.length;
184 if (emptySeqs.size() > 0)
186 SequenceI[] t_alseqs = new SequenceI[alseq_l + emptySeqs.size()];
191 for (i = 0, w = alseqs[0].getLength(); i < alseq_l; i++)
193 if (w < alseqs[i].getLength())
195 w = alseqs[i].getLength();
197 t_alseqs[i] = alseqs[i];
201 // check that aligned width is at least as wide as emptySeqs width.
203 for (i = 0, w = emptySeqs.size(); i < w; i++)
205 String[] es = (String[]) emptySeqs.get(i);
206 if (es != null && es[1] != null)
208 int sw = es[1].length();
215 // make a gapped string.
216 StringBuffer insbuff = new StringBuffer(w);
217 for (i = 0; i < nw; i++)
219 insbuff.append(alseq_gapchar);
223 for (i = 0; i < alseq_l; i++)
225 int sw = t_alseqs[i].getLength();
229 alseqs[i].setSequence(t_alseqs[i].getSequence() +
230 insbuff.substring(0, sw - nw));
234 for (i = 0, w = emptySeqs.size(); i < w; i++)
236 String[] es = (String[]) emptySeqs.get(i);
240 alseq_l] = new jalview.datamodel.Sequence(es[0],
241 insbuff.toString(), 1, 0);
245 if (es[1].length() < nw)
248 alseq_l] = new jalview.datamodel.Sequence(es[0],
249 es[1] + insbuff.substring(0, nw - es[1].length()), 1,
255 alseq_l] = new jalview.datamodel.Sequence(es[0], es[1]);
261 AlignmentOrder msaorder = new AlignmentOrder(alseqs);
262 // always recover the order - makes parseResult()'s life easier.
263 jalview.analysis.AlignmentSorter.recoverOrder(alseqs);
264 // account for any missing sequences
265 jalview.analysis.SeqsetUtils.deuniquify(SeqNames, alseqs);
273 boolean cancelled = false;
275 * mark subjob as cancelled and set result object appropriatly
279 subjobComplete = true;
284 MsaWSJob jobs[] = null;
286 String alTitle; // name which will be used to form new alignment window.
288 boolean jobComplete = false;
290 Alignment dataset; // dataset to which the new alignment will be
294 ext.vamsas.MuscleWS server = null;
299 * set basic options for this (group) of Msa jobs
306 MsaWSThread(ext.vamsas.MuscleWS server, String wsUrl,
307 WebserviceInfo wsinfo, jalview.gui.AlignFrame alFrame,
308 AlignmentView alview,
309 String wsname, boolean subgaps, boolean presorder)
311 this.server = server;
313 this.wsInfo = wsinfo;
314 this.WebServiceName = wsname;
316 this.submitGaps = subgaps;
317 this.preserveOrder = presorder;
318 this.alignFrame = alFrame;
322 * create one or more Msa jobs to align visible seuqences in _msa
335 MsaWSThread(ext.vamsas.MuscleWS server, String wsUrl,
336 WebserviceInfo wsinfo, jalview.gui.AlignFrame alFrame,
337 String wsname, String title, AlignmentView _msa, boolean subgaps,
338 boolean presorder, Alignment seqset)
340 this(server, wsUrl, wsinfo, alFrame, _msa, wsname, subgaps, presorder);
341 OutputHeader = wsInfo.getProgressText();
344 SeqCigar[] msa = _msa.getSequences();
345 int[] contigs = _msa.getContigs();
347 if (contigs != null && contigs.length > 0)
351 int width = _msa.getWidth();
352 for (int contig = 0; contig < contigs.length; contig += 3)
354 if ( (contigs[contig + 1] - start) > 0)
358 width += contigs[contig + 2]; // end up with full region width (including hidden regions)
359 start = contigs[contig + 1] + contigs[contig + 2];
365 jobs = new MsaWSJob[njobs];
368 for (int contig = 0; contig < contigs.length; contig += 3)
370 if (contigs[contig + 1] - start > 0)
372 SequenceI mseq[] = new SequenceI[msa.length];
373 for (int s = 0; s < mseq.length; s++)
375 mseq[s] = msa[s].getSeq('-').getSubSequence(start,
376 contigs[contig + 1]);
380 jobs[j] = new MsaWSJob(wsinfo.addJobPane(), mseq);
384 jobs[j] = new MsaWSJob(0, mseq);
386 wsinfo.setProgressName("region " + jobs[j].jobnum, jobs[j].jobnum);
387 wsinfo.setProgressText(jobs[j].jobnum, OutputHeader);
390 start = contigs[contig + 1] + contigs[contig + 2];
394 SequenceI mseq[] = new SequenceI[msa.length];
395 for (int s = 0; s < mseq.length; s++)
397 mseq[s] = msa[s].getSeq('-').getSubSequence(start,
402 jobs[j] = new MsaWSJob(wsinfo.addJobPane(), mseq);
406 jobs[j] = new MsaWSJob(0, mseq);
408 wsinfo.setProgressName("region " + jobs[j].jobnum, jobs[j].jobnum);
409 wsinfo.setProgressText(jobs[j].jobnum, OutputHeader);
415 SequenceI mseq[] = new SequenceI[msa.length];
416 for (int s = 0; s < mseq.length; s++)
418 mseq[s] = msa[s].getSeq('-');
420 jobs = new MsaWSJob[1];
421 wsinfo.setProgressText(OutputHeader); // ensures default text
422 jobs[0] = new MsaWSJob(0, mseq);
426 public boolean isCancellable()
431 public void cancelJob()
433 if (!jobComplete && jobs != null)
435 boolean cancelled = true;
436 for (int job = 0; job < jobs.length; job++)
438 if (jobs[job].submitted && !jobs[job].subjobComplete)
440 String cancelledMessage = "";
443 vamsas.objects.simple.WsJobId cancelledJob = server
444 .cancel(jobs[job].jobId);
445 if (cancelledJob.getStatus() == 2)
448 cancelledMessage = "Job cancelled.";
450 wsInfo.setStatus(jobs[job].jobnum,
451 WebserviceInfo.STATE_CANCELLED_OK);
453 else if (cancelledJob.getStatus() == 3)
455 // VALID UNSTOPPABLE JOB
457 "Server cannot cancel this job. just close the window.\n";
459 // wsInfo.setStatus(jobs[job].jobnum,
460 // WebserviceInfo.STATE_RUNNING);
463 if (cancelledJob.getJobId() != null)
465 cancelledMessage += ("[" + cancelledJob.getJobId() + "]");
468 cancelledMessage += "\n";
470 catch (Exception exc)
473 ("\nProblems cancelling the job : Exception received...\n"
475 Cache.log.warn("Exception whilst cancelling "+jobs[job].jobId,exc);
477 wsInfo.setProgressText(jobs[job].jobnum, OutputHeader
478 + cancelledMessage + "\n");
483 wsInfo.setStatus(WebserviceInfo.STATE_CANCELLED_OK);
486 this.interrupt(); // kick thread to update job states.
493 .setProgressText(OutputHeader
494 + "Server cannot cancel this job because it has not been submitted properly. just close the window.\n");
499 class JobStateSummary {
507 void updateJobPanelState(WebserviceInfo wsInfo, MsaWSJob j) {
508 if (j.result != null)
510 String progheader = "";
511 // Parse state of job[j]
512 if (j.result.isRunning())
515 wsInfo.setStatus(j.jobnum, WebserviceInfo.STATE_RUNNING);
517 else if (j.result.isQueued())
520 wsInfo.setStatus(j.jobnum, WebserviceInfo.STATE_QUEUING);
522 else if (j.result.isFinished())
525 j.subjobComplete = true;
528 wsInfo.setStatus(j.jobnum, WebserviceInfo.STATE_STOPPED_OK);
530 else if (j.result.isFailed())
532 progheader += "Job failed.\n";
533 j.subjobComplete = true;
534 wsInfo.setStatus(j.jobnum, WebserviceInfo.STATE_STOPPED_ERROR);
537 else if (j.result.isServerError())
540 j.subjobComplete = true;
541 wsInfo.setStatus(j.jobnum,
542 WebserviceInfo.STATE_STOPPED_SERVERERROR);
544 else if (j.result.isBroken() || j.result.isFailed())
547 j.subjobComplete = true;
548 wsInfo.setStatus(j.jobnum, WebserviceInfo.STATE_STOPPED_ERROR);
550 // and pass on any sub-job messages to the user
551 wsInfo.setProgressText(j.jobnum, OutputHeader);
552 wsInfo.appendProgressText(j.jobnum, progheader);
553 if (j.result.getStatus() != null)
555 wsInfo.appendProgressText(j.jobnum, j.result.getStatus());
560 if (j.submitted && j.subjobComplete)
562 if (j.allowedServerExceptions == 0)
566 else if (j.result == null)
576 JobStateSummary jstate=null;
579 jstate = new JobStateSummary();
580 for (int j = 0; j < jobs.length; j++)
583 if (!jobs[j].submitted && jobs[j].seqs.getSeqs() != null)
588 if (jobs[j].submitted && !jobs[j].subjobComplete)
592 if ( (jobs[j].result = server.getResult(jobs[j].jobId)) == null)
594 throw (new Exception(
595 "Timed out when communicating with server\nTry again later.\n"));
597 jalview.bin.Cache.log.debug("Job " + j + " Result state " +
598 jobs[j].result.getState()
600 jobs[j].result.isServerError() + ")");
604 // Deal with Transaction exceptions
605 wsInfo.appendProgressText(jobs[j].jobnum, "\n" + WebServiceName
606 + " Server exception!\n" + ex.getMessage());
607 Cache.log.warn(WebServiceName + " job(" + jobs[j].jobnum
608 + ") Server exception: " + ex.getMessage());
610 if (jobs[j].allowedServerExceptions > 0)
612 jobs[j].allowedServerExceptions--;
613 Cache.log.debug("Sleeping after a server exception.");
618 catch (InterruptedException ex1)
624 Cache.log.warn("Dropping job " + j + " " + jobs[j].jobId);
625 jobs[j].subjobComplete = true;
626 wsInfo.setStatus(jobs[j].jobnum,
627 WebserviceInfo.STATE_STOPPED_SERVERERROR);
630 catch (OutOfMemoryError er)
633 jobs[j].subjobComplete = true;
634 jobs[j].result = null; // may contain out of date result object
635 wsInfo.setStatus(jobs[j].jobnum,
636 WebserviceInfo.STATE_STOPPED_ERROR);
638 .showInternalMessageDialog(
640 "Out of memory handling result for job !!"
642 "\nSee help files for increasing Java Virtual Machine memory.",
643 "Out of memory", JOptionPane.WARNING_MESSAGE);
644 Cache.log.error("Out of memory when retrieving Job " + j + " id:" +
645 WsUrl + "/" + jobs[j].jobId, er);
649 jstate.updateJobPanelState(wsInfo, jobs[j]);
651 // Decide on overall state based on collected jobs[] states
652 if (jstate.running > 0)
654 wsInfo.setStatus(WebserviceInfo.STATE_RUNNING);
656 else if (jstate.queuing > 0)
658 wsInfo.setStatus(WebserviceInfo.STATE_QUEUING);
663 if (jstate.finished > 0)
665 wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_OK);
667 else if (jstate.error > 0)
669 wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_ERROR);
671 else if (jstate.serror > 0)
673 wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_SERVERERROR);
682 catch (InterruptedException e)
684 Cache.log.debug("Interrupted sleep waiting for next job poll.", e);
686 // System.out.println("I'm alive "+alTitle);
691 parseResult(); // tidy up and make results available to user
695 void StartJob(MsaWSJob j)
699 if (Cache.log.isDebugEnabled())
701 Cache.log.debug("Tried to submit an already submitted job " + j.jobId);
705 if (j.seqs.getSeqs() == null)
707 // special case - selection consisted entirely of empty sequences...
709 j.result = new MsaResult();
710 j.result.setFinished(true);
711 j.result.setStatus("Empty Alignment Job");
712 j.result.setMsa(null);
716 vamsas.objects.simple.WsJobId jobsubmit = server.align(j.seqs);
718 if ( (jobsubmit != null) && (jobsubmit.getStatus() == 1))
720 j.jobId = jobsubmit.getJobId();
722 j.subjobComplete = false;
723 // System.out.println(WsURL + " Job Id '" + jobId + "'");
727 if (jobsubmit == null)
733 " returned null object, it probably cannot be contacted. Try again later ?");
736 throw new Exception(jobsubmit.getJobId());
741 // TODO: JBPNote catch timeout or other fault types explicitly
742 // For unexpected errors
744 .println(WebServiceName
745 + "Client: Failed to submit the sequences for alignment (probably a server side problem)\n"
746 + "When contacting Server:" + WsUrl + "\n"
747 + e.toString() + "\n");
748 j.allowedServerExceptions = 0;
749 wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_SERVERERROR);
750 wsInfo.setStatus(j.jobnum, WebserviceInfo.STATE_STOPPED_SERVERERROR);
754 "Failed to submit sequences for alignment.\n"
755 + "It is most likely that there is a problem with the server.\n"
756 + "Just close the window\n");
758 // e.printStackTrace(); // TODO: JBPNote DEBUG
762 private jalview.datamodel.Sequence[] getVamsasAlignment(
763 vamsas.objects.simple.Alignment valign)
765 vamsas.objects.simple.Sequence[] seqs = valign.getSeqs().getSeqs();
766 jalview.datamodel.Sequence[] msa = new jalview.datamodel.Sequence[seqs.
769 for (int i = 0, j = seqs.length; i < j; i++)
771 msa[i] = new jalview.datamodel.Sequence(seqs[i].getId(), seqs[i]
780 int results = 0; // number of result sets received
781 JobStateSummary finalState = new JobStateSummary();
784 for (int j = 0; j < jobs.length; j++)
786 finalState.updateJobPanelState(wsInfo, jobs[j]);
787 if (jobs[j].submitted && jobs[j].subjobComplete && jobs[j].hasResults())
790 vamsas.objects.simple.Alignment valign = jobs[j].result.getMsa();
793 wsInfo.appendProgressText(jobs[j].jobnum,
794 "\nAlignment Object Method Notes\n");
795 String[] lines = valign.getMethod();
796 for (int line = 0; line < lines.length; line++)
798 wsInfo.appendProgressText(jobs[j].jobnum, lines[line] + "\n");
800 // JBPNote The returned files from a webservice could be
801 // hidden behind icons in the monitor window that,
802 // when clicked, pop up their corresponding data
810 Cache.log.error("Unexpected exception when processing results for " +
812 wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_ERROR);
816 wsInfo.showResultsNewFrame
817 .addActionListener(new java.awt.event.ActionListener()
819 public void actionPerformed(
820 java.awt.event.ActionEvent evt)
822 displayResults(true);
826 .addActionListener(new java.awt.event.ActionListener()
828 public void actionPerformed(
829 java.awt.event.ActionEvent evt)
831 displayResults(false);
834 wsInfo.setResultsReady();
838 void displayResults(boolean newFrame)
840 // view input or result data for each block
841 // warn user if a block is input rather than aligned data ?
843 int contigs[] = input.getContigs();
844 SeqCigar[] seqs = input.getSequences();
845 SequenceI[] alignment = new SequenceI[seqs.length];
846 ColumnSelection columnselection = new ColumnSelection();
847 Vector alorders = new Vector();
848 if (contigs != null && contigs.length > 0)
852 int owidth = input.getWidth();
854 for (int contig = 0; contig < contigs.length; contig += 3)
856 owidth += contigs[contig + 2]; // recover final column width
857 if (contigs[contig + 1] - start > 0)
859 int width = 0; // subalignment width
860 if (jobs[j].hasResults())
862 Object[] subalg = jobs[j++].getAlignment();
863 alorders.add(subalg[1]);
864 SequenceI mseq[] = (SequenceI[]) subalg[0];
865 width = mseq[0].getLength();
866 for (int s = 0; s < mseq.length; s++)
868 if (alignment[s] == null)
870 alignment[s] = mseq[s];
874 alignment[s].setSequence(alignment[s].getSequence() +
875 mseq[s].getSequence());
876 if (mseq[s].getStart() <= mseq[s].getEnd())
878 alignment[s].setEnd(mseq[s].getEnd());
880 ( (AlignmentOrder) subalg[1]).updateSequence(mseq[s],
887 // recover input data or place gaps
890 // recover input data
891 for (int s = 0; s < seqs.length; s++)
893 SequenceI oseq = seqs[s].getSeq('-').getSubSequence(start,
894 contigs[contig + 1]);
895 if (width < oseq.getLength())
897 width = oseq.getLength();
899 if (alignment[s] == null)
905 alignment[s].setSequence(alignment[s].getSequence() +
907 if (oseq.getEnd() >= oseq.getStart())
909 alignment[s].setEnd(oseq.getEnd());
919 // advance to begining of visible region
920 start = contigs[contig + 1] + contigs[contig + 2];
921 // add hidden segment to right of next region
922 for (int s = 0; s < seqs.length; s++)
924 SequenceI hseq = seqs[s].getSeq('-').getSubSequence(contigs[contig +
926 if (alignment[s] == null)
932 alignment[s].setSequence(alignment[s].getSequence() +
934 if (hseq.getEnd() >= hseq.getStart())
936 alignment[s].setEnd(hseq.getEnd());
940 // mark hidden segment as hidden in the new alignment
941 columnselection.hideColumns(nwidth, nwidth + contigs[contig + 2] - 1);
942 nwidth += contigs[contig + 2];
944 // Do final job - if it exists
948 if (jobs[j].hasResults())
950 Object[] subalg = jobs[j].getAlignment();
951 alorders.add(subalg[1]);
952 SequenceI mseq[] = (SequenceI[]) subalg[0];
953 width = mseq[0].getLength();
954 for (int s = 0; s < mseq.length; s++)
956 if (alignment[s] == null)
958 alignment[s] = mseq[s];
962 alignment[s].setSequence(alignment[s].getSequence() +
963 mseq[s].getSequence());
964 if (mseq[s].getEnd() >= mseq[s].getStart())
966 alignment[s].setEnd(mseq[s].getEnd());
968 ( (AlignmentOrder) subalg[1]).updateSequence(mseq[s], alignment[s]);
976 // recover input data or place gaps
979 // recover input data
980 for (int s = 0; s < seqs.length; s++)
982 SequenceI oseq = seqs[s].getSeq('-').getSubSequence(start,
984 if (width < oseq.getLength())
986 width = oseq.getLength();
988 if (alignment[s] == null)
994 alignment[s].setSequence(alignment[s].getSequence() +
996 if (oseq.getEnd() >= oseq.getStart())
998 alignment[s].setEnd(oseq.getEnd());
1007 throw new Error("Padding not yet implemented.");
1015 if (jobs[0].hasResults())
1017 Object[] alg = jobs[0].getAlignment();
1018 alignment = (SequenceI[]) alg[0];
1019 alorders.add(alg[1]);
1023 alignment = SeqCigar.createAlignmentSequences(seqs, '-',
1024 columnselection, null);
1027 Alignment al = new Alignment(alignment);
1028 if (dataset != null)
1030 al.setDataset(dataset);
1035 // TODO: JBPNote Should also rename the query sequence
1037 AlignFrame af = new AlignFrame(al, columnselection);
1039 // >>>This is a fix for the moment, until a better solution is
1041 af.getFeatureRenderer().transferSettings(
1042 alignFrame.getFeatureRenderer());
1043 if (alorders.size() > 0)
1045 if (alorders.size() == 1)
1047 af.addSortByOrderMenuItem(WebServiceName + " Ordering",
1048 (AlignmentOrder) alorders.get(0));
1052 // construct a non-redundant ordering set
1053 Vector names = new Vector();
1054 for (int i = 0, l = alorders.size(); i < l; i++)
1056 String orderName = new String("Region " + i);
1061 if ( ( (AlignmentOrder) alorders.get(i)).equals( ( (
1062 AlignmentOrder) alorders.get(j))))
1066 orderName += "," + j;
1074 if (i == 0 && j == 1)
1076 names.add(new String(""));
1080 names.add(orderName);
1083 for (int i = 0, l = alorders.size(); i < l; i++)
1085 af.addSortByOrderMenuItem(WebServiceName + ( (String) names.get(i)) +
1087 (AlignmentOrder) alorders.get(i));
1092 Desktop.addInternalFrame(af, alTitle,
1093 AlignFrame.NEW_WINDOW_WIDTH,
1094 AlignFrame.NEW_WINDOW_HEIGHT);
1099 System.out.println("MERGE WITH OLD FRAME");