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.bin.Console;
25 import jalview.datamodel.Alignment;
26 import jalview.datamodel.AlignmentI;
27 import jalview.datamodel.AlignmentOrder;
28 import jalview.datamodel.AlignmentView;
29 import jalview.datamodel.HiddenColumns;
30 import jalview.datamodel.SequenceI;
31 import jalview.gui.AlignFrame;
32 import jalview.gui.Desktop;
33 import jalview.gui.WebserviceInfo;
34 import jalview.util.MessageManager;
35 import jalview.ws.AWsJob;
36 import jalview.ws.JobStateSummary;
37 import jalview.ws.WSClientI;
39 import java.util.Hashtable;
40 import java.util.Vector;
42 import vamsas.objects.simple.MsaResult;
44 class MsaWSThread extends JWS1Thread implements WSClientI
46 boolean submitGaps = false; // pass sequences including gaps to alignment
50 boolean preserveOrder = true; // and always store and recover sequence
54 class MsaWSJob extends WSJob
56 // hold special input for this
57 vamsas.objects.simple.SequenceSet seqs = new vamsas.objects.simple.SequenceSet();
67 public MsaWSJob(int jobNum, SequenceI[] inSeqs)
70 if (!prepareInput(inSeqs, 2))
73 subjobComplete = true;
74 result = new MsaResult();
75 result.setFinished(true);
76 result.setStatus(MessageManager.getString("label.job_never_ran"));
81 Hashtable SeqNames = new Hashtable();
83 Vector emptySeqs = new Vector();
86 * prepare input sequences for MsaWS service
89 * jalview sequences to be prepared
91 * minimum number of residues required for this MsaWS service
92 * @return true if seqs contains sequences to be submitted to service.
94 private boolean prepareInput(SequenceI[] seqs, int minlen)
99 throw new Error(MessageManager.getString(
100 "error.implementation_error_minlen_must_be_greater_zero"));
102 for (int i = 0; i < seqs.length; i++)
104 if (seqs[i].getEnd() - seqs[i].getStart() > minlen - 1)
109 boolean valid = nseqs > 1; // need at least two seqs
110 vamsas.objects.simple.Sequence[] seqarray = (valid)
111 ? new vamsas.objects.simple.Sequence[nseqs]
113 for (int i = 0, n = 0; i < seqs.length; i++)
116 String newname = jalview.analysis.SeqsetUtils.unique_name(i); // same
120 SeqNames.put(newname,
121 jalview.analysis.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(jalview.util.Comparison.GapChars,
128 seqs[i].getSequenceAsString()));
133 if (seqs[i].getEnd() >= seqs[i].getStart())
135 empty = (submitGaps) ? seqs[i].getSequenceAsString()
136 : AlignSeq.extractGaps(jalview.util.Comparison.GapChars,
137 seqs[i].getSequenceAsString());
139 emptySeqs.add(new String[] { newname, empty });
142 this.seqs = new vamsas.objects.simple.SequenceSet();
143 this.seqs.setSeqs(seqarray);
149 * @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 jalview.datamodel.Sequence(es[0],
234 insbuff.toString(), 1, 0);
238 if (es[1].length() < nw)
240 t_alseqs[i + alseq_l] = new jalview.datamodel.Sequence(
242 es[1] + insbuff.substring(0, nw - es[1].length()),
243 1, 1 + es[1].length());
247 t_alseqs[i + alseq_l] = new jalview.datamodel.Sequence(
254 AlignmentOrder msaorder = new AlignmentOrder(alseqs);
255 // always recover the order - makes parseResult()'s life easier.
256 jalview.analysis.AlignmentSorter.recoverOrder(alseqs);
257 // account for any missing sequences
258 jalview.analysis.SeqsetUtils.deuniquify(SeqNames, alseqs);
259 return new Object[] { alseqs, msaorder };
265 * mark subjob as cancelled and set result object appropriatly
270 subjobComplete = true;
276 * @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 AlignmentI 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, jalview.gui.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, jalview.gui.AlignFrame alFrame,
332 String wsname, String title, AlignmentView _msa, boolean subgaps,
333 boolean presorder, AlignmentI 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);
366 public boolean isCancellable()
372 public void cancelJob()
374 if (!jobComplete && jobs != null)
376 boolean cancelled = true;
377 for (int job = 0; job < jobs.length; job++)
379 if (jobs[job].isSubmitted() && !jobs[job].isSubjobComplete())
381 String cancelledMessage = "";
384 vamsas.objects.simple.WsJobId cancelledJob = server
385 .cancel(jobs[job].getJobId());
386 if (cancelledJob.getStatus() == 2)
389 cancelledMessage = "Job cancelled.";
390 ((MsaWSJob) jobs[job]).cancel();
391 wsInfo.setStatus(jobs[job].getJobnum(),
392 WebserviceInfo.STATE_CANCELLED_OK);
394 else if (cancelledJob.getStatus() == 3)
396 // VALID UNSTOPPABLE JOB
397 cancelledMessage += "Server cannot cancel this job. just close the window.\n";
399 // wsInfo.setStatus(jobs[job].jobnum,
400 // WebserviceInfo.STATE_RUNNING);
403 if (cancelledJob.getJobId() != null)
405 cancelledMessage += ("[" + cancelledJob.getJobId() + "]");
408 cancelledMessage += "\n";
409 } catch (Exception exc)
411 cancelledMessage += ("\nProblems cancelling the job : Exception received...\n"
414 "Exception whilst cancelling " + jobs[job].getJobId(),
417 wsInfo.setProgressText(jobs[job].getJobnum(),
418 OutputHeader + cancelledMessage + "\n");
423 wsInfo.setStatus(WebserviceInfo.STATE_CANCELLED_OK);
426 this.interrupt(); // kick thread to update job states.
432 wsInfo.setProgressText(OutputHeader
433 + "Server cannot cancel this job because it has not been submitted properly. just close the window.\n");
439 public void pollJob(AWsJob job) throws Exception
441 ((MsaWSJob) job).result = server.getResult(((MsaWSJob) job).getJobId());
445 public void StartJob(AWsJob job)
447 if (!(job instanceof MsaWSJob))
449 throw new Error(MessageManager.formatMessage(
450 "error.implementation_error_msawbjob_called", new String[]
451 { job.getClass().toString() }));
453 MsaWSJob j = (MsaWSJob) job;
456 if (Console.isDebugEnabled())
459 "Tried to submit an already submitted job " + j.getJobId());
463 if (j.seqs.getSeqs() == null)
465 // special case - selection consisted entirely of empty sequences...
466 j.setSubmitted(true);
467 j.result = new MsaResult();
468 j.result.setFinished(true);
470 MessageManager.getString("label.empty_alignment_job"));
471 ((MsaResult) j.result).setMsa(null);
475 vamsas.objects.simple.WsJobId jobsubmit = server.align(j.seqs);
477 if ((jobsubmit != null) && (jobsubmit.getStatus() == 1))
479 j.setJobId(jobsubmit.getJobId());
480 j.setSubmitted(true);
481 j.setSubjobComplete(false);
482 // System.out.println(WsURL + " Job Id '" + jobId + "'");
486 if (jobsubmit == null)
488 throw new Exception(MessageManager.formatMessage(
489 "exception.web_service_returned_null_try_later",
494 throw new Exception(jobsubmit.getJobId());
496 } catch (Exception e)
498 // TODO: JBPNote catch timeout or other fault types explicitly
499 // For unexpected errors
500 System.err.println(WebServiceName
501 + "Client: Failed to submit the sequences for alignment (probably a server side problem)\n"
502 + "When contacting Server:" + WsUrl + "\n" + e.toString()
504 j.setAllowedServerExceptions(0);
505 wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_SERVERERROR);
506 wsInfo.setStatus(j.getJobnum(),
507 WebserviceInfo.STATE_STOPPED_SERVERERROR);
508 wsInfo.appendProgressText(j.getJobnum(), MessageManager
509 .getString("info.failed_to_submit_sequences_for_alignment"));
511 // e.printStackTrace(); // TODO: JBPNote DEBUG
515 private jalview.datamodel.Sequence[] getVamsasAlignment(
516 vamsas.objects.simple.Alignment valign)
518 // TODO: refactor to helper class for vamsas.objects.simple objects
519 vamsas.objects.simple.Sequence[] seqs = valign.getSeqs().getSeqs();
520 jalview.datamodel.Sequence[] msa = new jalview.datamodel.Sequence[seqs.length];
522 for (int i = 0, j = seqs.length; i < j; i++)
524 msa[i] = new jalview.datamodel.Sequence(seqs[i].getId(),
532 public void parseResult()
534 int results = 0; // number of result sets received
535 JobStateSummary finalState = new JobStateSummary();
538 for (int j = 0; j < jobs.length; j++)
540 finalState.updateJobPanelState(wsInfo, OutputHeader, jobs[j]);
541 if (jobs[j].isSubmitted() && jobs[j].isSubjobComplete()
542 && jobs[j].hasResults())
545 // if (Cache.isDebugEnabled())
547 // System.out.println("Job lob for job
548 // "+jobs[j].getJobId()+":"+jobs[j].getJobnum());
549 // System.out.println(jobs[j].getStatus());
552 vamsas.objects.simple.Alignment valign = ((MsaResult) ((MsaWSJob) jobs[j]).result)
556 wsInfo.appendProgressText(jobs[j].getJobnum(), MessageManager
557 .getString("info.alignment_object_method_notes"));
558 String[] lines = valign.getMethod();
559 for (int line = 0; line < lines.length; line++)
561 wsInfo.appendProgressText(jobs[j].getJobnum(),
564 // JBPNote The returned files from a webservice could be
565 // hidden behind icons in the monitor window that,
566 // when clicked, pop up their corresponding data
571 } catch (Exception ex)
575 "Unexpected exception when processing results for " + alTitle,
577 wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_ERROR);
581 wsInfo.showResultsNewFrame
582 .addActionListener(new java.awt.event.ActionListener()
585 public void actionPerformed(java.awt.event.ActionEvent evt)
587 displayResults(true);
591 .addActionListener(new java.awt.event.ActionListener()
594 public void actionPerformed(java.awt.event.ActionEvent evt)
596 displayResults(false);
599 wsInfo.setResultsReady();
603 wsInfo.setFinishedNoResults();
607 void displayResults(boolean newFrame)
609 // view input or result data for each block
610 Vector alorders = new Vector();
611 SequenceI[][] results = new SequenceI[jobs.length][];
612 AlignmentOrder[] orders = new AlignmentOrder[jobs.length];
613 for (int j = 0; j < jobs.length; j++)
615 if (jobs[j].hasResults())
617 Object[] res = ((MsaWSJob) jobs[j]).getAlignment();
618 alorders.add(res[1]);
619 results[j] = (SequenceI[]) res[0];
620 orders[j] = (AlignmentOrder) res[1];
622 // SequenceI[] alignment = input.getUpdated
629 Object[] newview = input.getUpdatedView(results, orders, getGapChar());
630 // trash references to original result data
631 for (int j = 0; j < jobs.length; j++)
636 SequenceI[] alignment = (SequenceI[]) newview[0];
637 HiddenColumns hidden = (HiddenColumns) newview[1];
638 Alignment al = new Alignment(alignment);
639 // TODO: add 'provenance' property to alignment from the method notes
640 // accompanying each subjob
643 al.setDataset(dataset);
646 propagateDatasetMappings(al);
647 // JBNote- TODO: warn user if a block is input rather than aligned data ?
651 AlignFrame af = new AlignFrame(al, hidden, AlignFrame.DEFAULT_WIDTH,
652 AlignFrame.DEFAULT_HEIGHT);
654 // initialise with same renderer settings as in parent alignframe.
655 af.getFeatureRenderer().transferSettings(this.featureSettings);
657 if (alorders.size() > 0)
659 if (alorders.size() == 1)
661 af.addSortByOrderMenuItem(WebServiceName + " Ordering",
662 (AlignmentOrder) alorders.get(0));
666 // construct a non-redundant ordering set
667 Vector names = new Vector();
668 for (int i = 0, l = alorders.size(); i < l; i++)
670 String orderName = new String(" Region " + i);
675 if (((AlignmentOrder) alorders.get(i))
676 .equals((alorders.get(j))))
680 orderName += "," + j;
688 if (i == 0 && j == 1)
690 names.add(new String(""));
694 names.add(orderName);
697 for (int i = 0, l = alorders.size(); i < l; i++)
699 af.addSortByOrderMenuItem(
700 WebServiceName + ((String) names.get(i)) + " Ordering",
701 (AlignmentOrder) alorders.get(i));
706 Desktop.addInternalFrame(af, alTitle, AlignFrame.DEFAULT_WIDTH,
707 AlignFrame.DEFAULT_HEIGHT);
712 System.out.println("MERGE WITH OLD FRAME");
713 // TODO: modify alignment in original frame, replacing old for new
714 // alignment using the commands.EditCommand model to ensure the update can
720 public boolean canMergeResults()