2 * Jalview - A Sequence Alignment Editor and Viewer
3 * Copyright (C) 2007 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;
28 import vamsas.objects.simple.MsaResult;
40 * Copyright: Copyright (c) 2004
44 * Company: Dundee University
47 * @author not attributable
51 extends WSThread implements WSClientI
53 boolean submitGaps = false; // pass sequences including gaps to alignment
57 boolean preserveOrder = true; // and always store and recover sequence
62 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].getSequenceAsString()
131 : AlignSeq.extractGaps(
132 jalview.util.Comparison.GapChars, seqs[i]
133 .getSequenceAsString()));
138 if (seqs[i].getEnd() >= seqs[i].getStart())
140 empty = (submitGaps) ? seqs[i].getSequenceAsString()
141 : AlignSeq.extractGaps(
142 jalview.util.Comparison.GapChars, seqs[i]
143 .getSequenceAsString());
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 &&
162 ( (MsaResult) result).getMsa().getSeqs() != null)
169 public Object[] getAlignment()
172 if (result != null && result.isFinished())
174 SequenceI[] alseqs = null;
175 char alseq_gapchar = '-';
177 if ( ( (MsaResult) result).getMsa() != null)
179 alseqs = getVamsasAlignment( ( (MsaResult) result).getMsa());
180 alseq_gapchar = ( (MsaResult) result).getMsa().getGapchar().charAt(0);
181 alseq_l = alseqs.length;
183 if (emptySeqs.size() > 0)
185 SequenceI[] t_alseqs = new SequenceI[alseq_l + emptySeqs.size()];
190 for (i = 0, w = alseqs[0].getLength(); i < alseq_l; i++)
192 if (w < alseqs[i].getLength())
194 w = alseqs[i].getLength();
196 t_alseqs[i] = alseqs[i];
200 // check that aligned width is at least as wide as emptySeqs width.
202 for (i = 0, w = emptySeqs.size(); i < w; i++)
204 String[] es = (String[]) emptySeqs.get(i);
205 if (es != null && es[1] != null)
207 int sw = es[1].length();
214 // make a gapped string.
215 StringBuffer insbuff = new StringBuffer(w);
216 for (i = 0; i < nw; i++)
218 insbuff.append(alseq_gapchar);
222 for (i = 0; i < alseq_l; i++)
224 int sw = t_alseqs[i].getLength();
228 alseqs[i].setSequence(t_alseqs[i].getSequenceAsString() +
229 insbuff.substring(0, sw - nw));
233 for (i = 0, w = emptySeqs.size(); i < w; i++)
235 String[] es = (String[]) emptySeqs.get(i);
239 alseq_l] = new jalview.datamodel.Sequence(es[0],
240 insbuff.toString(), 1, 0);
244 if (es[1].length() < nw)
247 alseq_l] = new jalview.datamodel.Sequence(es[0],
248 es[1] + insbuff.substring(0, nw - es[1].length()), 1,
254 alseq_l] = new jalview.datamodel.Sequence(es[0], es[1]);
260 AlignmentOrder msaorder = new AlignmentOrder(alseqs);
261 // always recover the order - makes parseResult()'s life easier.
262 jalview.analysis.AlignmentSorter.recoverOrder(alseqs);
263 // account for any missing sequences
264 jalview.analysis.SeqsetUtils.deuniquify(SeqNames, alseqs);
273 * mark subjob as cancelled and set result object appropriatly
278 subjobComplete = true;
284 * @return boolean true if job can be submitted.
286 boolean hasValidInput()
288 if (seqs.getSeqs() != null)
296 String alTitle; // name which will be used to form new alignment window.
297 Alignment dataset; // dataset to which the new alignment will be
301 ext.vamsas.MuscleWS server = null;
303 * set basic options for this (group) of Msa jobs
310 MsaWSThread(ext.vamsas.MuscleWS server, String wsUrl,
311 WebserviceInfo wsinfo, jalview.gui.AlignFrame alFrame,
312 AlignmentView alview,
313 String wsname, boolean subgaps, boolean presorder)
315 super(alFrame, wsinfo, alview, wsname, wsUrl);
316 this.server = server;
317 this.submitGaps = subgaps;
318 this.preserveOrder = presorder;
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();
345 SequenceI[][] conmsa = _msa.getVisibleContigs('-');
348 int njobs = conmsa.length;
349 jobs = new MsaWSJob[njobs];
350 for (int j = 0; j < njobs; j++)
354 jobs[j] = new MsaWSJob(wsinfo.addJobPane(), conmsa[j]);
358 jobs[j] = new MsaWSJob(0, conmsa[j]);
362 wsinfo.setProgressName("region " + jobs[j].jobnum, jobs[j].jobnum);
364 wsinfo.setProgressText(jobs[j].jobnum, OutputHeader);
369 public boolean isCancellable()
374 public void cancelJob()
376 if (!jobComplete && jobs != null)
378 boolean cancelled = true;
379 for (int job = 0; job < jobs.length; job++)
381 if (jobs[job].submitted && !jobs[job].subjobComplete)
383 String cancelledMessage = "";
386 vamsas.objects.simple.WsJobId cancelledJob = server
387 .cancel(jobs[job].jobId);
388 if (cancelledJob.getStatus() == 2)
391 cancelledMessage = "Job cancelled.";
392 ( (MsaWSJob) jobs[job]).cancel();
393 wsInfo.setStatus(jobs[job].jobnum,
394 WebserviceInfo.STATE_CANCELLED_OK);
396 else if (cancelledJob.getStatus() == 3)
398 // VALID UNSTOPPABLE JOB
400 "Server cannot cancel this job. just close the window.\n";
402 // wsInfo.setStatus(jobs[job].jobnum,
403 // WebserviceInfo.STATE_RUNNING);
406 if (cancelledJob.getJobId() != null)
408 cancelledMessage += ("[" + cancelledJob.getJobId() + "]");
411 cancelledMessage += "\n";
413 catch (Exception exc)
416 ("\nProblems cancelling the job : Exception received...\n"
418 Cache.log.warn("Exception whilst cancelling " + jobs[job].jobId,
421 wsInfo.setProgressText(jobs[job].jobnum, OutputHeader
422 + cancelledMessage + "\n");
427 wsInfo.setStatus(WebserviceInfo.STATE_CANCELLED_OK);
430 this.interrupt(); // kick thread to update job states.
437 .setProgressText(OutputHeader
438 + "Server cannot cancel this job because it has not been submitted properly. just close the window.\n");
443 void pollJob(WSJob job)
446 ( (MsaWSJob) job).result = server.getResult( ( (MsaWSJob) job).jobId);
449 void StartJob(WSJob job)
451 if (! (job instanceof MsaWSJob))
453 throw new Error("StartJob(MsaWSJob) called on a WSJobInstance " +
456 MsaWSJob j = (MsaWSJob) job;
459 if (Cache.log.isDebugEnabled())
461 Cache.log.debug("Tried to submit an already submitted job " + j.jobId);
465 if (j.seqs.getSeqs() == null)
467 // special case - selection consisted entirely of empty sequences...
469 j.result = new MsaResult();
470 j.result.setFinished(true);
471 j.result.setStatus("Empty Alignment Job");
472 ( (MsaResult) j.result).setMsa(null);
476 vamsas.objects.simple.WsJobId jobsubmit = server.align(j.seqs);
478 if ( (jobsubmit != null) && (jobsubmit.getStatus() == 1))
480 j.jobId = jobsubmit.getJobId();
482 j.subjobComplete = false;
483 // System.out.println(WsURL + " Job Id '" + jobId + "'");
487 if (jobsubmit == null)
493 " returned null object, it probably cannot be contacted. Try again later ?");
496 throw new Exception(jobsubmit.getJobId());
501 // TODO: JBPNote catch timeout or other fault types explicitly
502 // For unexpected errors
504 .println(WebServiceName
505 + "Client: Failed to submit the sequences for alignment (probably a server side problem)\n"
506 + "When contacting Server:" + WsUrl + "\n"
507 + e.toString() + "\n");
508 j.allowedServerExceptions = 0;
509 wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_SERVERERROR);
510 wsInfo.setStatus(j.jobnum, WebserviceInfo.STATE_STOPPED_SERVERERROR);
514 "Failed to submit sequences for alignment.\n"
515 + "It is most likely that there is a problem with the server.\n"
516 + "Just close the window\n");
518 // e.printStackTrace(); // TODO: JBPNote DEBUG
522 private jalview.datamodel.Sequence[] getVamsasAlignment(
523 vamsas.objects.simple.Alignment valign)
525 // TODO: refactor to helper class for vamsas.objects.simple objects
526 vamsas.objects.simple.Sequence[] seqs = valign.getSeqs().getSeqs();
527 jalview.datamodel.Sequence[] msa = new jalview.datamodel.Sequence[seqs.
530 for (int i = 0, j = seqs.length; i < j; i++)
532 msa[i] = new jalview.datamodel.Sequence(seqs[i].getId(), seqs[i]
541 int results = 0; // number of result sets received
542 JobStateSummary finalState = new JobStateSummary();
545 for (int j = 0; j < jobs.length; j++)
547 finalState.updateJobPanelState(wsInfo, OutputHeader, jobs[j]);
548 if (jobs[j].submitted && jobs[j].subjobComplete && jobs[j].hasResults())
551 vamsas.objects.simple.Alignment valign = ( (MsaResult) jobs[j].result).
555 wsInfo.appendProgressText(jobs[j].jobnum,
556 "\nAlignment Object Method Notes\n");
557 String[] lines = valign.getMethod();
558 for (int line = 0; line < lines.length; line++)
560 wsInfo.appendProgressText(jobs[j].jobnum, lines[line] + "\n");
562 // JBPNote The returned files from a webservice could be
563 // hidden behind icons in the monitor window that,
564 // when clicked, pop up their corresponding data
572 Cache.log.error("Unexpected exception when processing results for " +
574 wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_ERROR);
578 wsInfo.showResultsNewFrame
579 .addActionListener(new java.awt.event.ActionListener()
581 public void actionPerformed(
582 java.awt.event.ActionEvent evt)
584 displayResults(true);
588 .addActionListener(new java.awt.event.ActionListener()
590 public void actionPerformed(
591 java.awt.event.ActionEvent evt)
593 displayResults(false);
596 wsInfo.setResultsReady();
600 wsInfo.setFinishedNoResults();
604 void displayResults(boolean newFrame)
606 // view input or result data for each block
607 Vector alorders = new Vector();
608 SequenceI[][] results = new SequenceI[jobs.length][];
609 AlignmentOrder[] orders = new AlignmentOrder[jobs.length];
610 for (int j = 0; j < jobs.length; j++)
612 if (jobs[j].hasResults())
614 Object[] res = ( (MsaWSJob) jobs[j]).getAlignment();
615 alorders.add(res[1]);
616 results[j] = (SequenceI[]) res[0];
617 orders[j] = (AlignmentOrder) res[1];
619 // SequenceI[] alignment = input.getUpdated
626 Object[] newview = input.getUpdatedView(results, orders, getGapChar());
627 // trash references to original result data
628 for (int j = 0; j < jobs.length; j++)
633 SequenceI[] alignment = (SequenceI[]) newview[0];
634 ColumnSelection columnselection = (ColumnSelection) newview[1];
635 Alignment al = new Alignment(alignment);
636 // TODO: add 'provenance' property to alignment from the method notes accompanying each subjob
639 al.setDataset(dataset);
642 propagateDatasetMappings(al);
643 // JBNote- TODO: warn user if a block is input rather than aligned data ?
647 AlignFrame af = new AlignFrame(al, columnselection,
648 AlignFrame.DEFAULT_WIDTH,
649 AlignFrame.DEFAULT_HEIGHT);
651 // initialise with same renderer settings as in parent alignframe.
652 af.getFeatureRenderer().transferSettings(
653 this.featureSettings);
655 if (alorders.size() > 0)
657 if (alorders.size() == 1)
659 af.addSortByOrderMenuItem(WebServiceName + " Ordering",
660 (AlignmentOrder) alorders.get(0));
664 // construct a non-redundant ordering set
665 Vector names = new Vector();
666 for (int i = 0, l = alorders.size(); i < l; i++)
668 String orderName = new String(" Region " + i);
673 if ( ( (AlignmentOrder) alorders.get(i)).equals( ( (
674 AlignmentOrder) alorders.get(j))))
678 orderName += "," + j;
686 if (i == 0 && j == 1)
688 names.add(new String(""));
692 names.add(orderName);
695 for (int i = 0, l = alorders.size(); i < l; i++)
697 af.addSortByOrderMenuItem(WebServiceName
698 + ( (String) names.get(i)) +
700 (AlignmentOrder) alorders.get(i));
705 Desktop.addInternalFrame(af, alTitle,
706 AlignFrame.DEFAULT_WIDTH,
707 AlignFrame.DEFAULT_HEIGHT);
712 System.out.println("MERGE WITH OLD FRAME");
713 // TODO: modify alignment in original frame, replacing old for new alignment using the commands.EditCommand model to ensure the update can be undone
717 public boolean canMergeResults()