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.util.MessageManager;
30 import jalview.ws.AWsJob;
31 import jalview.ws.JobStateSummary;
32 import jalview.ws.WSClientI;
33 import vamsas.objects.simple.MsaResult;
35 class MsaWSThread extends JWS1Thread implements WSClientI
37 boolean submitGaps = false; // pass sequences including gaps to alignment
41 boolean preserveOrder = true; // and always store and recover sequence
45 class MsaWSJob extends WSJob
47 // hold special input for this
48 vamsas.objects.simple.SequenceSet seqs = new vamsas.objects.simple.SequenceSet();
58 public MsaWSJob(int jobNum, SequenceI[] inSeqs)
61 if (!prepareInput(inSeqs, 2))
64 subjobComplete = true;
65 result = new MsaResult();
66 result.setFinished(true);
67 result.setStatus(MessageManager.getString("label.job_never_ran"));
72 Hashtable SeqNames = new Hashtable();
74 Vector emptySeqs = new Vector();
77 * prepare input sequences for MsaWS service
80 * jalview sequences to be prepared
82 * minimum number of residues required for this MsaWS service
83 * @return true if seqs contains sequences to be submitted to service.
85 private boolean prepareInput(SequenceI[] seqs, int minlen)
90 throw new Error(MessageManager.getString("error.implementation_error_minlen_must_be_greater_zero"));
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(MessageManager.formatMessage("error.implementation_error_msawbjob_called", new String[]{job.getClass().toString()}));
436 MsaWSJob j = (MsaWSJob) job;
439 if (Cache.log.isDebugEnabled())
441 Cache.log.debug("Tried to submit an already submitted job "
446 if (j.seqs.getSeqs() == null)
448 // special case - selection consisted entirely of empty sequences...
449 j.setSubmitted(true);
450 j.result = new MsaResult();
451 j.result.setFinished(true);
452 j.result.setStatus(MessageManager.getString("label.empty_alignment_job"));
453 ((MsaResult) j.result).setMsa(null);
457 vamsas.objects.simple.WsJobId jobsubmit = server.align(j.seqs);
459 if ((jobsubmit != null) && (jobsubmit.getStatus() == 1))
461 j.setJobId(jobsubmit.getJobId());
462 j.setSubmitted(true);
463 j.setSubjobComplete(false);
464 // System.out.println(WsURL + " Job Id '" + jobId + "'");
468 if (jobsubmit == null)
470 throw new Exception(MessageManager.formatMessage("exception.web_service_returned_null_try_later", new String[]{WsUrl}));
473 throw new Exception(jobsubmit.getJobId());
475 } catch (Exception e)
477 // TODO: JBPNote catch timeout or other fault types explicitly
478 // For unexpected errors
480 .println(WebServiceName
481 + "Client: Failed to submit the sequences for alignment (probably a server side problem)\n"
482 + "When contacting Server:" + WsUrl + "\n"
483 + e.toString() + "\n");
484 j.setAllowedServerExceptions(0);
485 wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_SERVERERROR);
486 wsInfo.setStatus(j.getJobnum(),
487 WebserviceInfo.STATE_STOPPED_SERVERERROR);
488 wsInfo.appendProgressText(
490 MessageManager.getString("info.failed_to_submit_sequences_for_alignment"));
492 // e.printStackTrace(); // TODO: JBPNote DEBUG
496 private jalview.datamodel.Sequence[] getVamsasAlignment(
497 vamsas.objects.simple.Alignment valign)
499 // TODO: refactor to helper class for vamsas.objects.simple objects
500 vamsas.objects.simple.Sequence[] seqs = valign.getSeqs().getSeqs();
501 jalview.datamodel.Sequence[] msa = new jalview.datamodel.Sequence[seqs.length];
503 for (int i = 0, j = seqs.length; i < j; i++)
505 msa[i] = new jalview.datamodel.Sequence(seqs[i].getId(),
512 public void parseResult()
514 int results = 0; // number of result sets received
515 JobStateSummary finalState = new JobStateSummary();
518 for (int j = 0; j < jobs.length; j++)
520 finalState.updateJobPanelState(wsInfo, OutputHeader, jobs[j]);
521 if (jobs[j].isSubmitted() && jobs[j].isSubjobComplete()
522 && jobs[j].hasResults())
525 // if (Cache.log.isDebugEnabled())
527 // System.out.println("Job lob for job "+jobs[j].getJobId()+":"+jobs[j].getJobnum());
528 // System.out.println(jobs[j].getStatus());
531 vamsas.objects.simple.Alignment valign = ((MsaResult) ((MsaWSJob) jobs[j]).result)
535 wsInfo.appendProgressText(jobs[j].getJobnum(),
536 MessageManager.getString("info.alignment_object_method_notes"));
537 String[] lines = valign.getMethod();
538 for (int line = 0; line < lines.length; line++)
540 wsInfo.appendProgressText(jobs[j].getJobnum(), lines[line]
543 // JBPNote The returned files from a webservice could be
544 // hidden behind icons in the monitor window that,
545 // when clicked, pop up their corresponding data
550 } catch (Exception ex)
553 Cache.log.error("Unexpected exception when processing results for "
555 wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_ERROR);
559 wsInfo.showResultsNewFrame
560 .addActionListener(new java.awt.event.ActionListener()
562 public void actionPerformed(java.awt.event.ActionEvent evt)
564 displayResults(true);
568 .addActionListener(new java.awt.event.ActionListener()
570 public void actionPerformed(java.awt.event.ActionEvent evt)
572 displayResults(false);
575 wsInfo.setResultsReady();
579 wsInfo.setFinishedNoResults();
583 void displayResults(boolean newFrame)
585 // view input or result data for each block
586 Vector alorders = new Vector();
587 SequenceI[][] results = new SequenceI[jobs.length][];
588 AlignmentOrder[] orders = new AlignmentOrder[jobs.length];
589 for (int j = 0; j < jobs.length; j++)
591 if (jobs[j].hasResults())
593 Object[] res = ((MsaWSJob) jobs[j]).getAlignment();
594 alorders.add(res[1]);
595 results[j] = (SequenceI[]) res[0];
596 orders[j] = (AlignmentOrder) res[1];
598 // SequenceI[] alignment = input.getUpdated
605 Object[] newview = input.getUpdatedView(results, orders, getGapChar());
606 // trash references to original result data
607 for (int j = 0; j < jobs.length; j++)
612 SequenceI[] alignment = (SequenceI[]) newview[0];
613 ColumnSelection columnselection = (ColumnSelection) newview[1];
614 Alignment al = new Alignment(alignment);
615 // TODO: add 'provenance' property to alignment from the method notes
616 // accompanying each subjob
619 al.setDataset(dataset);
622 propagateDatasetMappings(al);
623 // JBNote- TODO: warn user if a block is input rather than aligned data ?
627 AlignFrame af = new AlignFrame(al, columnselection,
628 AlignFrame.DEFAULT_WIDTH, AlignFrame.DEFAULT_HEIGHT);
630 // initialise with same renderer settings as in parent alignframe.
631 af.getFeatureRenderer().transferSettings(this.featureSettings);
633 if (alorders.size() > 0)
635 if (alorders.size() == 1)
637 af.addSortByOrderMenuItem(WebServiceName + " Ordering",
638 (AlignmentOrder) alorders.get(0));
642 // construct a non-redundant ordering set
643 Vector names = new Vector();
644 for (int i = 0, l = alorders.size(); i < l; i++)
646 String orderName = new String(" Region " + i);
651 if (((AlignmentOrder) alorders.get(i))
652 .equals(((AlignmentOrder) alorders.get(j))))
656 orderName += "," + j;
664 if (i == 0 && j == 1)
666 names.add(new String(""));
670 names.add(orderName);
673 for (int i = 0, l = alorders.size(); i < l; i++)
675 af.addSortByOrderMenuItem(
676 WebServiceName + ((String) names.get(i)) + " Ordering",
677 (AlignmentOrder) alorders.get(i));
682 Desktop.addInternalFrame(af, alTitle, AlignFrame.DEFAULT_WIDTH,
683 AlignFrame.DEFAULT_HEIGHT);
688 System.out.println("MERGE WITH OLD FRAME");
689 // TODO: modify alignment in original frame, replacing old for new
690 // alignment using the commands.EditCommand model to ensure the update can
695 public boolean canMergeResults()