2 * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3 * Copyright (C) $$Year-Rel$$ 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;
23 import jalview.analysis.AlignSeq;
24 import jalview.analysis.AlignmentSorter;
25 import jalview.analysis.SeqsetUtils;
26 import jalview.bin.Cache;
27 import jalview.datamodel.Alignment;
28 import jalview.datamodel.AlignmentOrder;
29 import jalview.datamodel.AlignmentView;
30 import jalview.datamodel.ColumnSelection;
31 import jalview.datamodel.Sequence;
32 import jalview.datamodel.SequenceI;
33 import jalview.gui.AlignFrame;
34 import jalview.gui.Desktop;
35 import jalview.gui.WebserviceInfo;
36 import jalview.util.Comparison;
37 import jalview.util.MessageManager;
38 import jalview.ws.AWsJob;
39 import jalview.ws.JobStateSummary;
40 import jalview.ws.WSClientI;
42 import java.util.Hashtable;
43 import java.util.Vector;
45 import vamsas.objects.simple.MsaResult;
47 class MsaWSThread extends JWS1Thread implements WSClientI
49 boolean submitGaps = false; // pass sequences including gaps to alignment
53 boolean preserveOrder = true; // and always store and recover sequence
57 class MsaWSJob extends WSJob
59 // hold special input for this
60 vamsas.objects.simple.SequenceSet seqs = new vamsas.objects.simple.SequenceSet();
70 public MsaWSJob(int jobNum, SequenceI[] inSeqs)
73 if (!prepareInput(inSeqs, 2))
76 subjobComplete = true;
77 result = new MsaResult();
78 result.setFinished(true);
79 result.setStatus(MessageManager.getString("label.job_never_ran"));
84 Hashtable SeqNames = new Hashtable();
86 Vector emptySeqs = new Vector();
89 * prepare input sequences for MsaWS service
92 * jalview sequences to be prepared
94 * minimum number of residues required for this MsaWS service
95 * @return true if seqs contains sequences to be submitted to service.
97 private boolean prepareInput(SequenceI[] seqs, int minlen)
102 throw new Error(MessageManager.getString("error.implementation_error_minlen_must_be_greater_zero"));
104 for (int i = 0; i < seqs.length; i++)
106 if (seqs[i].getEnd() - seqs[i].getStart() > minlen - 1)
111 boolean valid = nseqs > 1; // need at least two seqs
112 vamsas.objects.simple.Sequence[] seqarray = (valid) ? new vamsas.objects.simple.Sequence[nseqs]
114 for (int i = 0, n = 0; i < seqs.length; i++)
117 String newname = SeqsetUtils.unique_name(i); // same
121 SeqNames.put(newname, SeqsetUtils.SeqCharacterHash(seqs[i]));
122 if (valid && seqs[i].getEnd() - seqs[i].getStart() > minlen - 1)
124 seqarray[n] = new vamsas.objects.simple.Sequence();
125 seqarray[n].setId(newname);
126 seqarray[n++].setSeq((submitGaps) ? seqs[i].getSequenceAsString()
127 : AlignSeq.extractGaps(Comparison.GapChars,
128 seqs[i].getSequenceAsString()));
133 if (seqs[i].getEnd() >= seqs[i].getStart())
135 empty = (submitGaps) ? seqs[i].getSequenceAsString() : AlignSeq
136 .extractGaps(Comparison.GapChars,
137 seqs[i].getSequenceAsString());
139 emptySeqs.add(new String[]
143 this.seqs = new vamsas.objects.simple.SequenceSet();
144 this.seqs.setSeqs(seqarray);
150 * @return true if getAlignment will return a valid alignment result.
152 public boolean hasResults()
154 if (subjobComplete && result != null && result.isFinished()
155 && ((MsaResult) result).getMsa() != null
156 && ((MsaResult) result).getMsa().getSeqs() != null)
163 public Object[] getAlignment()
166 if (result != null && result.isFinished())
168 SequenceI[] alseqs = null;
169 char alseq_gapchar = '-';
171 if (((MsaResult) result).getMsa() != null)
173 alseqs = getVamsasAlignment(((MsaResult) result).getMsa());
174 alseq_gapchar = ((MsaResult) result).getMsa().getGapchar()
176 alseq_l = alseqs.length;
178 if (emptySeqs.size() > 0)
180 SequenceI[] t_alseqs = new SequenceI[alseq_l + emptySeqs.size()];
185 for (i = 0, w = alseqs[0].getLength(); i < alseq_l; i++)
187 if (w < alseqs[i].getLength())
189 w = alseqs[i].getLength();
191 t_alseqs[i] = alseqs[i];
195 // check that aligned width is at least as wide as emptySeqs width.
197 for (i = 0, w = emptySeqs.size(); i < w; i++)
199 String[] es = (String[]) emptySeqs.get(i);
200 if (es != null && es[1] != null)
202 int sw = es[1].length();
209 // make a gapped string.
210 StringBuffer insbuff = new StringBuffer(w);
211 for (i = 0; i < nw; i++)
213 insbuff.append(alseq_gapchar);
217 for (i = 0; i < alseq_l; i++)
219 int sw = t_alseqs[i].getLength();
223 alseqs[i].setSequence(t_alseqs[i].getSequenceAsString()
224 + insbuff.substring(0, sw - nw));
228 for (i = 0, w = emptySeqs.size(); i < w; i++)
230 String[] es = (String[]) emptySeqs.get(i);
233 t_alseqs[i + alseq_l] = new Sequence(es[0],
234 insbuff.toString(), 1, 0);
238 if (es[1].length() < nw)
240 t_alseqs[i + alseq_l] = new Sequence(
242 es[1] + insbuff.substring(0, nw - es[1].length()),
243 1, 1 + es[1].length());
247 t_alseqs[i + alseq_l] = new Sequence(
254 AlignmentOrder msaorder = new AlignmentOrder(alseqs);
255 // always recover the order - makes parseResult()'s life easier.
256 AlignmentSorter.recoverOrder(alseqs);
257 // account for any missing sequences
258 SeqsetUtils.deuniquify(SeqNames, alseqs);
260 { alseqs, msaorder };
266 * mark subjob as cancelled and set result object appropriatly
271 subjobComplete = true;
277 * @return boolean true if job can be submitted.
279 public boolean hasValidInput()
281 if (seqs.getSeqs() != null)
289 String alTitle; // name which will be used to form new alignment window.
291 Alignment dataset; // dataset to which the new alignment will be
295 ext.vamsas.MuscleWS server = null;
298 * set basic options for this (group) of Msa jobs
305 MsaWSThread(ext.vamsas.MuscleWS server, String wsUrl,
306 WebserviceInfo wsinfo, AlignFrame alFrame,
307 AlignmentView alview, String wsname, boolean subgaps,
310 super(alFrame, wsinfo, alview, wsname, wsUrl);
311 this.server = server;
312 this.submitGaps = subgaps;
313 this.preserveOrder = presorder;
317 * create one or more Msa jobs to align visible seuqences in _msa
330 MsaWSThread(ext.vamsas.MuscleWS server, String wsUrl,
331 WebserviceInfo wsinfo, AlignFrame alFrame,
332 String wsname, String title, AlignmentView _msa, boolean subgaps,
333 boolean presorder, Alignment seqset)
335 this(server, wsUrl, wsinfo, alFrame, _msa, wsname, subgaps, presorder);
336 OutputHeader = wsInfo.getProgressText();
340 SequenceI[][] conmsa = _msa.getVisibleContigs('-');
343 int njobs = conmsa.length;
344 jobs = new MsaWSJob[njobs];
345 for (int j = 0; j < njobs; j++)
349 jobs[j] = new MsaWSJob(wsinfo.addJobPane(), conmsa[j]);
353 jobs[j] = new MsaWSJob(0, conmsa[j]);
357 wsinfo.setProgressName("region " + jobs[j].getJobnum(),
358 jobs[j].getJobnum());
360 wsinfo.setProgressText(jobs[j].getJobnum(), OutputHeader);
365 public boolean isCancellable()
370 public void cancelJob()
372 if (!jobComplete && jobs != null)
374 boolean cancelled = true;
375 for (int job = 0; job < jobs.length; job++)
377 if (jobs[job].isSubmitted() && !jobs[job].isSubjobComplete())
379 String cancelledMessage = "";
382 vamsas.objects.simple.WsJobId cancelledJob = server
383 .cancel(jobs[job].getJobId());
384 if (cancelledJob.getStatus() == 2)
387 cancelledMessage = "Job cancelled.";
388 ((MsaWSJob) jobs[job]).cancel();
389 wsInfo.setStatus(jobs[job].getJobnum(),
390 WebserviceInfo.STATE_CANCELLED_OK);
392 else if (cancelledJob.getStatus() == 3)
394 // VALID UNSTOPPABLE JOB
395 cancelledMessage += "Server cannot cancel this job. just close the window.\n";
397 // wsInfo.setStatus(jobs[job].jobnum,
398 // WebserviceInfo.STATE_RUNNING);
401 if (cancelledJob.getJobId() != null)
403 cancelledMessage += ("[" + cancelledJob.getJobId() + "]");
406 cancelledMessage += "\n";
407 } catch (Exception exc)
409 cancelledMessage += ("\nProblems cancelling the job : Exception received...\n"
412 "Exception whilst cancelling " + jobs[job].getJobId(),
415 wsInfo.setProgressText(jobs[job].getJobnum(), OutputHeader
416 + cancelledMessage + "\n");
421 wsInfo.setStatus(WebserviceInfo.STATE_CANCELLED_OK);
424 this.interrupt(); // kick thread to update job states.
430 wsInfo.setProgressText(OutputHeader
431 + "Server cannot cancel this job because it has not been submitted properly. just close the window.\n");
436 public void pollJob(AWsJob job) throws Exception
438 ((MsaWSJob) job).result = server.getResult(((MsaWSJob) job).getJobId());
441 public void StartJob(AWsJob job)
443 if (!(job instanceof MsaWSJob))
445 throw new Error(MessageManager.formatMessage("error.implementation_error_msawbjob_called", new String[]{job.getClass().toString()}));
447 MsaWSJob j = (MsaWSJob) job;
450 if (Cache.log.isDebugEnabled())
452 Cache.log.debug("Tried to submit an already submitted job "
457 if (j.seqs.getSeqs() == null)
459 // special case - selection consisted entirely of empty sequences...
460 j.setSubmitted(true);
461 j.result = new MsaResult();
462 j.result.setFinished(true);
463 j.result.setStatus(MessageManager.getString("label.empty_alignment_job"));
464 ((MsaResult) j.result).setMsa(null);
468 vamsas.objects.simple.WsJobId jobsubmit = server.align(j.seqs);
470 if ((jobsubmit != null) && (jobsubmit.getStatus() == 1))
472 j.setJobId(jobsubmit.getJobId());
473 j.setSubmitted(true);
474 j.setSubjobComplete(false);
475 // System.out.println(WsURL + " Job Id '" + jobId + "'");
479 if (jobsubmit == null)
481 throw new Exception(MessageManager.formatMessage("exception.web_service_returned_null_try_later", new String[]{WsUrl}));
484 throw new Exception(jobsubmit.getJobId());
486 } catch (Exception e)
488 // TODO: JBPNote catch timeout or other fault types explicitly
489 // For unexpected errors
491 .println(WebServiceName
492 + "Client: Failed to submit the sequences for alignment (probably a server side problem)\n"
493 + "When contacting Server:" + WsUrl + "\n"
494 + e.toString() + "\n");
495 j.setAllowedServerExceptions(0);
496 wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_SERVERERROR);
497 wsInfo.setStatus(j.getJobnum(),
498 WebserviceInfo.STATE_STOPPED_SERVERERROR);
499 wsInfo.appendProgressText(
501 MessageManager.getString("info.failed_to_submit_sequences_for_alignment"));
503 // e.printStackTrace(); // TODO: JBPNote DEBUG
507 private Sequence[] getVamsasAlignment(
508 vamsas.objects.simple.Alignment valign)
510 // TODO: refactor to helper class for vamsas.objects.simple objects
511 vamsas.objects.simple.Sequence[] seqs = valign.getSeqs().getSeqs();
512 Sequence[] msa = new Sequence[seqs.length];
514 for (int i = 0, j = seqs.length; i < j; i++)
516 msa[i] = new Sequence(seqs[i].getId(),
523 public void parseResult()
525 int results = 0; // number of result sets received
526 JobStateSummary finalState = new JobStateSummary();
529 for (int j = 0; j < jobs.length; j++)
531 finalState.updateJobPanelState(wsInfo, OutputHeader, jobs[j]);
532 if (jobs[j].isSubmitted() && jobs[j].isSubjobComplete()
533 && jobs[j].hasResults())
536 // if (Cache.log.isDebugEnabled())
538 // System.out.println("Job lob for job "+jobs[j].getJobId()+":"+jobs[j].getJobnum());
539 // System.out.println(jobs[j].getStatus());
542 vamsas.objects.simple.Alignment valign = ((MsaResult) ((MsaWSJob) jobs[j]).result)
546 wsInfo.appendProgressText(jobs[j].getJobnum(),
547 MessageManager.getString("info.alignment_object_method_notes"));
548 String[] lines = valign.getMethod();
549 for (int line = 0; line < lines.length; line++)
551 wsInfo.appendProgressText(jobs[j].getJobnum(), lines[line]
554 // JBPNote The returned files from a webservice could be
555 // hidden behind icons in the monitor window that,
556 // when clicked, pop up their corresponding data
561 } catch (Exception ex)
564 Cache.log.error("Unexpected exception when processing results for "
566 wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_ERROR);
570 wsInfo.showResultsNewFrame
571 .addActionListener(new java.awt.event.ActionListener()
573 public void actionPerformed(java.awt.event.ActionEvent evt)
575 displayResults(true);
579 .addActionListener(new java.awt.event.ActionListener()
581 public void actionPerformed(java.awt.event.ActionEvent evt)
583 displayResults(false);
586 wsInfo.setResultsReady();
590 wsInfo.setFinishedNoResults();
594 void displayResults(boolean newFrame)
596 // view input or result data for each block
597 Vector alorders = new Vector();
598 SequenceI[][] results = new SequenceI[jobs.length][];
599 AlignmentOrder[] orders = new AlignmentOrder[jobs.length];
600 for (int j = 0; j < jobs.length; j++)
602 if (jobs[j].hasResults())
604 Object[] res = ((MsaWSJob) jobs[j]).getAlignment();
605 alorders.add(res[1]);
606 results[j] = (SequenceI[]) res[0];
607 orders[j] = (AlignmentOrder) res[1];
609 // SequenceI[] alignment = input.getUpdated
616 Object[] newview = input.getUpdatedView(results, orders, getGapChar());
617 // trash references to original result data
618 for (int j = 0; j < jobs.length; j++)
623 SequenceI[] alignment = (SequenceI[]) newview[0];
624 ColumnSelection columnselection = (ColumnSelection) newview[1];
625 Alignment al = new Alignment(alignment);
626 // TODO: add 'provenance' property to alignment from the method notes
627 // accompanying each subjob
630 al.setDataset(dataset);
633 propagateDatasetMappings(al);
634 // JBNote- TODO: warn user if a block is input rather than aligned data ?
638 AlignFrame af = new AlignFrame(al, columnselection,
639 AlignFrame.DEFAULT_WIDTH, AlignFrame.DEFAULT_HEIGHT);
641 // initialise with same renderer settings as in parent alignframe.
642 af.getFeatureRenderer().transferSettings(this.featureSettings);
644 if (alorders.size() > 0)
646 if (alorders.size() == 1)
648 af.addSortByOrderMenuItem(WebServiceName + " Ordering",
649 (AlignmentOrder) alorders.get(0));
653 // construct a non-redundant ordering set
654 Vector names = new Vector();
655 for (int i = 0, l = alorders.size(); i < l; i++)
657 String orderName = new String(" Region " + i);
662 if (((AlignmentOrder) alorders.get(i))
663 .equals((alorders.get(j))))
667 orderName += "," + j;
675 if (i == 0 && j == 1)
677 names.add(new String(""));
681 names.add(orderName);
684 for (int i = 0, l = alorders.size(); i < l; i++)
686 af.addSortByOrderMenuItem(
687 WebServiceName + ((String) names.get(i)) + " Ordering",
688 (AlignmentOrder) alorders.get(i));
693 Desktop.addInternalFrame(af, alTitle, AlignFrame.DEFAULT_WIDTH,
694 AlignFrame.DEFAULT_HEIGHT);
699 System.out.println("MERGE WITH OLD FRAME");
700 // TODO: modify alignment in original frame, replacing old for new
701 // alignment using the commands.EditCommand model to ensure the update can
706 public boolean canMergeResults()