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.Cache;
25 import jalview.datamodel.Alignment;
26 import jalview.datamodel.AlignmentI;
27 import jalview.datamodel.AlignmentOrder;
28 import jalview.datamodel.AlignmentView;
29 import jalview.datamodel.ColumnSelection;
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)
101 .getString("error.implementation_error_minlen_must_be_greater_zero"));
103 for (int i = 0; i < seqs.length; i++)
105 if (seqs[i].getEnd() - seqs[i].getStart() > minlen - 1)
110 boolean valid = nseqs > 1; // need at least two seqs
111 vamsas.objects.simple.Sequence[] seqarray = (valid) ? 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() : AlignSeq
136 .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(), OutputHeader
418 + 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",
451 new String[] { job.getClass().toString() }));
453 MsaWSJob j = (MsaWSJob) job;
456 if (Cache.log.isDebugEnabled())
458 Cache.log.debug("Tried to submit an already submitted job "
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);
469 j.result.setStatus(MessageManager
470 .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",
490 new String[] { WsUrl }));
493 throw new Exception(jobsubmit.getJobId());
495 } catch (Exception e)
497 // TODO: JBPNote catch timeout or other fault types explicitly
498 // For unexpected errors
500 .println(WebServiceName
501 + "Client: Failed to submit the sequences for alignment (probably a server side problem)\n"
502 + "When contacting Server:" + WsUrl + "\n"
503 + e.toString() + "\n");
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.log.isDebugEnabled())
547 // System.out.println("Job lob for job "+jobs[j].getJobId()+":"+jobs[j].getJobnum());
548 // System.out.println(jobs[j].getStatus());
551 vamsas.objects.simple.Alignment valign = ((MsaResult) ((MsaWSJob) jobs[j]).result)
555 wsInfo.appendProgressText(jobs[j].getJobnum(), MessageManager
556 .getString("info.alignment_object_method_notes"));
557 String[] lines = valign.getMethod();
558 for (int line = 0; line < lines.length; line++)
560 wsInfo.appendProgressText(jobs[j].getJobnum(), lines[line]
563 // JBPNote The returned files from a webservice could be
564 // hidden behind icons in the monitor window that,
565 // when clicked, pop up their corresponding data
570 } catch (Exception ex)
573 Cache.log.error("Unexpected exception when processing results for "
575 wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_ERROR);
579 wsInfo.showResultsNewFrame
580 .addActionListener(new java.awt.event.ActionListener()
583 public void actionPerformed(java.awt.event.ActionEvent evt)
585 displayResults(true);
589 .addActionListener(new java.awt.event.ActionListener()
592 public void actionPerformed(java.awt.event.ActionEvent evt)
594 displayResults(false);
597 wsInfo.setResultsReady();
601 wsInfo.setFinishedNoResults();
605 void displayResults(boolean newFrame)
607 // view input or result data for each block
608 Vector alorders = new Vector();
609 SequenceI[][] results = new SequenceI[jobs.length][];
610 AlignmentOrder[] orders = new AlignmentOrder[jobs.length];
611 for (int j = 0; j < jobs.length; j++)
613 if (jobs[j].hasResults())
615 Object[] res = ((MsaWSJob) jobs[j]).getAlignment();
616 alorders.add(res[1]);
617 results[j] = (SequenceI[]) res[0];
618 orders[j] = (AlignmentOrder) res[1];
620 // SequenceI[] alignment = input.getUpdated
627 Object[] newview = input.getUpdatedView(results, orders, getGapChar());
628 // trash references to original result data
629 for (int j = 0; j < jobs.length; j++)
634 SequenceI[] alignment = (SequenceI[]) newview[0];
635 ColumnSelection columnselection = (ColumnSelection) newview[1];
636 Alignment al = new Alignment(alignment);
637 // TODO: add 'provenance' property to alignment from the method notes
638 // accompanying each subjob
641 al.setDataset(dataset);
644 propagateDatasetMappings(al);
645 // JBNote- TODO: warn user if a block is input rather than aligned data ?
649 AlignFrame af = new AlignFrame(al, columnselection,
650 AlignFrame.DEFAULT_WIDTH, AlignFrame.DEFAULT_HEIGHT);
652 // initialise with same renderer settings as in parent alignframe.
653 af.getFeatureRenderer().transferSettings(this.featureSettings);
655 if (alorders.size() > 0)
657 if (alorders.size() == 1)
659 af.addSortByOrderMenuItem(WebServiceName + " Ordering",
660 (AlignmentOrder) alorders.get(0));
664 // construct a non-redundant ordering set
665 Vector names = new Vector();
666 for (int i = 0, l = alorders.size(); i < l; i++)
668 String orderName = new String(" Region " + i);
673 if (((AlignmentOrder) alorders.get(i)).equals((alorders
678 orderName += "," + j;
686 if (i == 0 && j == 1)
688 names.add(new String(""));
692 names.add(orderName);
695 for (int i = 0, l = alorders.size(); i < l; i++)
697 af.addSortByOrderMenuItem(
698 WebServiceName + ((String) names.get(i)) + " Ordering",
699 (AlignmentOrder) alorders.get(i));
704 Desktop.addInternalFrame(af, alTitle, AlignFrame.DEFAULT_WIDTH,
705 AlignFrame.DEFAULT_HEIGHT);
710 System.out.println("MERGE WITH OLD FRAME");
711 // TODO: modify alignment in original frame, replacing old for new
712 // alignment using the commands.EditCommand model to ensure the update can
718 public boolean canMergeResults()