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();
343 SequenceI[][] conmsa = _msa.getVisibleContigs('-');
346 int njobs = conmsa.length;
347 jobs = new MsaWSJob[njobs];
348 for (int j = 0; j < njobs; j++)
352 jobs[j] = new MsaWSJob(wsinfo.addJobPane(), conmsa[j]);
356 jobs[j] = new MsaWSJob(0, conmsa[j]);
359 wsinfo.setProgressName("region " + jobs[j].jobnum, jobs[j].jobnum);
360 wsinfo.setProgressText(jobs[j].jobnum, OutputHeader);
364 public boolean isCancellable()
369 public void cancelJob()
371 if (!jobComplete && jobs != null)
373 boolean cancelled = true;
374 for (int job = 0; job < jobs.length; job++)
376 if (jobs[job].submitted && !jobs[job].subjobComplete)
378 String cancelledMessage = "";
381 vamsas.objects.simple.WsJobId cancelledJob = server
382 .cancel(jobs[job].jobId);
383 if (cancelledJob.getStatus() == 2)
386 cancelledMessage = "Job cancelled.";
387 ((MsaWSJob) jobs[job]).cancel();
388 wsInfo.setStatus(jobs[job].jobnum,
389 WebserviceInfo.STATE_CANCELLED_OK);
391 else if (cancelledJob.getStatus() == 3)
393 // VALID UNSTOPPABLE JOB
395 "Server cannot cancel this job. just close the window.\n";
397 // wsInfo.setStatus(jobs[job].jobnum,
398 // WebserviceInfo.STATE_RUNNING);
401 if (cancelledJob.getJobId() != null)
403 cancelledMessage += ("[" + cancelledJob.getJobId() + "]");
406 cancelledMessage += "\n";
408 catch (Exception exc)
411 ("\nProblems cancelling the job : Exception received...\n"
413 Cache.log.warn("Exception whilst cancelling "+jobs[job].jobId,exc);
415 wsInfo.setProgressText(jobs[job].jobnum, OutputHeader
416 + cancelledMessage + "\n");
421 wsInfo.setStatus(WebserviceInfo.STATE_CANCELLED_OK);
424 this.interrupt(); // kick thread to update job states.
431 .setProgressText(OutputHeader
432 + "Server cannot cancel this job because it has not been submitted properly. just close the window.\n");
436 void pollJob(WSJob job) throws Exception {
437 ((MsaWSJob) job).result = server.getResult(((MsaWSJob) job).jobId);
439 void StartJob(WSJob job)
441 if (!(job instanceof MsaWSJob)) {
442 throw new Error("StartJob(MsaWSJob) called on a WSJobInstance "+job.getClass());
444 MsaWSJob j = (MsaWSJob) job;
447 if (Cache.log.isDebugEnabled())
449 Cache.log.debug("Tried to submit an already submitted job " + j.jobId);
453 if (j.seqs.getSeqs() == null)
455 // special case - selection consisted entirely of empty sequences...
457 j.result = new MsaResult();
458 j.result.setFinished(true);
459 j.result.setStatus("Empty Alignment Job");
460 ((MsaResult) j.result).setMsa(null);
464 vamsas.objects.simple.WsJobId jobsubmit = server.align(j.seqs);
466 if ( (jobsubmit != null) && (jobsubmit.getStatus() == 1))
468 j.jobId = jobsubmit.getJobId();
470 j.subjobComplete = false;
471 // System.out.println(WsURL + " Job Id '" + jobId + "'");
475 if (jobsubmit == null)
481 " returned null object, it probably cannot be contacted. Try again later ?");
484 throw new Exception(jobsubmit.getJobId());
489 // TODO: JBPNote catch timeout or other fault types explicitly
490 // For unexpected errors
492 .println(WebServiceName
493 + "Client: Failed to submit the sequences for alignment (probably a server side problem)\n"
494 + "When contacting Server:" + WsUrl + "\n"
495 + e.toString() + "\n");
496 j.allowedServerExceptions = 0;
497 wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_SERVERERROR);
498 wsInfo.setStatus(j.jobnum, WebserviceInfo.STATE_STOPPED_SERVERERROR);
502 "Failed to submit sequences for alignment.\n"
503 + "It is most likely that there is a problem with the server.\n"
504 + "Just close the window\n");
506 // e.printStackTrace(); // TODO: JBPNote DEBUG
510 private jalview.datamodel.Sequence[] getVamsasAlignment(
511 vamsas.objects.simple.Alignment valign)
513 vamsas.objects.simple.Sequence[] seqs = valign.getSeqs().getSeqs();
514 jalview.datamodel.Sequence[] msa = new jalview.datamodel.Sequence[seqs.
517 for (int i = 0, j = seqs.length; i < j; i++)
519 msa[i] = new jalview.datamodel.Sequence(seqs[i].getId(), seqs[i]
528 int results = 0; // number of result sets received
529 JobStateSummary finalState = new JobStateSummary();
532 for (int j = 0; j < jobs.length; j++)
534 finalState.updateJobPanelState(wsInfo, OutputHeader, jobs[j]);
535 if (jobs[j].submitted && jobs[j].subjobComplete && jobs[j].hasResults())
538 vamsas.objects.simple.Alignment valign = ((MsaResult) jobs[j].result).getMsa();
541 wsInfo.appendProgressText(jobs[j].jobnum,
542 "\nAlignment Object Method Notes\n");
543 String[] lines = valign.getMethod();
544 for (int line = 0; line < lines.length; line++)
546 wsInfo.appendProgressText(jobs[j].jobnum, lines[line] + "\n");
548 // JBPNote The returned files from a webservice could be
549 // hidden behind icons in the monitor window that,
550 // when clicked, pop up their corresponding data
558 Cache.log.error("Unexpected exception when processing results for " +
560 wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_ERROR);
564 wsInfo.showResultsNewFrame
565 .addActionListener(new java.awt.event.ActionListener()
567 public void actionPerformed(
568 java.awt.event.ActionEvent evt)
570 displayResults(true);
574 .addActionListener(new java.awt.event.ActionListener()
576 public void actionPerformed(
577 java.awt.event.ActionEvent evt)
579 displayResults(false);
582 wsInfo.setResultsReady();
584 wsInfo.setFinishedNoResults();
587 void displayResults(boolean newFrame)
589 // view input or result data for each block
590 Vector alorders = new Vector();
591 SequenceI[][] results=new SequenceI[jobs.length][];
592 AlignmentOrder[] orders = new AlignmentOrder[jobs.length];
594 for (int j=0; j<jobs.length; j++) {
595 if (jobs[j].hasResults()) {
596 Object[] res = ( (MsaWSJob) jobs[j]).getAlignment();
597 alorders.add(res[1]);
598 results[j] = (SequenceI[]) res[0];
599 orders[j] = (AlignmentOrder) res[1];
600 // SequenceI[] alignment = input.getUpdated
605 Object[] newview = input.getUpdatedView(results, orders, '-');
606 // trash references to original result data
607 for (int j=0; j<jobs.length; j++) {
611 SequenceI[] alignment = (SequenceI[]) newview[0];
612 ColumnSelection columnselection = (ColumnSelection) newview[1];
613 Alignment al = new Alignment(alignment);
616 al.setDataset(dataset);
619 // JBNote- TODO: warn user if a block is input rather than aligned data ?
623 AlignFrame af = new AlignFrame(al, columnselection,
624 AlignFrame.DEFAULT_WIDTH,
625 AlignFrame.DEFAULT_HEIGHT);
627 // >>>This is a fix for the moment, until a better solution is
629 af.getFeatureRenderer().transferSettings(
630 alignFrame.getFeatureRenderer());
632 if (alorders.size() > 0)
634 if (alorders.size() == 1)
636 af.addSortByOrderMenuItem(WebServiceName + " Ordering",
637 (AlignmentOrder) alorders.get(0));
641 // construct a non-redundant ordering set
642 Vector names = new Vector();
643 for (int i = 0, l = alorders.size(); i < l; i++)
645 String orderName = new String(" Region " + i);
650 if ( ( (AlignmentOrder) alorders.get(i)).equals( ( (
651 AlignmentOrder) alorders.get(j))))
655 orderName += "," + j;
663 if (i == 0 && j == 1)
665 names.add(new String(""));
669 names.add(orderName);
672 for (int i = 0, l = alorders.size(); i < l; i++)
674 af.addSortByOrderMenuItem(WebServiceName
675 +( (String) names.get(i)) +
677 (AlignmentOrder) alorders.get(i));
682 Desktop.addInternalFrame(af, alTitle,
683 AlignFrame.DEFAULT_WIDTH,
684 AlignFrame.DEFAULT_HEIGHT);
689 System.out.println("MERGE WITH OLD FRAME");
694 public boolean canMergeResults()