2 * Jalview - A Sequence Alignment Editor and Viewer
3 * Copyright (C) 2006 AM Waterhouse, J Procter, G Barton, M Clamp, S Searle
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License
7 * as published by the Free Software Foundation; either version 2
8 * of the License, or (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
23 import jalview.analysis.*;
25 import jalview.datamodel.*;
26 import jalview.datamodel.Alignment;
27 import jalview.datamodel.Sequence;
29 import vamsas.objects.simple.MsaResult;
41 * Copyright: Copyright (c) 2004
45 * Company: Dundee University
48 * @author not attributable
52 extends WSThread implements WSClientI
54 boolean submitGaps = false; // pass sequences including gaps to alignment
58 boolean preserveOrder = true; // and always store and recover sequence
62 class MsaWSJob extends WSThread.WSJob
64 // hold special input for this
65 vamsas.objects.simple.SequenceSet seqs = new vamsas.objects.simple.
76 public MsaWSJob(int jobNum, SequenceI[] inSeqs)
79 if (!prepareInput(inSeqs, 2))
82 subjobComplete = true;
83 result = new MsaResult();
84 result.setFinished(true);
85 result.setStatus("Job never ran - input returned to user.");
90 Hashtable SeqNames = new Hashtable();
91 Vector emptySeqs = new Vector();
93 * prepare input sequences for MsaWS service
94 * @param seqs jalview sequences to be prepared
95 * @param minlen minimum number of residues required for this MsaWS service
96 * @return true if seqs contains sequences to be submitted to service.
98 private boolean prepareInput(SequenceI[] seqs, int minlen)
103 throw new Error("Implementation error: minlen must be zero or more.");
105 for (int i = 0; i < seqs.length; i++)
107 if (seqs[i].getEnd() - seqs[i].getStart() > minlen - 1)
112 boolean valid = nseqs > 1; // need at least two seqs
113 vamsas.objects.simple.Sequence[] seqarray =
115 ? new vamsas.objects.simple.Sequence[nseqs]
117 for (int i = 0, n = 0; i < seqs.length; i++)
120 String newname = jalview.analysis.SeqsetUtils.unique_name(i); // same
124 SeqNames.put(newname, jalview.analysis.SeqsetUtils
125 .SeqCharacterHash(seqs[i]));
126 if (valid && seqs[i].getEnd() - seqs[i].getStart() > minlen - 1)
128 seqarray[n] = new vamsas.objects.simple.Sequence();
129 seqarray[n].setId(newname);
130 seqarray[n++].setSeq( (submitGaps) ? seqs[i].getSequence()
131 : AlignSeq.extractGaps(
132 jalview.util.Comparison.GapChars, seqs[i]
138 if (seqs[i].getEnd() >= seqs[i].getStart())
140 empty = (submitGaps) ? seqs[i].getSequence()
141 : AlignSeq.extractGaps(
142 jalview.util.Comparison.GapChars, seqs[i]
145 emptySeqs.add(new String[]
149 this.seqs = new vamsas.objects.simple.SequenceSet();
150 this.seqs.setSeqs(seqarray);
156 * @return true if getAlignment will return a valid alignment result.
158 public boolean hasResults()
160 if (subjobComplete && result != null && result.isFinished()
161 && ((MsaResult) result).getMsa() != null && ((MsaResult) result).getMsa().getSeqs() != null)
168 public Object[] getAlignment()
171 if (result != null && result.isFinished())
173 SequenceI[] alseqs = null;
174 char alseq_gapchar = '-';
176 if (((MsaResult) result).getMsa() != null)
178 alseqs = getVamsasAlignment(((MsaResult) result).getMsa());
179 alseq_gapchar = ((MsaResult) result).getMsa().getGapchar().charAt(0);
180 alseq_l = alseqs.length;
182 if (emptySeqs.size() > 0)
184 SequenceI[] t_alseqs = new SequenceI[alseq_l + emptySeqs.size()];
189 for (i = 0, w = alseqs[0].getLength(); i < alseq_l; i++)
191 if (w < alseqs[i].getLength())
193 w = alseqs[i].getLength();
195 t_alseqs[i] = alseqs[i];
199 // check that aligned width is at least as wide as emptySeqs width.
201 for (i = 0, w = emptySeqs.size(); i < w; i++)
203 String[] es = (String[]) emptySeqs.get(i);
204 if (es != null && es[1] != null)
206 int sw = es[1].length();
213 // make a gapped string.
214 StringBuffer insbuff = new StringBuffer(w);
215 for (i = 0; i < nw; i++)
217 insbuff.append(alseq_gapchar);
221 for (i = 0; i < alseq_l; i++)
223 int sw = t_alseqs[i].getLength();
227 alseqs[i].setSequence(t_alseqs[i].getSequence() +
228 insbuff.substring(0, sw - nw));
232 for (i = 0, w = emptySeqs.size(); i < w; i++)
234 String[] es = (String[]) emptySeqs.get(i);
238 alseq_l] = new jalview.datamodel.Sequence(es[0],
239 insbuff.toString(), 1, 0);
243 if (es[1].length() < nw)
246 alseq_l] = new jalview.datamodel.Sequence(es[0],
247 es[1] + insbuff.substring(0, nw - es[1].length()), 1,
253 alseq_l] = new jalview.datamodel.Sequence(es[0], es[1]);
259 AlignmentOrder msaorder = new AlignmentOrder(alseqs);
260 // always recover the order - makes parseResult()'s life easier.
261 jalview.analysis.AlignmentSorter.recoverOrder(alseqs);
262 // account for any missing sequences
263 jalview.analysis.SeqsetUtils.deuniquify(SeqNames, alseqs);
271 * mark subjob as cancelled and set result object appropriatly
275 subjobComplete = true;
280 * @return boolean true if job can be submitted.
282 boolean hasValidInput() {
283 if (seqs.getSeqs()!=null)
290 String alTitle; // name which will be used to form new alignment window.
291 Alignment dataset; // dataset to which the new alignment will be
295 ext.vamsas.MuscleWS server = null;
297 * set basic options for this (group) of Msa jobs
304 MsaWSThread(ext.vamsas.MuscleWS server, String wsUrl,
305 WebserviceInfo wsinfo, jalview.gui.AlignFrame alFrame,
306 AlignmentView alview,
307 String wsname, boolean subgaps, boolean presorder)
309 this.server = server;
311 this.wsInfo = wsinfo;
312 this.WebServiceName = wsname;
314 this.submitGaps = subgaps;
315 this.preserveOrder = presorder;
316 this.alignFrame = alFrame;
320 * create one or more Msa jobs to align visible seuqences in _msa
333 MsaWSThread(ext.vamsas.MuscleWS server, String wsUrl,
334 WebserviceInfo wsinfo, jalview.gui.AlignFrame alFrame,
335 String wsname, String title, AlignmentView _msa, boolean subgaps,
336 boolean presorder, Alignment seqset)
338 this(server, wsUrl, wsinfo, alFrame, _msa, wsname, subgaps, presorder);
339 OutputHeader = wsInfo.getProgressText();
342 SequenceI[][] conmsa = _msa.getVisibleContigs('-');
345 int njobs = conmsa.length;
346 jobs = new MsaWSJob[njobs];
347 for (int j = 0; j < njobs; j++)
351 jobs[j] = new MsaWSJob(wsinfo.addJobPane(), conmsa[j]);
355 jobs[j] = new MsaWSJob(0, conmsa[j]);
358 wsinfo.setProgressName("region " + jobs[j].jobnum, jobs[j].jobnum);
359 wsinfo.setProgressText(jobs[j].jobnum, OutputHeader);
363 public boolean isCancellable()
368 public void cancelJob()
370 if (!jobComplete && jobs != null)
372 boolean cancelled = true;
373 for (int job = 0; job < jobs.length; job++)
375 if (jobs[job].submitted && !jobs[job].subjobComplete)
377 String cancelledMessage = "";
380 vamsas.objects.simple.WsJobId cancelledJob = server
381 .cancel(jobs[job].jobId);
382 if (cancelledJob.getStatus() == 2)
385 cancelledMessage = "Job cancelled.";
386 ((MsaWSJob) jobs[job]).cancel();
387 wsInfo.setStatus(jobs[job].jobnum,
388 WebserviceInfo.STATE_CANCELLED_OK);
390 else if (cancelledJob.getStatus() == 3)
392 // VALID UNSTOPPABLE JOB
394 "Server cannot cancel this job. just close the window.\n";
396 // wsInfo.setStatus(jobs[job].jobnum,
397 // WebserviceInfo.STATE_RUNNING);
400 if (cancelledJob.getJobId() != null)
402 cancelledMessage += ("[" + cancelledJob.getJobId() + "]");
405 cancelledMessage += "\n";
407 catch (Exception exc)
410 ("\nProblems cancelling the job : Exception received...\n"
412 Cache.log.warn("Exception whilst cancelling "+jobs[job].jobId,exc);
414 wsInfo.setProgressText(jobs[job].jobnum, OutputHeader
415 + cancelledMessage + "\n");
420 wsInfo.setStatus(WebserviceInfo.STATE_CANCELLED_OK);
423 this.interrupt(); // kick thread to update job states.
430 .setProgressText(OutputHeader
431 + "Server cannot cancel this job because it has not been submitted properly. just close the window.\n");
435 void pollJob(WSJob job) throws Exception {
436 ((MsaWSJob) job).result = server.getResult(((MsaWSJob) job).jobId);
438 void StartJob(WSJob job)
440 if (!(job instanceof MsaWSJob)) {
441 throw new Error("StartJob(MsaWSJob) called on a WSJobInstance "+job.getClass());
443 MsaWSJob j = (MsaWSJob) job;
446 if (Cache.log.isDebugEnabled())
448 Cache.log.debug("Tried to submit an already submitted job " + j.jobId);
452 if (j.seqs.getSeqs() == null)
454 // special case - selection consisted entirely of empty sequences...
456 j.result = new MsaResult();
457 j.result.setFinished(true);
458 j.result.setStatus("Empty Alignment Job");
459 ((MsaResult) j.result).setMsa(null);
463 vamsas.objects.simple.WsJobId jobsubmit = server.align(j.seqs);
465 if ( (jobsubmit != null) && (jobsubmit.getStatus() == 1))
467 j.jobId = jobsubmit.getJobId();
469 j.subjobComplete = false;
470 // System.out.println(WsURL + " Job Id '" + jobId + "'");
474 if (jobsubmit == null)
480 " returned null object, it probably cannot be contacted. Try again later ?");
483 throw new Exception(jobsubmit.getJobId());
488 // TODO: JBPNote catch timeout or other fault types explicitly
489 // For unexpected errors
491 .println(WebServiceName
492 + "Client: Failed to submit the sequences for alignment (probably a server side problem)\n"
493 + "When contacting Server:" + WsUrl + "\n"
494 + e.toString() + "\n");
495 j.allowedServerExceptions = 0;
496 wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_SERVERERROR);
497 wsInfo.setStatus(j.jobnum, WebserviceInfo.STATE_STOPPED_SERVERERROR);
501 "Failed to submit sequences for alignment.\n"
502 + "It is most likely that there is a problem with the server.\n"
503 + "Just close the window\n");
505 // e.printStackTrace(); // TODO: JBPNote DEBUG
509 private jalview.datamodel.Sequence[] getVamsasAlignment(
510 vamsas.objects.simple.Alignment valign)
512 vamsas.objects.simple.Sequence[] seqs = valign.getSeqs().getSeqs();
513 jalview.datamodel.Sequence[] msa = new jalview.datamodel.Sequence[seqs.
516 for (int i = 0, j = seqs.length; i < j; i++)
518 msa[i] = new jalview.datamodel.Sequence(seqs[i].getId(), seqs[i]
527 int results = 0; // number of result sets received
528 JobStateSummary finalState = new JobStateSummary();
531 for (int j = 0; j < jobs.length; j++)
533 finalState.updateJobPanelState(wsInfo, OutputHeader, jobs[j]);
534 if (jobs[j].submitted && jobs[j].subjobComplete && jobs[j].hasResults())
537 vamsas.objects.simple.Alignment valign = ((MsaResult) jobs[j].result).getMsa();
540 wsInfo.appendProgressText(jobs[j].jobnum,
541 "\nAlignment Object Method Notes\n");
542 String[] lines = valign.getMethod();
543 for (int line = 0; line < lines.length; line++)
545 wsInfo.appendProgressText(jobs[j].jobnum, lines[line] + "\n");
547 // JBPNote The returned files from a webservice could be
548 // hidden behind icons in the monitor window that,
549 // when clicked, pop up their corresponding data
557 Cache.log.error("Unexpected exception when processing results for " +
559 wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_ERROR);
563 wsInfo.showResultsNewFrame
564 .addActionListener(new java.awt.event.ActionListener()
566 public void actionPerformed(
567 java.awt.event.ActionEvent evt)
569 displayResults(true);
573 .addActionListener(new java.awt.event.ActionListener()
575 public void actionPerformed(
576 java.awt.event.ActionEvent evt)
578 displayResults(false);
581 wsInfo.setResultsReady();
583 wsInfo.setFinishedNoResults();
586 void displayResults(boolean newFrame)
588 // view input or result data for each block
589 Vector alorders = new Vector();
590 SequenceI[][] results=new SequenceI[jobs.length][];
591 AlignmentOrder[] orders = new AlignmentOrder[jobs.length];
592 SequenceI[] first=null;
593 for (int j=0; j<jobs.length; j++) {
594 if (jobs[j].hasResults()) {
595 Object[] res = ( (MsaWSJob) jobs[j]).getAlignment();
596 alorders.add(res[1]);
597 results[j] = (SequenceI[]) res[0];
598 orders[j] = (AlignmentOrder) res[1];
599 // SequenceI[] alignment = input.getUpdated
604 Object[] newview = input.getUpdatedView(results, orders, '-');
605 // trash references to original result data
606 for (int j=0; j<jobs.length; j++) {
610 SequenceI[] alignment = (SequenceI[]) newview[0];
611 ColumnSelection columnselection = (ColumnSelection) newview[1];
612 Alignment al = new Alignment(alignment);
615 al.setDataset(dataset);
618 // JBNote- TODO: warn user if a block is input rather than aligned data ?
622 AlignFrame af = new AlignFrame(al, columnselection);
624 // >>>This is a fix for the moment, until a better solution is
626 af.getFeatureRenderer().transferSettings(
627 alignFrame.getFeatureRenderer());
629 if (alorders.size() > 0)
631 if (alorders.size() == 1)
633 af.addSortByOrderMenuItem(WebServiceName + " Ordering",
634 (AlignmentOrder) alorders.get(0));
638 // construct a non-redundant ordering set
639 Vector names = new Vector();
640 for (int i = 0, l = alorders.size(); i < l; i++)
642 String orderName = new String(" Region " + i);
647 if ( ( (AlignmentOrder) alorders.get(i)).equals( ( (
648 AlignmentOrder) alorders.get(j))))
652 orderName += "," + j;
660 if (i == 0 && j == 1)
662 names.add(new String(""));
666 names.add(orderName);
669 for (int i = 0, l = alorders.size(); i < l; i++)
671 af.addSortByOrderMenuItem(WebServiceName
672 +( (String) names.get(i)) +
674 (AlignmentOrder) alorders.get(i));
679 Desktop.addInternalFrame(af, alTitle,
680 AlignFrame.NEW_WINDOW_WIDTH,
681 AlignFrame.NEW_WINDOW_HEIGHT);
686 System.out.println("MERGE WITH OLD FRAME");
691 public boolean canMergeResults()