2 * Jalview - A Sequence Alignment Editor and Viewer (Version 2.8.2)
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
10 * of the License, or (at your option) any later version.
12 * Jalview is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty
14 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15 * PURPOSE. See the GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with Jalview. If not, see <http://www.gnu.org/licenses/>.
19 * The Jalview Authors are detailed in the 'AUTHORS' file.
21 package jalview.ws.jws1;
25 import jalview.analysis.*;
27 import jalview.datamodel.*;
29 import jalview.ws.AWsJob;
30 import jalview.ws.JobStateSummary;
31 import jalview.ws.WSClientI;
32 import vamsas.objects.simple.MsaResult;
34 class MsaWSThread extends JWS1Thread implements WSClientI
36 boolean submitGaps = false; // pass sequences including gaps to alignment
40 boolean preserveOrder = true; // and always store and recover sequence
44 class MsaWSJob extends WSJob
46 // hold special input for this
47 vamsas.objects.simple.SequenceSet seqs = new vamsas.objects.simple.SequenceSet();
57 public MsaWSJob(int jobNum, SequenceI[] inSeqs)
60 if (!prepareInput(inSeqs, 2))
63 subjobComplete = true;
64 result = new MsaResult();
65 result.setFinished(true);
66 result.setStatus("Job never ran - input returned to user.");
71 Hashtable SeqNames = new Hashtable();
73 Vector emptySeqs = new Vector();
76 * prepare input sequences for MsaWS service
79 * jalview sequences to be prepared
81 * minimum number of residues required for this MsaWS service
82 * @return true if seqs contains sequences to be submitted to service.
84 private boolean prepareInput(SequenceI[] seqs, int minlen)
90 "Implementation error: minlen must be zero or more.");
92 for (int i = 0; i < seqs.length; i++)
94 if (seqs[i].getEnd() - seqs[i].getStart() > minlen - 1)
99 boolean valid = nseqs > 1; // need at least two seqs
100 vamsas.objects.simple.Sequence[] seqarray = (valid) ? new vamsas.objects.simple.Sequence[nseqs]
102 for (int i = 0, n = 0; i < seqs.length; i++)
105 String newname = jalview.analysis.SeqsetUtils.unique_name(i); // same
109 SeqNames.put(newname,
110 jalview.analysis.SeqsetUtils.SeqCharacterHash(seqs[i]));
111 if (valid && seqs[i].getEnd() - seqs[i].getStart() > minlen - 1)
113 seqarray[n] = new vamsas.objects.simple.Sequence();
114 seqarray[n].setId(newname);
115 seqarray[n++].setSeq((submitGaps) ? seqs[i].getSequenceAsString()
116 : AlignSeq.extractGaps(jalview.util.Comparison.GapChars,
117 seqs[i].getSequenceAsString()));
122 if (seqs[i].getEnd() >= seqs[i].getStart())
124 empty = (submitGaps) ? seqs[i].getSequenceAsString() : AlignSeq
125 .extractGaps(jalview.util.Comparison.GapChars,
126 seqs[i].getSequenceAsString());
128 emptySeqs.add(new String[]
132 this.seqs = new vamsas.objects.simple.SequenceSet();
133 this.seqs.setSeqs(seqarray);
139 * @return true if getAlignment will return a valid alignment result.
141 public boolean hasResults()
143 if (subjobComplete && result != null && result.isFinished()
144 && ((MsaResult) result).getMsa() != null
145 && ((MsaResult) result).getMsa().getSeqs() != null)
152 public Object[] getAlignment()
155 if (result != null && result.isFinished())
157 SequenceI[] alseqs = null;
158 char alseq_gapchar = '-';
160 if (((MsaResult) result).getMsa() != null)
162 alseqs = getVamsasAlignment(((MsaResult) result).getMsa());
163 alseq_gapchar = ((MsaResult) result).getMsa().getGapchar()
165 alseq_l = alseqs.length;
167 if (emptySeqs.size() > 0)
169 SequenceI[] t_alseqs = new SequenceI[alseq_l + emptySeqs.size()];
174 for (i = 0, w = alseqs[0].getLength(); i < alseq_l; i++)
176 if (w < alseqs[i].getLength())
178 w = alseqs[i].getLength();
180 t_alseqs[i] = alseqs[i];
184 // check that aligned width is at least as wide as emptySeqs width.
186 for (i = 0, w = emptySeqs.size(); i < w; i++)
188 String[] es = (String[]) emptySeqs.get(i);
189 if (es != null && es[1] != null)
191 int sw = es[1].length();
198 // make a gapped string.
199 StringBuffer insbuff = new StringBuffer(w);
200 for (i = 0; i < nw; i++)
202 insbuff.append(alseq_gapchar);
206 for (i = 0; i < alseq_l; i++)
208 int sw = t_alseqs[i].getLength();
212 alseqs[i].setSequence(t_alseqs[i].getSequenceAsString()
213 + insbuff.substring(0, sw - nw));
217 for (i = 0, w = emptySeqs.size(); i < w; i++)
219 String[] es = (String[]) emptySeqs.get(i);
222 t_alseqs[i + alseq_l] = new jalview.datamodel.Sequence(es[0],
223 insbuff.toString(), 1, 0);
227 if (es[1].length() < nw)
229 t_alseqs[i + alseq_l] = new jalview.datamodel.Sequence(
231 es[1] + insbuff.substring(0, nw - es[1].length()),
232 1, 1 + es[1].length());
236 t_alseqs[i + alseq_l] = new jalview.datamodel.Sequence(
243 AlignmentOrder msaorder = new AlignmentOrder(alseqs);
244 // always recover the order - makes parseResult()'s life easier.
245 jalview.analysis.AlignmentSorter.recoverOrder(alseqs);
246 // account for any missing sequences
247 jalview.analysis.SeqsetUtils.deuniquify(SeqNames, alseqs);
249 { alseqs, msaorder };
255 * mark subjob as cancelled and set result object appropriatly
260 subjobComplete = true;
266 * @return boolean true if job can be submitted.
268 public boolean hasValidInput()
270 if (seqs.getSeqs() != null)
278 String alTitle; // name which will be used to form new alignment window.
280 Alignment dataset; // dataset to which the new alignment will be
284 ext.vamsas.MuscleWS server = null;
287 * set basic options for this (group) of Msa jobs
294 MsaWSThread(ext.vamsas.MuscleWS server, String wsUrl,
295 WebserviceInfo wsinfo, jalview.gui.AlignFrame alFrame,
296 AlignmentView alview, String wsname, boolean subgaps,
299 super(alFrame, wsinfo, alview, wsname, wsUrl);
300 this.server = server;
301 this.submitGaps = subgaps;
302 this.preserveOrder = presorder;
306 * create one or more Msa jobs to align visible seuqences in _msa
319 MsaWSThread(ext.vamsas.MuscleWS server, String wsUrl,
320 WebserviceInfo wsinfo, jalview.gui.AlignFrame alFrame,
321 String wsname, String title, AlignmentView _msa, boolean subgaps,
322 boolean presorder, Alignment seqset)
324 this(server, wsUrl, wsinfo, alFrame, _msa, wsname, subgaps, presorder);
325 OutputHeader = wsInfo.getProgressText();
329 SequenceI[][] conmsa = _msa.getVisibleContigs('-');
332 int njobs = conmsa.length;
333 jobs = new MsaWSJob[njobs];
334 for (int j = 0; j < njobs; j++)
338 jobs[j] = new MsaWSJob(wsinfo.addJobPane(), conmsa[j]);
342 jobs[j] = new MsaWSJob(0, conmsa[j]);
346 wsinfo.setProgressName("region " + jobs[j].getJobnum(),
347 jobs[j].getJobnum());
349 wsinfo.setProgressText(jobs[j].getJobnum(), OutputHeader);
354 public boolean isCancellable()
359 public void cancelJob()
361 if (!jobComplete && jobs != null)
363 boolean cancelled = true;
364 for (int job = 0; job < jobs.length; job++)
366 if (jobs[job].isSubmitted() && !jobs[job].isSubjobComplete())
368 String cancelledMessage = "";
371 vamsas.objects.simple.WsJobId cancelledJob = server
372 .cancel(jobs[job].getJobId());
373 if (cancelledJob.getStatus() == 2)
376 cancelledMessage = "Job cancelled.";
377 ((MsaWSJob) jobs[job]).cancel();
378 wsInfo.setStatus(jobs[job].getJobnum(),
379 WebserviceInfo.STATE_CANCELLED_OK);
381 else if (cancelledJob.getStatus() == 3)
383 // VALID UNSTOPPABLE JOB
384 cancelledMessage += "Server cannot cancel this job. just close the window.\n";
386 // wsInfo.setStatus(jobs[job].jobnum,
387 // WebserviceInfo.STATE_RUNNING);
390 if (cancelledJob.getJobId() != null)
392 cancelledMessage += ("[" + cancelledJob.getJobId() + "]");
395 cancelledMessage += "\n";
396 } catch (Exception exc)
398 cancelledMessage += ("\nProblems cancelling the job : Exception received...\n"
401 "Exception whilst cancelling " + jobs[job].getJobId(),
404 wsInfo.setProgressText(jobs[job].getJobnum(), OutputHeader
405 + cancelledMessage + "\n");
410 wsInfo.setStatus(WebserviceInfo.STATE_CANCELLED_OK);
413 this.interrupt(); // kick thread to update job states.
419 wsInfo.setProgressText(OutputHeader
420 + "Server cannot cancel this job because it has not been submitted properly. just close the window.\n");
425 public void pollJob(AWsJob job) throws Exception
427 ((MsaWSJob) job).result = server.getResult(((MsaWSJob) job).getJobId());
430 public void StartJob(AWsJob job)
432 if (!(job instanceof MsaWSJob))
434 throw new Error("StartJob(MsaWSJob) called on a WSJobInstance "
437 MsaWSJob j = (MsaWSJob) job;
440 if (Cache.log.isDebugEnabled())
442 Cache.log.debug("Tried to submit an already submitted job "
447 if (j.seqs.getSeqs() == null)
449 // special case - selection consisted entirely of empty sequences...
450 j.setSubmitted(true);
451 j.result = new MsaResult();
452 j.result.setFinished(true);
453 j.result.setStatus("Empty Alignment Job");
454 ((MsaResult) j.result).setMsa(null);
458 vamsas.objects.simple.WsJobId jobsubmit = server.align(j.seqs);
460 if ((jobsubmit != null) && (jobsubmit.getStatus() == 1))
462 j.setJobId(jobsubmit.getJobId());
463 j.setSubmitted(true);
464 j.setSubjobComplete(false);
465 // System.out.println(WsURL + " Job Id '" + jobId + "'");
469 if (jobsubmit == null)
474 + " returned null object, it probably cannot be contacted. Try again later ?");
477 throw new Exception(jobsubmit.getJobId());
479 } catch (Exception e)
481 // TODO: JBPNote catch timeout or other fault types explicitly
482 // For unexpected errors
484 .println(WebServiceName
485 + "Client: Failed to submit the sequences for alignment (probably a server side problem)\n"
486 + "When contacting Server:" + WsUrl + "\n"
487 + e.toString() + "\n");
488 j.setAllowedServerExceptions(0);
489 wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_SERVERERROR);
490 wsInfo.setStatus(j.getJobnum(),
491 WebserviceInfo.STATE_STOPPED_SERVERERROR);
492 wsInfo.appendProgressText(
494 "Failed to submit sequences for alignment.\n"
495 + "It is most likely that there is a problem with the server.\n"
496 + "Just close the window\n");
498 // e.printStackTrace(); // TODO: JBPNote DEBUG
502 private jalview.datamodel.Sequence[] getVamsasAlignment(
503 vamsas.objects.simple.Alignment valign)
505 // TODO: refactor to helper class for vamsas.objects.simple objects
506 vamsas.objects.simple.Sequence[] seqs = valign.getSeqs().getSeqs();
507 jalview.datamodel.Sequence[] msa = new jalview.datamodel.Sequence[seqs.length];
509 for (int i = 0, j = seqs.length; i < j; i++)
511 msa[i] = new jalview.datamodel.Sequence(seqs[i].getId(),
518 public void parseResult()
520 int results = 0; // number of result sets received
521 JobStateSummary finalState = new JobStateSummary();
524 for (int j = 0; j < jobs.length; j++)
526 finalState.updateJobPanelState(wsInfo, OutputHeader, jobs[j]);
527 if (jobs[j].isSubmitted() && jobs[j].isSubjobComplete()
528 && jobs[j].hasResults())
531 // if (Cache.log.isDebugEnabled())
533 // System.out.println("Job lob for job "+jobs[j].getJobId()+":"+jobs[j].getJobnum());
534 // System.out.println(jobs[j].getStatus());
537 vamsas.objects.simple.Alignment valign = ((MsaResult) ((MsaWSJob) jobs[j]).result)
541 wsInfo.appendProgressText(jobs[j].getJobnum(),
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].getJobnum(), lines[line]
549 // JBPNote The returned files from a webservice could be
550 // hidden behind icons in the monitor window that,
551 // when clicked, pop up their corresponding data
556 } catch (Exception ex)
559 Cache.log.error("Unexpected exception when processing results for "
561 wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_ERROR);
565 wsInfo.showResultsNewFrame
566 .addActionListener(new java.awt.event.ActionListener()
568 public void actionPerformed(java.awt.event.ActionEvent evt)
570 displayResults(true);
574 .addActionListener(new java.awt.event.ActionListener()
576 public void actionPerformed(java.awt.event.ActionEvent evt)
578 displayResults(false);
581 wsInfo.setResultsReady();
585 wsInfo.setFinishedNoResults();
589 void displayResults(boolean newFrame)
591 // view input or result data for each block
592 Vector alorders = new Vector();
593 SequenceI[][] results = new SequenceI[jobs.length][];
594 AlignmentOrder[] orders = new AlignmentOrder[jobs.length];
595 for (int j = 0; j < jobs.length; j++)
597 if (jobs[j].hasResults())
599 Object[] res = ((MsaWSJob) jobs[j]).getAlignment();
600 alorders.add(res[1]);
601 results[j] = (SequenceI[]) res[0];
602 orders[j] = (AlignmentOrder) res[1];
604 // SequenceI[] alignment = input.getUpdated
611 Object[] newview = input.getUpdatedView(results, orders, getGapChar());
612 // trash references to original result data
613 for (int j = 0; j < jobs.length; j++)
618 SequenceI[] alignment = (SequenceI[]) newview[0];
619 ColumnSelection columnselection = (ColumnSelection) newview[1];
620 Alignment al = new Alignment(alignment);
621 // TODO: add 'provenance' property to alignment from the method notes
622 // accompanying each subjob
625 al.setDataset(dataset);
628 propagateDatasetMappings(al);
629 // JBNote- TODO: warn user if a block is input rather than aligned data ?
633 AlignFrame af = new AlignFrame(al, columnselection,
634 AlignFrame.DEFAULT_WIDTH, AlignFrame.DEFAULT_HEIGHT);
636 // initialise with same renderer settings as in parent alignframe.
637 af.getFeatureRenderer().transferSettings(this.featureSettings);
639 if (alorders.size() > 0)
641 if (alorders.size() == 1)
643 af.addSortByOrderMenuItem(WebServiceName + " Ordering",
644 (AlignmentOrder) alorders.get(0));
648 // construct a non-redundant ordering set
649 Vector names = new Vector();
650 for (int i = 0, l = alorders.size(); i < l; i++)
652 String orderName = new String(" Region " + i);
657 if (((AlignmentOrder) alorders.get(i))
658 .equals(((AlignmentOrder) alorders.get(j))))
662 orderName += "," + j;
670 if (i == 0 && j == 1)
672 names.add(new String(""));
676 names.add(orderName);
679 for (int i = 0, l = alorders.size(); i < l; i++)
681 af.addSortByOrderMenuItem(
682 WebServiceName + ((String) names.get(i)) + " Ordering",
683 (AlignmentOrder) alorders.get(i));
688 Desktop.addInternalFrame(af, alTitle, AlignFrame.DEFAULT_WIDTH,
689 AlignFrame.DEFAULT_HEIGHT);
694 System.out.println("MERGE WITH OLD FRAME");
695 // TODO: modify alignment in original frame, replacing old for new
696 // alignment using the commands.EditCommand model to ensure the update can
701 public boolean canMergeResults()