2 * Jalview - A Sequence Alignment Editor and Viewer (Version 2.5)
\r
3 * Copyright (C) 2010 J Procter, AM Waterhouse, G Barton, M Clamp, S Searle
\r
5 * This file is part of Jalview.
\r
7 * Jalview is free software: you can redistribute it and/or
\r
8 * modify it under the terms of the GNU General Public License
\r
9 * as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
\r
11 * Jalview is distributed in the hope that it will be useful, but
\r
12 * WITHOUT ANY WARRANTY; without even the implied warranty
\r
13 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR
\r
14 * PURPOSE. See the GNU General Public License for more details.
\r
16 * You should have received a copy of the GNU General Public License along with Jalview. If not, see <http://www.gnu.org/licenses/>.
\r
22 import jalview.analysis.*;
\r
23 import jalview.bin.*;
\r
24 import jalview.datamodel.*;
\r
25 import jalview.gui.*;
\r
26 import vamsas.objects.simple.MsaResult;
\r
38 * Copyright: Copyright (c) 2004
\r
42 * Company: Dundee University
\r
45 * @author not attributable
\r
48 class MsaWSThread extends JWS1Thread implements WSClientI
\r
50 boolean submitGaps = false; // pass sequences including gaps to alignment
\r
54 boolean preserveOrder = true; // and always store and recover sequence
\r
58 class MsaWSJob extends WSJob
\r
60 // hold special input for this
\r
61 vamsas.objects.simple.SequenceSet seqs = new vamsas.objects.simple.SequenceSet();
\r
71 public MsaWSJob(int jobNum, SequenceI[] inSeqs)
\r
73 this.jobnum = jobNum;
\r
74 if (!prepareInput(inSeqs, 2))
\r
77 subjobComplete = true;
\r
78 result = new MsaResult();
\r
79 result.setFinished(true);
\r
80 result.setStatus("Job never ran - input returned to user.");
\r
85 Hashtable SeqNames = new Hashtable();
\r
87 Vector emptySeqs = new Vector();
\r
90 * prepare input sequences for MsaWS service
\r
93 * jalview sequences to be prepared
\r
95 * 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
104 "Implementation error: minlen must be zero or more.");
\r
106 for (int i = 0; i < seqs.length; i++)
\r
108 if (seqs[i].getEnd() - seqs[i].getStart() > minlen - 1)
\r
113 boolean valid = nseqs > 1; // need at least two seqs
\r
114 vamsas.objects.simple.Sequence[] seqarray = (valid) ? new vamsas.objects.simple.Sequence[nseqs]
\r
116 for (int i = 0, n = 0; i < seqs.length; i++)
\r
119 String newname = jalview.analysis.SeqsetUtils.unique_name(i); // same
\r
123 SeqNames.put(newname, jalview.analysis.SeqsetUtils
\r
124 .SeqCharacterHash(seqs[i]));
\r
125 if (valid && seqs[i].getEnd() - seqs[i].getStart() > minlen - 1)
\r
127 seqarray[n] = new vamsas.objects.simple.Sequence();
\r
128 seqarray[n].setId(newname);
\r
129 seqarray[n++].setSeq((submitGaps) ? seqs[i].getSequenceAsString()
\r
130 : AlignSeq.extractGaps(jalview.util.Comparison.GapChars,
\r
131 seqs[i].getSequenceAsString()));
\r
135 String empty = null;
\r
136 if (seqs[i].getEnd() >= seqs[i].getStart())
\r
138 empty = (submitGaps) ? seqs[i].getSequenceAsString() : AlignSeq
\r
139 .extractGaps(jalview.util.Comparison.GapChars, seqs[i]
\r
140 .getSequenceAsString());
\r
142 emptySeqs.add(new String[]
\r
143 { newname, empty });
\r
146 this.seqs = new vamsas.objects.simple.SequenceSet();
\r
147 this.seqs.setSeqs(seqarray);
\r
153 * @return true if getAlignment will return a valid alignment result.
\r
155 public boolean hasResults()
\r
157 if (subjobComplete && result != null && result.isFinished()
\r
158 && ((MsaResult) result).getMsa() != null
\r
159 && ((MsaResult) result).getMsa().getSeqs() != null)
\r
166 public Object[] getAlignment()
\r
169 if (result != null && result.isFinished())
\r
171 SequenceI[] alseqs = null;
\r
172 char alseq_gapchar = '-';
\r
174 if (((MsaResult) result).getMsa() != null)
\r
176 alseqs = getVamsasAlignment(((MsaResult) result).getMsa());
\r
177 alseq_gapchar = ((MsaResult) result).getMsa().getGapchar()
\r
179 alseq_l = alseqs.length;
\r
181 if (emptySeqs.size() > 0)
\r
183 SequenceI[] t_alseqs = new SequenceI[alseq_l + emptySeqs.size()];
\r
188 for (i = 0, w = alseqs[0].getLength(); i < alseq_l; i++)
\r
190 if (w < alseqs[i].getLength())
\r
192 w = alseqs[i].getLength();
\r
194 t_alseqs[i] = alseqs[i];
\r
198 // check that aligned width is at least as wide as emptySeqs width.
\r
199 int ow = w, nw = w;
\r
200 for (i = 0, w = emptySeqs.size(); i < w; i++)
\r
202 String[] es = (String[]) emptySeqs.get(i);
\r
203 if (es != null && es[1] != null)
\r
205 int sw = es[1].length();
\r
212 // make a gapped string.
\r
213 StringBuffer insbuff = new StringBuffer(w);
\r
214 for (i = 0; i < nw; i++)
\r
216 insbuff.append(alseq_gapchar);
\r
220 for (i = 0; i < alseq_l; i++)
\r
222 int sw = t_alseqs[i].getLength();
\r
226 alseqs[i].setSequence(t_alseqs[i].getSequenceAsString()
\r
227 + insbuff.substring(0, sw - nw));
\r
231 for (i = 0, w = emptySeqs.size(); i < w; i++)
\r
233 String[] es = (String[]) emptySeqs.get(i);
\r
236 t_alseqs[i + alseq_l] = new jalview.datamodel.Sequence(es[0],
\r
237 insbuff.toString(), 1, 0);
\r
241 if (es[1].length() < nw)
\r
243 t_alseqs[i + alseq_l] = new jalview.datamodel.Sequence(
\r
245 es[1] + insbuff.substring(0, nw - es[1].length()),
\r
246 1, 1 + es[1].length());
\r
250 t_alseqs[i + alseq_l] = new jalview.datamodel.Sequence(
\r
257 AlignmentOrder msaorder = new AlignmentOrder(alseqs);
\r
258 // always recover the order - makes parseResult()'s life easier.
\r
259 jalview.analysis.AlignmentSorter.recoverOrder(alseqs);
\r
260 // account for any missing sequences
\r
261 jalview.analysis.SeqsetUtils.deuniquify(SeqNames, alseqs);
\r
262 return new Object[]
\r
263 { alseqs, msaorder };
\r
269 * mark subjob as cancelled and set result object appropriatly
\r
274 subjobComplete = true;
\r
280 * @return boolean true if job can be submitted.
\r
282 public boolean hasValidInput()
\r
284 if (seqs.getSeqs() != null)
\r
292 String alTitle; // name which will be used to form new alignment window.
\r
294 Alignment dataset; // dataset to which the new alignment will be
\r
298 ext.vamsas.MuscleWS server = null;
\r
301 * set basic options for this (group) of Msa jobs
\r
308 MsaWSThread(ext.vamsas.MuscleWS server, String wsUrl,
\r
309 WebserviceInfo wsinfo, jalview.gui.AlignFrame alFrame,
\r
310 AlignmentView alview, String wsname, boolean subgaps,
\r
313 super(alFrame, wsinfo, alview, wsname, wsUrl);
\r
314 this.server = server;
\r
315 this.submitGaps = subgaps;
\r
316 this.preserveOrder = presorder;
\r
320 * create one or more Msa jobs to align visible seuqences in _msa
\r
333 MsaWSThread(ext.vamsas.MuscleWS server, String wsUrl,
\r
334 WebserviceInfo wsinfo, jalview.gui.AlignFrame alFrame,
\r
335 String wsname, String title, AlignmentView _msa, boolean subgaps,
\r
336 boolean presorder, Alignment seqset)
\r
338 this(server, wsUrl, wsinfo, alFrame, _msa, wsname, subgaps, presorder);
\r
339 OutputHeader = wsInfo.getProgressText();
\r
343 SequenceI[][] conmsa = _msa.getVisibleContigs('-');
\r
344 if (conmsa != null)
\r
346 int njobs = conmsa.length;
\r
347 jobs = new MsaWSJob[njobs];
\r
348 for (int j = 0; j < njobs; j++)
\r
352 jobs[j] = new MsaWSJob(wsinfo.addJobPane(), conmsa[j]);
\r
356 jobs[j] = new MsaWSJob(0, conmsa[j]);
\r
361 .setProgressName("region " + 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 += "Server cannot cancel this job. just close the window.\n";
\r
401 // wsInfo.setStatus(jobs[job].jobnum,
\r
402 // WebserviceInfo.STATE_RUNNING);
\r
405 if (cancelledJob.getJobId() != null)
\r
407 cancelledMessage += ("[" + cancelledJob.getJobId() + "]");
\r
410 cancelledMessage += "\n";
\r
411 } catch (Exception exc)
\r
413 cancelledMessage += ("\nProblems cancelling the job : Exception received...\n"
\r
416 "Exception whilst cancelling " + jobs[job].jobId, exc);
\r
418 wsInfo.setProgressText(jobs[job].jobnum, OutputHeader
\r
419 + cancelledMessage + "\n");
\r
424 wsInfo.setStatus(WebserviceInfo.STATE_CANCELLED_OK);
\r
425 jobComplete = true;
\r
427 this.interrupt(); // kick thread to update job states.
\r
434 .setProgressText(OutputHeader
\r
435 + "Server cannot cancel this job because it has not been submitted properly. just close the window.\n");
\r
440 public void pollJob(AWsJob job) throws Exception
\r
442 ((MsaWSJob) job).result = server.getResult(((MsaWSJob) job).jobId);
\r
445 public void StartJob(AWsJob job)
\r
447 if (!(job instanceof MsaWSJob))
\r
449 throw new Error("StartJob(MsaWSJob) called on a WSJobInstance "
\r
452 MsaWSJob j = (MsaWSJob) job;
\r
455 if (Cache.log.isDebugEnabled())
\r
457 Cache.log.debug("Tried to submit an already submitted job "
\r
462 if (j.seqs.getSeqs() == null)
\r
464 // special case - selection consisted entirely of empty sequences...
\r
465 j.submitted = true;
\r
466 j.result = new MsaResult();
\r
467 j.result.setFinished(true);
\r
468 j.result.setStatus("Empty Alignment Job");
\r
469 ((MsaResult) j.result).setMsa(null);
\r
473 vamsas.objects.simple.WsJobId jobsubmit = server.align(j.seqs);
\r
475 if ((jobsubmit != null) && (jobsubmit.getStatus() == 1))
\r
477 j.jobId = jobsubmit.getJobId();
\r
478 j.submitted = true;
\r
479 j.subjobComplete = false;
\r
480 // System.out.println(WsURL + " Job Id '" + jobId + "'");
\r
484 if (jobsubmit == null)
\r
486 throw new Exception(
\r
489 + " returned null object, it probably cannot be contacted. Try again later ?");
\r
492 throw new Exception(jobsubmit.getJobId());
\r
494 } catch (Exception e)
\r
496 // TODO: JBPNote catch timeout or other fault types explicitly
\r
497 // For unexpected errors
\r
499 .println(WebServiceName
\r
500 + "Client: Failed to submit the sequences for alignment (probably a server side problem)\n"
\r
501 + "When contacting Server:" + WsUrl + "\n"
\r
502 + e.toString() + "\n");
\r
503 j.allowedServerExceptions = 0;
\r
504 wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_SERVERERROR);
\r
505 wsInfo.setStatus(j.jobnum, WebserviceInfo.STATE_STOPPED_SERVERERROR);
\r
507 .appendProgressText(
\r
509 "Failed to submit sequences for alignment.\n"
\r
510 + "It is most likely that there is a problem with the server.\n"
\r
511 + "Just close the window\n");
\r
513 // e.printStackTrace(); // TODO: JBPNote DEBUG
\r
517 private jalview.datamodel.Sequence[] getVamsasAlignment(
\r
518 vamsas.objects.simple.Alignment valign)
\r
520 // TODO: refactor to helper class for vamsas.objects.simple objects
\r
521 vamsas.objects.simple.Sequence[] seqs = valign.getSeqs().getSeqs();
\r
522 jalview.datamodel.Sequence[] msa = new jalview.datamodel.Sequence[seqs.length];
\r
524 for (int i = 0, j = seqs.length; i < j; i++)
\r
526 msa[i] = new jalview.datamodel.Sequence(seqs[i].getId(), seqs[i]
\r
533 public void parseResult()
\r
535 int results = 0; // number of result sets received
\r
536 JobStateSummary finalState = new JobStateSummary();
\r
539 for (int j = 0; j < jobs.length; j++)
\r
541 finalState.updateJobPanelState(wsInfo, OutputHeader, jobs[j]);
\r
542 if (jobs[j].submitted && jobs[j].subjobComplete
\r
543 && jobs[j].hasResults())
\r
546 vamsas.objects.simple.Alignment valign = ((MsaResult)((MsaWSJob) jobs[j]).result)
\r
548 if (valign != null)
\r
550 wsInfo.appendProgressText(jobs[j].jobnum,
\r
551 "\nAlignment Object Method Notes\n");
\r
552 String[] lines = valign.getMethod();
\r
553 for (int line = 0; line < lines.length; line++)
\r
555 wsInfo.appendProgressText(jobs[j].jobnum, lines[line] + "\n");
\r
557 // JBPNote The returned files from a webservice could be
\r
558 // hidden behind icons in the monitor window that,
\r
559 // when clicked, pop up their corresponding data
\r
563 } catch (Exception ex)
\r
566 Cache.log.error("Unexpected exception when processing results for "
\r
568 wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_ERROR);
\r
572 wsInfo.showResultsNewFrame
\r
573 .addActionListener(new java.awt.event.ActionListener()
\r
575 public void actionPerformed(java.awt.event.ActionEvent evt)
\r
577 displayResults(true);
\r
580 wsInfo.mergeResults
\r
581 .addActionListener(new java.awt.event.ActionListener()
\r
583 public void actionPerformed(java.awt.event.ActionEvent evt)
\r
585 displayResults(false);
\r
588 wsInfo.setResultsReady();
\r
592 wsInfo.setFinishedNoResults();
\r
596 void displayResults(boolean newFrame)
\r
598 // view input or result data for each block
\r
599 Vector alorders = new Vector();
\r
600 SequenceI[][] results = new SequenceI[jobs.length][];
\r
601 AlignmentOrder[] orders = new AlignmentOrder[jobs.length];
\r
602 for (int j = 0; j < jobs.length; j++)
\r
604 if (jobs[j].hasResults())
\r
606 Object[] res = ((MsaWSJob) jobs[j]).getAlignment();
\r
607 alorders.add(res[1]);
\r
608 results[j] = (SequenceI[]) res[0];
\r
609 orders[j] = (AlignmentOrder) res[1];
\r
611 // SequenceI[] alignment = input.getUpdated
\r
618 Object[] newview = input.getUpdatedView(results, orders, getGapChar());
\r
619 // trash references to original result data
\r
620 for (int j = 0; j < jobs.length; j++)
\r
625 SequenceI[] alignment = (SequenceI[]) newview[0];
\r
626 ColumnSelection columnselection = (ColumnSelection) newview[1];
\r
627 Alignment al = new Alignment(alignment);
\r
628 // TODO: add 'provenance' property to alignment from the method notes
\r
629 // accompanying each subjob
\r
630 if (dataset != null)
\r
632 al.setDataset(dataset);
\r
635 propagateDatasetMappings(al);
\r
636 // JBNote- TODO: warn user if a block is input rather than aligned data ?
\r
640 AlignFrame af = new AlignFrame(al, columnselection,
\r
641 AlignFrame.DEFAULT_WIDTH, AlignFrame.DEFAULT_HEIGHT);
\r
643 // initialise with same renderer settings as in parent alignframe.
\r
644 af.getFeatureRenderer().transferSettings(this.featureSettings);
\r
646 if (alorders.size() > 0)
\r
648 if (alorders.size() == 1)
\r
650 af.addSortByOrderMenuItem(WebServiceName + " Ordering",
\r
651 (AlignmentOrder) alorders.get(0));
\r
655 // construct a non-redundant ordering set
\r
656 Vector names = new Vector();
\r
657 for (int i = 0, l = alorders.size(); i < l; i++)
\r
659 String orderName = new String(" Region " + i);
\r
664 if (((AlignmentOrder) alorders.get(i))
\r
665 .equals(((AlignmentOrder) alorders.get(j))))
\r
667 alorders.remove(j);
\r
669 orderName += "," + j;
\r
677 if (i == 0 && j == 1)
\r
679 names.add(new String(""));
\r
683 names.add(orderName);
\r
686 for (int i = 0, l = alorders.size(); i < l; i++)
\r
688 af.addSortByOrderMenuItem(WebServiceName
\r
689 + ((String) names.get(i)) + " Ordering",
\r
690 (AlignmentOrder) alorders.get(i));
\r
695 Desktop.addInternalFrame(af, alTitle, AlignFrame.DEFAULT_WIDTH,
\r
696 AlignFrame.DEFAULT_HEIGHT);
\r
701 System.out.println("MERGE WITH OLD FRAME");
\r
702 // TODO: modify alignment in original frame, replacing old for new
\r
703 // alignment using the commands.EditCommand model to ensure the update can
\r
708 public boolean canMergeResults()
\r