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.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.Vector;
41 import vamsas.objects.simple.MsaResult;
43 class MsaWSThread extends JWS1Thread implements WSClientI
45 boolean submitGaps = false; // pass sequences including gaps to alignment
49 boolean preserveOrder = true; // and always store and recover sequence
53 class MsaWSJob extends WSJob
55 // hold special input for this
56 vamsas.objects.simple.SequenceSet seqs = new vamsas.objects.simple.SequenceSet();
66 public MsaWSJob(int jobNum, SequenceI[] inSeqs)
69 if (!prepareInput(inSeqs, 2))
72 subjobComplete = true;
73 result = new MsaResult();
74 result.setFinished(true);
75 result.setStatus(MessageManager.getString("label.job_never_ran"));
80 Vector emptySeqs = new Vector();
83 * prepare input sequences for MsaWS service
86 * jalview sequences to be prepared
88 * minimum number of residues required for this MsaWS service
89 * @return true if seqs contains sequences to be submitted to service.
91 private boolean prepareInput(SequenceI[] seqs, int minlen)
96 throw new Error(MessageManager.getString(
97 "error.implementation_error_minlen_must_be_greater_zero"));
99 for (int i = 0; i < seqs.length; i++)
101 if (seqs[i].getEnd() - seqs[i].getStart() > minlen - 1)
106 boolean valid = nseqs > 1; // need at least two seqs
107 vamsas.objects.simple.Sequence[] seqarray = (valid)
108 ? new vamsas.objects.simple.Sequence[nseqs]
110 for (int i = 0, n = 0; i < seqs.length; i++)
113 String newname = jalview.analysis.SeqsetUtils.unique_name(i); // same
117 SeqNames.put(newname,
118 jalview.analysis.SeqsetUtils.SeqCharacterHash(seqs[i]));
119 if (valid && seqs[i].getEnd() - seqs[i].getStart() > minlen - 1)
121 seqarray[n] = new vamsas.objects.simple.Sequence();
122 seqarray[n].setId(newname);
123 seqarray[n++].setSeq((submitGaps) ? seqs[i].getSequenceAsString()
124 : AlignSeq.extractGaps(jalview.util.Comparison.GapChars,
125 seqs[i].getSequenceAsString()));
130 if (seqs[i].getEnd() >= seqs[i].getStart())
132 empty = (submitGaps) ? seqs[i].getSequenceAsString()
133 : AlignSeq.extractGaps(jalview.util.Comparison.GapChars,
134 seqs[i].getSequenceAsString());
136 emptySeqs.add(new String[] { newname, empty });
139 this.seqs = new vamsas.objects.simple.SequenceSet();
140 this.seqs.setSeqs(seqarray);
146 * @return true if getAlignment will return a valid alignment result.
149 public boolean hasResults()
151 if (subjobComplete && result != null && result.isFinished()
152 && ((MsaResult) result).getMsa() != null
153 && ((MsaResult) result).getMsa().getSeqs() != null)
160 public Object[] getAlignment()
163 if (result != null && result.isFinished())
165 SequenceI[] alseqs = null;
166 char alseq_gapchar = '-';
168 if (((MsaResult) result).getMsa() != null)
170 alseqs = getVamsasAlignment(((MsaResult) result).getMsa());
171 alseq_gapchar = ((MsaResult) result).getMsa().getGapchar()
173 alseq_l = alseqs.length;
175 if (emptySeqs.size() > 0)
177 SequenceI[] t_alseqs = new SequenceI[alseq_l + emptySeqs.size()];
182 for (i = 0, w = alseqs[0].getLength(); i < alseq_l; i++)
184 if (w < alseqs[i].getLength())
186 w = alseqs[i].getLength();
188 t_alseqs[i] = alseqs[i];
192 // check that aligned width is at least as wide as emptySeqs width.
194 for (i = 0, w = emptySeqs.size(); i < w; i++)
196 String[] es = (String[]) emptySeqs.get(i);
197 if (es != null && es[1] != null)
199 int sw = es[1].length();
206 // make a gapped string.
207 StringBuffer insbuff = new StringBuffer(w);
208 for (i = 0; i < nw; i++)
210 insbuff.append(alseq_gapchar);
214 for (i = 0; i < alseq_l; i++)
216 int sw = t_alseqs[i].getLength();
220 alseqs[i].setSequence(t_alseqs[i].getSequenceAsString()
221 + insbuff.substring(0, sw - nw));
225 for (i = 0, w = emptySeqs.size(); i < w; i++)
227 String[] es = (String[]) emptySeqs.get(i);
230 t_alseqs[i + alseq_l] = new jalview.datamodel.Sequence(es[0],
231 insbuff.toString(), 1, 0);
235 if (es[1].length() < nw)
237 t_alseqs[i + alseq_l] = new jalview.datamodel.Sequence(
239 es[1] + insbuff.substring(0, nw - es[1].length()),
240 1, 1 + es[1].length());
244 t_alseqs[i + alseq_l] = new jalview.datamodel.Sequence(
251 AlignmentOrder msaorder = new AlignmentOrder(alseqs);
252 // always recover the order - makes parseResult()'s life easier.
253 jalview.analysis.AlignmentSorter.recoverOrder(alseqs);
254 // account for any missing sequences
255 jalview.analysis.SeqsetUtils.deuniquify(SeqNames, alseqs);
256 return new Object[] { alseqs, msaorder };
262 * mark subjob as cancelled and set result object appropriatly
267 subjobComplete = true;
273 * @return boolean true if job can be submitted.
276 public boolean hasValidInput()
278 if (seqs.getSeqs() != null)
286 String alTitle; // name which will be used to form new alignment window.
288 AlignmentI dataset; // dataset to which the new alignment will be
292 ext.vamsas.MuscleWS server = null;
295 * set basic options for this (group) of Msa jobs
302 MsaWSThread(ext.vamsas.MuscleWS server, String wsUrl,
303 WebserviceInfo wsinfo, jalview.gui.AlignFrame alFrame,
304 AlignmentView alview, String wsname, boolean subgaps,
307 super(alFrame, wsinfo, alview, wsname, wsUrl);
308 this.server = server;
309 this.submitGaps = subgaps;
310 this.preserveOrder = presorder;
314 * create one or more Msa jobs to align visible seuqences in _msa
327 MsaWSThread(ext.vamsas.MuscleWS server, String wsUrl,
328 WebserviceInfo wsinfo, jalview.gui.AlignFrame alFrame,
329 String wsname, String title, AlignmentView _msa, boolean subgaps,
330 boolean presorder, AlignmentI seqset)
332 this(server, wsUrl, wsinfo, alFrame, _msa, wsname, subgaps, presorder);
333 OutputHeader = wsInfo.getProgressText();
337 SequenceI[][] conmsa = _msa.getVisibleContigs('-');
340 int njobs = conmsa.length;
341 jobs = new MsaWSJob[njobs];
342 for (int j = 0; j < njobs; j++)
346 jobs[j] = new MsaWSJob(wsinfo.addJobPane(), conmsa[j]);
350 jobs[j] = new MsaWSJob(0, conmsa[j]);
354 wsinfo.setProgressName("region " + jobs[j].getJobnum(),
355 jobs[j].getJobnum());
357 wsinfo.setProgressText(jobs[j].getJobnum(), OutputHeader);
363 public boolean isCancellable()
369 public void cancelJob()
371 if (!jobComplete && jobs != null)
373 boolean cancelled = true;
374 for (int job = 0; job < jobs.length; job++)
376 if (jobs[job].isSubmitted() && !jobs[job].isSubjobComplete())
378 String cancelledMessage = "";
381 vamsas.objects.simple.WsJobId cancelledJob = server
382 .cancel(jobs[job].getJobId());
383 if (cancelledJob.getStatus() == 2)
386 cancelledMessage = "Job cancelled.";
387 ((MsaWSJob) jobs[job]).cancel();
388 wsInfo.setStatus(jobs[job].getJobnum(),
389 WebserviceInfo.STATE_CANCELLED_OK);
391 else if (cancelledJob.getStatus() == 3)
393 // VALID UNSTOPPABLE JOB
394 cancelledMessage += "Server cannot cancel this job. just close the window.\n";
396 // wsInfo.setStatus(jobs[job].jobnum,
397 // WebserviceInfo.STATE_RUNNING);
400 if (cancelledJob.getJobId() != null)
402 cancelledMessage += ("[" + cancelledJob.getJobId() + "]");
405 cancelledMessage += "\n";
406 } catch (Exception exc)
408 cancelledMessage += ("\nProblems cancelling the job : Exception received...\n"
411 "Exception whilst cancelling " + jobs[job].getJobId(),
414 wsInfo.setProgressText(jobs[job].getJobnum(),
415 OutputHeader + cancelledMessage + "\n");
420 wsInfo.setStatus(WebserviceInfo.STATE_CANCELLED_OK);
423 this.interrupt(); // kick thread to update job states.
429 wsInfo.setProgressText(OutputHeader
430 + "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());
442 public void StartJob(AWsJob job)
444 if (!(job instanceof MsaWSJob))
446 throw new Error(MessageManager.formatMessage(
447 "error.implementation_error_msawbjob_called", new String[]
448 { job.getClass().toString() }));
450 MsaWSJob j = (MsaWSJob) job;
453 if (Cache.log.isDebugEnabled())
456 "Tried to submit an already submitted job " + j.getJobId());
460 if (j.seqs.getSeqs() == null)
462 // special case - selection consisted entirely of empty sequences...
463 j.setSubmitted(true);
464 j.result = new MsaResult();
465 j.result.setFinished(true);
467 MessageManager.getString("label.empty_alignment_job"));
468 ((MsaResult) j.result).setMsa(null);
472 vamsas.objects.simple.WsJobId jobsubmit = server.align(j.seqs);
474 if ((jobsubmit != null) && (jobsubmit.getStatus() == 1))
476 j.setJobId(jobsubmit.getJobId());
477 j.setSubmitted(true);
478 j.setSubjobComplete(false);
479 // System.out.println(WsURL + " Job Id '" + jobId + "'");
483 if (jobsubmit == null)
485 throw new Exception(MessageManager.formatMessage(
486 "exception.web_service_returned_null_try_later",
491 throw new Exception(jobsubmit.getJobId());
493 } catch (Exception e)
495 // TODO: JBPNote catch timeout or other fault types explicitly
496 // For unexpected errors
497 System.err.println(WebServiceName
498 + "Client: Failed to submit the sequences for alignment (probably a server side problem)\n"
499 + "When contacting Server:" + WsUrl + "\n" + e.toString()
501 j.setAllowedServerExceptions(0);
502 wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_SERVERERROR);
503 wsInfo.setStatus(j.getJobnum(),
504 WebserviceInfo.STATE_STOPPED_SERVERERROR);
505 wsInfo.appendProgressText(j.getJobnum(), MessageManager
506 .getString("info.failed_to_submit_sequences_for_alignment"));
508 // e.printStackTrace(); // TODO: JBPNote DEBUG
512 private jalview.datamodel.Sequence[] getVamsasAlignment(
513 vamsas.objects.simple.Alignment valign)
515 // TODO: refactor to helper class for vamsas.objects.simple objects
516 vamsas.objects.simple.Sequence[] seqs = valign.getSeqs().getSeqs();
517 jalview.datamodel.Sequence[] msa = new jalview.datamodel.Sequence[seqs.length];
519 for (int i = 0, j = seqs.length; i < j; i++)
521 msa[i] = new jalview.datamodel.Sequence(seqs[i].getId(),
529 public void parseResult()
531 int results = 0; // number of result sets received
532 JobStateSummary finalState = new JobStateSummary();
535 for (int j = 0; j < jobs.length; j++)
537 finalState.updateJobPanelState(wsInfo, OutputHeader, jobs[j]);
538 if (jobs[j].isSubmitted() && jobs[j].isSubjobComplete()
539 && jobs[j].hasResults())
542 // if (Cache.log.isDebugEnabled())
544 // System.out.println("Job lob for job
545 // "+jobs[j].getJobId()+":"+jobs[j].getJobnum());
546 // System.out.println(jobs[j].getStatus());
549 vamsas.objects.simple.Alignment valign = ((MsaResult) ((MsaWSJob) jobs[j]).result)
553 wsInfo.appendProgressText(jobs[j].getJobnum(), MessageManager
554 .getString("info.alignment_object_method_notes"));
555 String[] lines = valign.getMethod();
556 for (int line = 0; line < lines.length; line++)
558 wsInfo.appendProgressText(jobs[j].getJobnum(),
561 // JBPNote The returned files from a webservice could be
562 // hidden behind icons in the monitor window that,
563 // when clicked, pop up their corresponding data
568 } catch (Exception ex)
572 "Unexpected exception when processing results for " + alTitle,
574 wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_ERROR);
578 wsInfo.showResultsNewFrame
579 .addActionListener(new java.awt.event.ActionListener()
582 public void actionPerformed(java.awt.event.ActionEvent evt)
584 displayResults(true);
588 .addActionListener(new java.awt.event.ActionListener()
591 public void actionPerformed(java.awt.event.ActionEvent evt)
593 displayResults(false);
596 wsInfo.setResultsReady();
600 wsInfo.setFinishedNoResults();
604 void displayResults(boolean newFrame)
606 // view input or result data for each block
607 Vector alorders = new Vector();
608 SequenceI[][] results = new SequenceI[jobs.length][];
609 AlignmentOrder[] orders = new AlignmentOrder[jobs.length];
610 for (int j = 0; j < jobs.length; j++)
612 if (jobs[j].hasResults())
614 Object[] res = ((MsaWSJob) jobs[j]).getAlignment();
615 alorders.add(res[1]);
616 results[j] = (SequenceI[]) res[0];
617 orders[j] = (AlignmentOrder) res[1];
619 // SequenceI[] alignment = input.getUpdated
626 Object[] newview = input.getUpdatedView(results, orders, getGapChar());
627 // trash references to original result data
628 for (int j = 0; j < jobs.length; j++)
633 SequenceI[] alignment = (SequenceI[]) newview[0];
634 HiddenColumns hidden = (HiddenColumns) newview[1];
635 Alignment al = new Alignment(alignment);
636 // TODO: add 'provenance' property to alignment from the method notes
637 // accompanying each subjob
640 al.setDataset(dataset);
643 propagateDatasetMappings(al);
644 // JBNote- TODO: warn user if a block is input rather than aligned data ?
648 AlignFrame af = new AlignFrame(al, hidden, AlignFrame.DEFAULT_WIDTH,
649 AlignFrame.DEFAULT_HEIGHT);
651 // initialise with same renderer settings as in parent alignframe.
652 af.getFeatureRenderer().transferSettings(this.featureSettings);
654 if (alorders.size() > 0)
656 if (alorders.size() == 1)
658 af.addSortByOrderMenuItem(WebServiceName + " Ordering",
659 (AlignmentOrder) alorders.get(0));
663 // construct a non-redundant ordering set
664 Vector names = new Vector();
665 for (int i = 0, l = alorders.size(); i < l; i++)
667 String orderName = new String(" Region " + i);
672 if (((AlignmentOrder) alorders.get(i))
673 .equals((alorders.get(j))))
677 orderName += "," + j;
685 if (i == 0 && j == 1)
687 names.add(new String(""));
691 names.add(orderName);
694 for (int i = 0, l = alorders.size(); i < l; i++)
696 af.addSortByOrderMenuItem(
697 WebServiceName + ((String) names.get(i)) + " Ordering",
698 (AlignmentOrder) alorders.get(i));
703 Desktop.addInternalFrame(af, alTitle, AlignFrame.DEFAULT_WIDTH,
704 AlignFrame.DEFAULT_HEIGHT);
709 System.out.println("MERGE WITH OLD FRAME");
710 // TODO: modify alignment in original frame, replacing old for new
711 // alignment using the commands.EditCommand model to ensure the update can
717 public boolean canMergeResults()