2 * Jalview - A Sequence Alignment Editor and Viewer (Version 2.4)
\r
3 * Copyright (C) 2008 AM Waterhouse, J Procter, G Barton, M Clamp, S Searle
\r
5 * This program is free software; you can redistribute it and/or
\r
6 * modify it under the terms of the GNU General Public License
\r
7 * as published by the Free Software Foundation; either version 2
\r
8 * of the License, or (at your option) any later version.
\r
10 * This program is distributed in the hope that it will be useful,
\r
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
\r
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
\r
13 * GNU General Public License for more details.
\r
15 * You should have received a copy of the GNU General Public License
\r
16 * along with this program; if not, write to the Free Software
\r
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
\r
23 import jalview.analysis.*;
\r
24 import jalview.bin.*;
\r
25 import jalview.datamodel.*;
\r
26 import jalview.datamodel.Alignment;
\r
27 import jalview.gui.*;
\r
28 import vamsas.objects.simple.MsaResult;
\r
40 * Copyright: Copyright (c) 2004
\r
44 * Company: Dundee University
\r
47 * @author not attributable
\r
51 extends WSThread implements WSClientI
\r
53 boolean submitGaps = false; // pass sequences including gaps to alignment
\r
57 boolean preserveOrder = true; // and always store and recover sequence
\r
62 extends WSThread.WSJob
\r
64 // hold special input for this
\r
65 vamsas.objects.simple.SequenceSet seqs = new vamsas.objects.simple.
\r
76 public MsaWSJob(int jobNum, SequenceI[] inSeqs)
\r
78 this.jobnum = jobNum;
\r
79 if (!prepareInput(inSeqs, 2))
\r
82 subjobComplete = true;
\r
83 result = new MsaResult();
\r
84 result.setFinished(true);
\r
85 result.setStatus("Job never ran - input returned to user.");
\r
90 Hashtable SeqNames = new Hashtable();
\r
91 Vector emptySeqs = new Vector();
\r
93 * prepare input sequences for MsaWS service
\r
94 * @param seqs jalview sequences to be prepared
\r
95 * @param minlen minimum number of residues required for this MsaWS service
\r
96 * @return true if seqs contains sequences to be submitted to service.
\r
98 private boolean prepareInput(SequenceI[] seqs, int minlen)
\r
103 throw new Error("Implementation error: minlen must be zero or more.");
\r
105 for (int i = 0; i < seqs.length; i++)
\r
107 if (seqs[i].getEnd() - seqs[i].getStart() > minlen - 1)
\r
112 boolean valid = nseqs > 1; // need at least two seqs
\r
113 vamsas.objects.simple.Sequence[] seqarray =
\r
115 ? new vamsas.objects.simple.Sequence[nseqs]
\r
117 for (int i = 0, n = 0; i < seqs.length; i++)
\r
120 String newname = jalview.analysis.SeqsetUtils.unique_name(i); // same
\r
124 SeqNames.put(newname, jalview.analysis.SeqsetUtils
\r
125 .SeqCharacterHash(seqs[i]));
\r
126 if (valid && seqs[i].getEnd() - seqs[i].getStart() > minlen - 1)
\r
128 seqarray[n] = new vamsas.objects.simple.Sequence();
\r
129 seqarray[n].setId(newname);
\r
130 seqarray[n++].setSeq( (submitGaps) ? seqs[i].getSequenceAsString()
\r
131 : AlignSeq.extractGaps(
\r
132 jalview.util.Comparison.GapChars, seqs[i]
\r
133 .getSequenceAsString()));
\r
137 String empty = null;
\r
138 if (seqs[i].getEnd() >= seqs[i].getStart())
\r
140 empty = (submitGaps) ? seqs[i].getSequenceAsString()
\r
141 : AlignSeq.extractGaps(
\r
142 jalview.util.Comparison.GapChars, seqs[i]
\r
143 .getSequenceAsString());
\r
145 emptySeqs.add(new String[]
\r
149 this.seqs = new vamsas.objects.simple.SequenceSet();
\r
150 this.seqs.setSeqs(seqarray);
\r
156 * @return true if getAlignment will return a valid alignment result.
\r
158 public boolean hasResults()
\r
160 if (subjobComplete && result != null && result.isFinished()
\r
161 && ( (MsaResult) result).getMsa() != null &&
\r
162 ( (MsaResult) result).getMsa().getSeqs() != null)
\r
169 public Object[] getAlignment()
\r
172 if (result != null && result.isFinished())
\r
174 SequenceI[] alseqs = null;
\r
175 char alseq_gapchar = '-';
\r
177 if ( ( (MsaResult) result).getMsa() != null)
\r
179 alseqs = getVamsasAlignment( ( (MsaResult) result).getMsa());
\r
180 alseq_gapchar = ( (MsaResult) result).getMsa().getGapchar().charAt(0);
\r
181 alseq_l = alseqs.length;
\r
183 if (emptySeqs.size() > 0)
\r
185 SequenceI[] t_alseqs = new SequenceI[alseq_l + emptySeqs.size()];
\r
190 for (i = 0, w = alseqs[0].getLength(); i < alseq_l; i++)
\r
192 if (w < alseqs[i].getLength())
\r
194 w = alseqs[i].getLength();
\r
196 t_alseqs[i] = alseqs[i];
\r
200 // check that aligned width is at least as wide as emptySeqs width.
\r
201 int ow = w, nw = w;
\r
202 for (i = 0, w = emptySeqs.size(); i < w; i++)
\r
204 String[] es = (String[]) emptySeqs.get(i);
\r
205 if (es != null && es[1] != null)
\r
207 int sw = es[1].length();
\r
214 // make a gapped string.
\r
215 StringBuffer insbuff = new StringBuffer(w);
\r
216 for (i = 0; i < nw; i++)
\r
218 insbuff.append(alseq_gapchar);
\r
222 for (i = 0; i < alseq_l; i++)
\r
224 int sw = t_alseqs[i].getLength();
\r
228 alseqs[i].setSequence(t_alseqs[i].getSequenceAsString() +
\r
229 insbuff.substring(0, sw - nw));
\r
233 for (i = 0, w = emptySeqs.size(); i < w; i++)
\r
235 String[] es = (String[]) emptySeqs.get(i);
\r
239 alseq_l] = new jalview.datamodel.Sequence(es[0],
\r
240 insbuff.toString(), 1, 0);
\r
244 if (es[1].length() < nw)
\r
247 alseq_l] = new jalview.datamodel.Sequence(es[0],
\r
248 es[1] + insbuff.substring(0, nw - es[1].length()), 1,
\r
249 1 + es[1].length());
\r
254 alseq_l] = new jalview.datamodel.Sequence(es[0], es[1]);
\r
260 AlignmentOrder msaorder = new AlignmentOrder(alseqs);
\r
261 // always recover the order - makes parseResult()'s life easier.
\r
262 jalview.analysis.AlignmentSorter.recoverOrder(alseqs);
\r
263 // account for any missing sequences
\r
264 jalview.analysis.SeqsetUtils.deuniquify(SeqNames, alseqs);
\r
265 return new Object[]
\r
273 * mark subjob as cancelled and set result object appropriatly
\r
278 subjobComplete = true;
\r
284 * @return boolean true if job can be submitted.
\r
286 boolean hasValidInput()
\r
288 if (seqs.getSeqs() != null)
\r
296 String alTitle; // name which will be used to form new alignment window.
\r
297 Alignment dataset; // dataset to which the new alignment will be
\r
301 ext.vamsas.MuscleWS server = null;
\r
303 * set basic options for this (group) of Msa jobs
\r
310 MsaWSThread(ext.vamsas.MuscleWS server, String wsUrl,
\r
311 WebserviceInfo wsinfo, jalview.gui.AlignFrame alFrame,
\r
312 AlignmentView alview,
\r
313 String wsname, boolean subgaps, boolean presorder)
\r
315 super(alFrame, wsinfo, alview, wsname, wsUrl);
\r
316 this.server = server;
\r
317 this.submitGaps = subgaps;
\r
318 this.preserveOrder = presorder;
\r
322 * create one or more Msa jobs to align visible seuqences in _msa
\r
335 MsaWSThread(ext.vamsas.MuscleWS server, String wsUrl,
\r
336 WebserviceInfo wsinfo, jalview.gui.AlignFrame alFrame,
\r
337 String wsname, String title, AlignmentView _msa, boolean subgaps,
\r
338 boolean presorder, Alignment seqset)
\r
340 this(server, wsUrl, wsinfo, alFrame, _msa, wsname, subgaps, presorder);
\r
341 OutputHeader = wsInfo.getProgressText();
\r
345 SequenceI[][] conmsa = _msa.getVisibleContigs('-');
\r
346 if (conmsa != null)
\r
348 int njobs = conmsa.length;
\r
349 jobs = new MsaWSJob[njobs];
\r
350 for (int j = 0; j < njobs; j++)
\r
354 jobs[j] = new MsaWSJob(wsinfo.addJobPane(), conmsa[j]);
\r
358 jobs[j] = new MsaWSJob(0, conmsa[j]);
\r
362 wsinfo.setProgressName("region " + jobs[j].jobnum, jobs[j].jobnum);
\r
364 wsinfo.setProgressText(jobs[j].jobnum, OutputHeader);
\r
369 public boolean isCancellable()
\r
374 public void cancelJob()
\r
376 if (!jobComplete && jobs != null)
\r
378 boolean cancelled = true;
\r
379 for (int job = 0; job < jobs.length; job++)
\r
381 if (jobs[job].submitted && !jobs[job].subjobComplete)
\r
383 String cancelledMessage = "";
\r
386 vamsas.objects.simple.WsJobId cancelledJob = server
\r
387 .cancel(jobs[job].jobId);
\r
388 if (cancelledJob.getStatus() == 2)
\r
391 cancelledMessage = "Job cancelled.";
\r
392 ( (MsaWSJob) jobs[job]).cancel();
\r
393 wsInfo.setStatus(jobs[job].jobnum,
\r
394 WebserviceInfo.STATE_CANCELLED_OK);
\r
396 else if (cancelledJob.getStatus() == 3)
\r
398 // VALID UNSTOPPABLE JOB
\r
399 cancelledMessage +=
\r
400 "Server cannot cancel this job. just close the window.\n";
\r
402 // wsInfo.setStatus(jobs[job].jobnum,
\r
403 // WebserviceInfo.STATE_RUNNING);
\r
406 if (cancelledJob.getJobId() != null)
\r
408 cancelledMessage += ("[" + cancelledJob.getJobId() + "]");
\r
411 cancelledMessage += "\n";
\r
413 catch (Exception exc)
\r
415 cancelledMessage +=
\r
416 ("\nProblems cancelling the job : Exception received...\n"
\r
418 Cache.log.warn("Exception whilst cancelling " + jobs[job].jobId,
\r
421 wsInfo.setProgressText(jobs[job].jobnum, OutputHeader
\r
422 + cancelledMessage + "\n");
\r
427 wsInfo.setStatus(WebserviceInfo.STATE_CANCELLED_OK);
\r
428 jobComplete = true;
\r
430 this.interrupt(); // kick thread to update job states.
\r
437 .setProgressText(OutputHeader
\r
438 + "Server cannot cancel this job because it has not been submitted properly. just close the window.\n");
\r
443 void pollJob(WSJob job)
\r
446 ( (MsaWSJob) job).result = server.getResult( ( (MsaWSJob) job).jobId);
\r
449 void StartJob(WSJob job)
\r
451 if (! (job instanceof MsaWSJob))
\r
453 throw new Error("StartJob(MsaWSJob) called on a WSJobInstance " +
\r
456 MsaWSJob j = (MsaWSJob) job;
\r
459 if (Cache.log.isDebugEnabled())
\r
461 Cache.log.debug("Tried to submit an already submitted job " + j.jobId);
\r
465 if (j.seqs.getSeqs() == null)
\r
467 // special case - selection consisted entirely of empty sequences...
\r
468 j.submitted = true;
\r
469 j.result = new MsaResult();
\r
470 j.result.setFinished(true);
\r
471 j.result.setStatus("Empty Alignment Job");
\r
472 ( (MsaResult) j.result).setMsa(null);
\r
476 vamsas.objects.simple.WsJobId jobsubmit = server.align(j.seqs);
\r
478 if ( (jobsubmit != null) && (jobsubmit.getStatus() == 1))
\r
480 j.jobId = jobsubmit.getJobId();
\r
481 j.submitted = true;
\r
482 j.subjobComplete = false;
\r
483 // System.out.println(WsURL + " Job Id '" + jobId + "'");
\r
487 if (jobsubmit == null)
\r
489 throw new Exception(
\r
493 " returned null object, it probably cannot be contacted. Try again later ?");
\r
496 throw new Exception(jobsubmit.getJobId());
\r
499 catch (Exception e)
\r
501 // TODO: JBPNote catch timeout or other fault types explicitly
\r
502 // For unexpected errors
\r
504 .println(WebServiceName
\r
505 + "Client: Failed to submit the sequences for alignment (probably a server side problem)\n"
\r
506 + "When contacting Server:" + WsUrl + "\n"
\r
507 + e.toString() + "\n");
\r
508 j.allowedServerExceptions = 0;
\r
509 wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_SERVERERROR);
\r
510 wsInfo.setStatus(j.jobnum, WebserviceInfo.STATE_STOPPED_SERVERERROR);
\r
512 .appendProgressText(
\r
514 "Failed to submit sequences for alignment.\n"
\r
515 + "It is most likely that there is a problem with the server.\n"
\r
516 + "Just close the window\n");
\r
518 // e.printStackTrace(); // TODO: JBPNote DEBUG
\r
522 private jalview.datamodel.Sequence[] getVamsasAlignment(
\r
523 vamsas.objects.simple.Alignment valign)
\r
525 // TODO: refactor to helper class for vamsas.objects.simple objects
\r
526 vamsas.objects.simple.Sequence[] seqs = valign.getSeqs().getSeqs();
\r
527 jalview.datamodel.Sequence[] msa = new jalview.datamodel.Sequence[seqs.
\r
530 for (int i = 0, j = seqs.length; i < j; i++)
\r
532 msa[i] = new jalview.datamodel.Sequence(seqs[i].getId(), seqs[i]
\r
541 int results = 0; // number of result sets received
\r
542 JobStateSummary finalState = new JobStateSummary();
\r
545 for (int j = 0; j < jobs.length; j++)
\r
547 finalState.updateJobPanelState(wsInfo, OutputHeader, jobs[j]);
\r
548 if (jobs[j].submitted && jobs[j].subjobComplete && jobs[j].hasResults())
\r
551 vamsas.objects.simple.Alignment valign = ( (MsaResult) jobs[j].result).
\r
553 if (valign != null)
\r
555 wsInfo.appendProgressText(jobs[j].jobnum,
\r
556 "\nAlignment Object Method Notes\n");
\r
557 String[] lines = valign.getMethod();
\r
558 for (int line = 0; line < lines.length; line++)
\r
560 wsInfo.appendProgressText(jobs[j].jobnum, lines[line] + "\n");
\r
562 // JBPNote The returned files from a webservice could be
\r
563 // hidden behind icons in the monitor window that,
\r
564 // when clicked, pop up their corresponding data
\r
569 catch (Exception ex)
\r
572 Cache.log.error("Unexpected exception when processing results for " +
\r
574 wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_ERROR);
\r
578 wsInfo.showResultsNewFrame
\r
579 .addActionListener(new java.awt.event.ActionListener()
\r
581 public void actionPerformed(
\r
582 java.awt.event.ActionEvent evt)
\r
584 displayResults(true);
\r
587 wsInfo.mergeResults
\r
588 .addActionListener(new java.awt.event.ActionListener()
\r
590 public void actionPerformed(
\r
591 java.awt.event.ActionEvent evt)
\r
593 displayResults(false);
\r
596 wsInfo.setResultsReady();
\r
600 wsInfo.setFinishedNoResults();
\r
604 void displayResults(boolean newFrame)
\r
606 // view input or result data for each block
\r
607 Vector alorders = new Vector();
\r
608 SequenceI[][] results = new SequenceI[jobs.length][];
\r
609 AlignmentOrder[] orders = new AlignmentOrder[jobs.length];
\r
610 for (int j = 0; j < jobs.length; j++)
\r
612 if (jobs[j].hasResults())
\r
614 Object[] res = ( (MsaWSJob) jobs[j]).getAlignment();
\r
615 alorders.add(res[1]);
\r
616 results[j] = (SequenceI[]) res[0];
\r
617 orders[j] = (AlignmentOrder) res[1];
\r
619 // SequenceI[] alignment = input.getUpdated
\r
626 Object[] newview = input.getUpdatedView(results, orders, getGapChar());
\r
627 // trash references to original result data
\r
628 for (int j = 0; j < jobs.length; j++)
\r
633 SequenceI[] alignment = (SequenceI[]) newview[0];
\r
634 ColumnSelection columnselection = (ColumnSelection) newview[1];
\r
635 Alignment al = new Alignment(alignment);
\r
636 // TODO: add 'provenance' property to alignment from the method notes accompanying each subjob
\r
637 if (dataset != null)
\r
639 al.setDataset(dataset);
\r
642 propagateDatasetMappings(al);
\r
643 // JBNote- TODO: warn user if a block is input rather than aligned data ?
\r
647 AlignFrame af = new AlignFrame(al, columnselection,
\r
648 AlignFrame.DEFAULT_WIDTH,
\r
649 AlignFrame.DEFAULT_HEIGHT);
\r
651 // initialise with same renderer settings as in parent alignframe.
\r
652 af.getFeatureRenderer().transferSettings(
\r
653 this.featureSettings);
\r
655 if (alorders.size() > 0)
\r
657 if (alorders.size() == 1)
\r
659 af.addSortByOrderMenuItem(WebServiceName + " Ordering",
\r
660 (AlignmentOrder) alorders.get(0));
\r
664 // construct a non-redundant ordering set
\r
665 Vector names = new Vector();
\r
666 for (int i = 0, l = alorders.size(); i < l; i++)
\r
668 String orderName = new String(" Region " + i);
\r
673 if ( ( (AlignmentOrder) alorders.get(i)).equals( ( (
\r
674 AlignmentOrder) alorders.get(j))))
\r
676 alorders.remove(j);
\r
678 orderName += "," + j;
\r
686 if (i == 0 && j == 1)
\r
688 names.add(new String(""));
\r
692 names.add(orderName);
\r
695 for (int i = 0, l = alorders.size(); i < l; i++)
\r
697 af.addSortByOrderMenuItem(WebServiceName
\r
698 + ( (String) names.get(i)) +
\r
700 (AlignmentOrder) alorders.get(i));
\r
705 Desktop.addInternalFrame(af, alTitle,
\r
706 AlignFrame.DEFAULT_WIDTH,
\r
707 AlignFrame.DEFAULT_HEIGHT);
\r
712 System.out.println("MERGE WITH OLD FRAME");
\r
713 // TODO: modify alignment in original frame, replacing old for new alignment using the commands.EditCommand model to ensure the update can be undone
\r
717 public boolean canMergeResults()
\r