2 * Jalview - A Sequence Alignment Editor and Viewer (Version 2.8.0b1)
3 * Copyright (C) 2014 The Jalview Authors
5 * This file is part of Jalview.
7 * Jalview is free software: you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
11 * Jalview is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty
13 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR
14 * PURPOSE. See the GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License along with Jalview. If not, see <http://www.gnu.org/licenses/>.
17 * The Jalview Authors are detailed in the 'AUTHORS' file.
19 package jalview.ws.jws1;
23 import jalview.analysis.*;
25 import jalview.datamodel.*;
27 import jalview.ws.AWsJob;
28 import jalview.ws.JobStateSummary;
29 import jalview.ws.WSClientI;
30 import vamsas.objects.simple.MsaResult;
32 class MsaWSThread extends JWS1Thread implements WSClientI
34 boolean submitGaps = false; // pass sequences including gaps to alignment
38 boolean preserveOrder = true; // and always store and recover sequence
42 class MsaWSJob extends WSJob
44 // hold special input for this
45 vamsas.objects.simple.SequenceSet seqs = new vamsas.objects.simple.SequenceSet();
55 public MsaWSJob(int jobNum, SequenceI[] inSeqs)
58 if (!prepareInput(inSeqs, 2))
61 subjobComplete = true;
62 result = new MsaResult();
63 result.setFinished(true);
64 result.setStatus("Job never ran - input returned to user.");
69 Hashtable SeqNames = new Hashtable();
71 Vector emptySeqs = new Vector();
74 * prepare input sequences for MsaWS service
77 * jalview sequences to be prepared
79 * minimum number of residues required for this MsaWS service
80 * @return true if seqs contains sequences to be submitted to service.
82 private boolean prepareInput(SequenceI[] seqs, int minlen)
88 "Implementation error: minlen must be zero or more.");
90 for (int i = 0; i < seqs.length; i++)
92 if (seqs[i].getEnd() - seqs[i].getStart() > minlen - 1)
97 boolean valid = nseqs > 1; // need at least two seqs
98 vamsas.objects.simple.Sequence[] seqarray = (valid) ? new vamsas.objects.simple.Sequence[nseqs]
100 for (int i = 0, n = 0; i < seqs.length; i++)
103 String newname = jalview.analysis.SeqsetUtils.unique_name(i); // same
107 SeqNames.put(newname,
108 jalview.analysis.SeqsetUtils.SeqCharacterHash(seqs[i]));
109 if (valid && seqs[i].getEnd() - seqs[i].getStart() > minlen - 1)
111 seqarray[n] = new vamsas.objects.simple.Sequence();
112 seqarray[n].setId(newname);
113 seqarray[n++].setSeq((submitGaps) ? seqs[i].getSequenceAsString()
114 : AlignSeq.extractGaps(jalview.util.Comparison.GapChars,
115 seqs[i].getSequenceAsString()));
120 if (seqs[i].getEnd() >= seqs[i].getStart())
122 empty = (submitGaps) ? seqs[i].getSequenceAsString() : AlignSeq
123 .extractGaps(jalview.util.Comparison.GapChars,
124 seqs[i].getSequenceAsString());
126 emptySeqs.add(new String[]
130 this.seqs = new vamsas.objects.simple.SequenceSet();
131 this.seqs.setSeqs(seqarray);
137 * @return true if getAlignment will return a valid alignment result.
139 public boolean hasResults()
141 if (subjobComplete && result != null && result.isFinished()
142 && ((MsaResult) result).getMsa() != null
143 && ((MsaResult) result).getMsa().getSeqs() != null)
150 public Object[] getAlignment()
153 if (result != null && result.isFinished())
155 SequenceI[] alseqs = null;
156 char alseq_gapchar = '-';
158 if (((MsaResult) result).getMsa() != null)
160 alseqs = getVamsasAlignment(((MsaResult) result).getMsa());
161 alseq_gapchar = ((MsaResult) result).getMsa().getGapchar()
163 alseq_l = alseqs.length;
165 if (emptySeqs.size() > 0)
167 SequenceI[] t_alseqs = new SequenceI[alseq_l + emptySeqs.size()];
172 for (i = 0, w = alseqs[0].getLength(); i < alseq_l; i++)
174 if (w < alseqs[i].getLength())
176 w = alseqs[i].getLength();
178 t_alseqs[i] = alseqs[i];
182 // check that aligned width is at least as wide as emptySeqs width.
184 for (i = 0, w = emptySeqs.size(); i < w; i++)
186 String[] es = (String[]) emptySeqs.get(i);
187 if (es != null && es[1] != null)
189 int sw = es[1].length();
196 // make a gapped string.
197 StringBuffer insbuff = new StringBuffer(w);
198 for (i = 0; i < nw; i++)
200 insbuff.append(alseq_gapchar);
204 for (i = 0; i < alseq_l; i++)
206 int sw = t_alseqs[i].getLength();
210 alseqs[i].setSequence(t_alseqs[i].getSequenceAsString()
211 + insbuff.substring(0, sw - nw));
215 for (i = 0, w = emptySeqs.size(); i < w; i++)
217 String[] es = (String[]) emptySeqs.get(i);
220 t_alseqs[i + alseq_l] = new jalview.datamodel.Sequence(es[0],
221 insbuff.toString(), 1, 0);
225 if (es[1].length() < nw)
227 t_alseqs[i + alseq_l] = new jalview.datamodel.Sequence(
229 es[1] + insbuff.substring(0, nw - es[1].length()),
230 1, 1 + es[1].length());
234 t_alseqs[i + alseq_l] = new jalview.datamodel.Sequence(
241 AlignmentOrder msaorder = new AlignmentOrder(alseqs);
242 // always recover the order - makes parseResult()'s life easier.
243 jalview.analysis.AlignmentSorter.recoverOrder(alseqs);
244 // account for any missing sequences
245 jalview.analysis.SeqsetUtils.deuniquify(SeqNames, alseqs);
247 { alseqs, msaorder };
253 * mark subjob as cancelled and set result object appropriatly
258 subjobComplete = true;
264 * @return boolean true if job can be submitted.
266 public boolean hasValidInput()
268 if (seqs.getSeqs() != null)
276 String alTitle; // name which will be used to form new alignment window.
278 Alignment dataset; // dataset to which the new alignment will be
282 ext.vamsas.MuscleWS server = null;
285 * set basic options for this (group) of Msa jobs
292 MsaWSThread(ext.vamsas.MuscleWS server, String wsUrl,
293 WebserviceInfo wsinfo, jalview.gui.AlignFrame alFrame,
294 AlignmentView alview, String wsname, boolean subgaps,
297 super(alFrame, wsinfo, alview, wsname, wsUrl);
298 this.server = server;
299 this.submitGaps = subgaps;
300 this.preserveOrder = presorder;
304 * create one or more Msa jobs to align visible seuqences in _msa
317 MsaWSThread(ext.vamsas.MuscleWS server, String wsUrl,
318 WebserviceInfo wsinfo, jalview.gui.AlignFrame alFrame,
319 String wsname, String title, AlignmentView _msa, boolean subgaps,
320 boolean presorder, Alignment seqset)
322 this(server, wsUrl, wsinfo, alFrame, _msa, wsname, subgaps, presorder);
323 OutputHeader = wsInfo.getProgressText();
327 SequenceI[][] conmsa = _msa.getVisibleContigs('-');
330 int njobs = conmsa.length;
331 jobs = new MsaWSJob[njobs];
332 for (int j = 0; j < njobs; j++)
336 jobs[j] = new MsaWSJob(wsinfo.addJobPane(), conmsa[j]);
340 jobs[j] = new MsaWSJob(0, conmsa[j]);
344 wsinfo.setProgressName("region " + jobs[j].getJobnum(),
345 jobs[j].getJobnum());
347 wsinfo.setProgressText(jobs[j].getJobnum(), OutputHeader);
352 public boolean isCancellable()
357 public void cancelJob()
359 if (!jobComplete && jobs != null)
361 boolean cancelled = true;
362 for (int job = 0; job < jobs.length; job++)
364 if (jobs[job].isSubmitted() && !jobs[job].isSubjobComplete())
366 String cancelledMessage = "";
369 vamsas.objects.simple.WsJobId cancelledJob = server
370 .cancel(jobs[job].getJobId());
371 if (cancelledJob.getStatus() == 2)
374 cancelledMessage = "Job cancelled.";
375 ((MsaWSJob) jobs[job]).cancel();
376 wsInfo.setStatus(jobs[job].getJobnum(),
377 WebserviceInfo.STATE_CANCELLED_OK);
379 else if (cancelledJob.getStatus() == 3)
381 // VALID UNSTOPPABLE JOB
382 cancelledMessage += "Server cannot cancel this job. just close the window.\n";
384 // wsInfo.setStatus(jobs[job].jobnum,
385 // WebserviceInfo.STATE_RUNNING);
388 if (cancelledJob.getJobId() != null)
390 cancelledMessage += ("[" + cancelledJob.getJobId() + "]");
393 cancelledMessage += "\n";
394 } catch (Exception exc)
396 cancelledMessage += ("\nProblems cancelling the job : Exception received...\n"
399 "Exception whilst cancelling " + jobs[job].getJobId(),
402 wsInfo.setProgressText(jobs[job].getJobnum(), OutputHeader
403 + cancelledMessage + "\n");
408 wsInfo.setStatus(WebserviceInfo.STATE_CANCELLED_OK);
411 this.interrupt(); // kick thread to update job states.
417 wsInfo.setProgressText(OutputHeader
418 + "Server cannot cancel this job because it has not been submitted properly. just close the window.\n");
423 public void pollJob(AWsJob job) throws Exception
425 ((MsaWSJob) job).result = server.getResult(((MsaWSJob) job).getJobId());
428 public void StartJob(AWsJob job)
430 if (!(job instanceof MsaWSJob))
432 throw new Error("StartJob(MsaWSJob) called on a WSJobInstance "
435 MsaWSJob j = (MsaWSJob) job;
438 if (Cache.log.isDebugEnabled())
440 Cache.log.debug("Tried to submit an already submitted job "
445 if (j.seqs.getSeqs() == null)
447 // special case - selection consisted entirely of empty sequences...
448 j.setSubmitted(true);
449 j.result = new MsaResult();
450 j.result.setFinished(true);
451 j.result.setStatus("Empty Alignment Job");
452 ((MsaResult) j.result).setMsa(null);
456 vamsas.objects.simple.WsJobId jobsubmit = server.align(j.seqs);
458 if ((jobsubmit != null) && (jobsubmit.getStatus() == 1))
460 j.setJobId(jobsubmit.getJobId());
461 j.setSubmitted(true);
462 j.setSubjobComplete(false);
463 // System.out.println(WsURL + " Job Id '" + jobId + "'");
467 if (jobsubmit == null)
472 + " returned null object, it probably cannot be contacted. Try again later ?");
475 throw new Exception(jobsubmit.getJobId());
477 } catch (Exception e)
479 // TODO: JBPNote catch timeout or other fault types explicitly
480 // For unexpected errors
482 .println(WebServiceName
483 + "Client: Failed to submit the sequences for alignment (probably a server side problem)\n"
484 + "When contacting Server:" + WsUrl + "\n"
485 + e.toString() + "\n");
486 j.setAllowedServerExceptions(0);
487 wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_SERVERERROR);
488 wsInfo.setStatus(j.getJobnum(),
489 WebserviceInfo.STATE_STOPPED_SERVERERROR);
490 wsInfo.appendProgressText(
492 "Failed to submit sequences for alignment.\n"
493 + "It is most likely that there is a problem with the server.\n"
494 + "Just close the window\n");
496 // e.printStackTrace(); // TODO: JBPNote DEBUG
500 private jalview.datamodel.Sequence[] getVamsasAlignment(
501 vamsas.objects.simple.Alignment valign)
503 // TODO: refactor to helper class for vamsas.objects.simple objects
504 vamsas.objects.simple.Sequence[] seqs = valign.getSeqs().getSeqs();
505 jalview.datamodel.Sequence[] msa = new jalview.datamodel.Sequence[seqs.length];
507 for (int i = 0, j = seqs.length; i < j; i++)
509 msa[i] = new jalview.datamodel.Sequence(seqs[i].getId(),
516 public void parseResult()
518 int results = 0; // number of result sets received
519 JobStateSummary finalState = new JobStateSummary();
522 for (int j = 0; j < jobs.length; j++)
524 finalState.updateJobPanelState(wsInfo, OutputHeader, jobs[j]);
525 if (jobs[j].isSubmitted() && jobs[j].isSubjobComplete()
526 && jobs[j].hasResults())
529 // if (Cache.log.isDebugEnabled())
531 // System.out.println("Job lob for job "+jobs[j].getJobId()+":"+jobs[j].getJobnum());
532 // System.out.println(jobs[j].getStatus());
535 vamsas.objects.simple.Alignment valign = ((MsaResult) ((MsaWSJob) jobs[j]).result)
539 wsInfo.appendProgressText(jobs[j].getJobnum(),
540 "\nAlignment Object Method Notes\n");
541 String[] lines = valign.getMethod();
542 for (int line = 0; line < lines.length; line++)
544 wsInfo.appendProgressText(jobs[j].getJobnum(), lines[line]
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
554 } catch (Exception ex)
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(java.awt.event.ActionEvent evt)
568 displayResults(true);
572 .addActionListener(new java.awt.event.ActionListener()
574 public void actionPerformed(java.awt.event.ActionEvent evt)
576 displayResults(false);
579 wsInfo.setResultsReady();
583 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];
593 for (int j = 0; j < jobs.length; j++)
595 if (jobs[j].hasResults())
597 Object[] res = ((MsaWSJob) jobs[j]).getAlignment();
598 alorders.add(res[1]);
599 results[j] = (SequenceI[]) res[0];
600 orders[j] = (AlignmentOrder) res[1];
602 // SequenceI[] alignment = input.getUpdated
609 Object[] newview = input.getUpdatedView(results, orders, getGapChar());
610 // trash references to original result data
611 for (int j = 0; j < jobs.length; j++)
616 SequenceI[] alignment = (SequenceI[]) newview[0];
617 ColumnSelection columnselection = (ColumnSelection) newview[1];
618 Alignment al = new Alignment(alignment);
619 // TODO: add 'provenance' property to alignment from the method notes
620 // accompanying each subjob
623 al.setDataset(dataset);
626 propagateDatasetMappings(al);
627 // JBNote- TODO: warn user if a block is input rather than aligned data ?
631 AlignFrame af = new AlignFrame(al, columnselection,
632 AlignFrame.DEFAULT_WIDTH, AlignFrame.DEFAULT_HEIGHT);
634 // initialise with same renderer settings as in parent alignframe.
635 af.getFeatureRenderer().transferSettings(this.featureSettings);
637 if (alorders.size() > 0)
639 if (alorders.size() == 1)
641 af.addSortByOrderMenuItem(WebServiceName + " Ordering",
642 (AlignmentOrder) alorders.get(0));
646 // construct a non-redundant ordering set
647 Vector names = new Vector();
648 for (int i = 0, l = alorders.size(); i < l; i++)
650 String orderName = new String(" Region " + i);
655 if (((AlignmentOrder) alorders.get(i))
656 .equals(((AlignmentOrder) alorders.get(j))))
660 orderName += "," + j;
668 if (i == 0 && j == 1)
670 names.add(new String(""));
674 names.add(orderName);
677 for (int i = 0, l = alorders.size(); i < l; i++)
679 af.addSortByOrderMenuItem(
680 WebServiceName + ((String) names.get(i)) + " Ordering",
681 (AlignmentOrder) alorders.get(i));
686 Desktop.addInternalFrame(af, alTitle, AlignFrame.DEFAULT_WIDTH,
687 AlignFrame.DEFAULT_HEIGHT);
692 System.out.println("MERGE WITH OLD FRAME");
693 // TODO: modify alignment in original frame, replacing old for new
694 // alignment using the commands.EditCommand model to ensure the update can
699 public boolean canMergeResults()