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.AlignmentOrder;
27 import jalview.datamodel.AlignmentView;
28 import jalview.datamodel.ColumnSelection;
29 import jalview.datamodel.SequenceI;
30 import jalview.gui.AlignFrame;
31 import jalview.gui.Desktop;
32 import jalview.gui.WebserviceInfo;
33 import jalview.util.MessageManager;
34 import jalview.ws.AWsJob;
35 import jalview.ws.JobStateSummary;
36 import jalview.ws.WSClientI;
38 import java.util.Hashtable;
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 Hashtable SeqNames = new Hashtable();
82 Vector emptySeqs = new Vector();
85 * prepare input sequences for MsaWS service
88 * jalview sequences to be prepared
90 * minimum number of residues required for this MsaWS service
91 * @return true if seqs contains sequences to be submitted to service.
93 private boolean prepareInput(SequenceI[] seqs, int minlen)
100 .getString("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) ? new vamsas.objects.simple.Sequence[nseqs]
112 for (int i = 0, n = 0; i < seqs.length; i++)
115 String newname = jalview.analysis.SeqsetUtils.unique_name(i); // same
119 SeqNames.put(newname,
120 jalview.analysis.SeqsetUtils.SeqCharacterHash(seqs[i]));
121 if (valid && seqs[i].getEnd() - seqs[i].getStart() > minlen - 1)
123 seqarray[n] = new vamsas.objects.simple.Sequence();
124 seqarray[n].setId(newname);
125 seqarray[n++].setSeq((submitGaps) ? seqs[i].getSequenceAsString()
126 : AlignSeq.extractGaps(jalview.util.Comparison.GapChars,
127 seqs[i].getSequenceAsString()));
132 if (seqs[i].getEnd() >= seqs[i].getStart())
134 empty = (submitGaps) ? seqs[i].getSequenceAsString() : AlignSeq
135 .extractGaps(jalview.util.Comparison.GapChars,
136 seqs[i].getSequenceAsString());
138 emptySeqs.add(new String[] { newname, empty });
141 this.seqs = new vamsas.objects.simple.SequenceSet();
142 this.seqs.setSeqs(seqarray);
148 * @return true if getAlignment will return a valid alignment result.
150 public boolean hasResults()
152 if (subjobComplete && result != null && result.isFinished()
153 && ((MsaResult) result).getMsa() != null
154 && ((MsaResult) result).getMsa().getSeqs() != null)
161 public Object[] getAlignment()
164 if (result != null && result.isFinished())
166 SequenceI[] alseqs = null;
167 char alseq_gapchar = '-';
169 if (((MsaResult) result).getMsa() != null)
171 alseqs = getVamsasAlignment(((MsaResult) result).getMsa());
172 alseq_gapchar = ((MsaResult) result).getMsa().getGapchar()
174 alseq_l = alseqs.length;
176 if (emptySeqs.size() > 0)
178 SequenceI[] t_alseqs = new SequenceI[alseq_l + emptySeqs.size()];
183 for (i = 0, w = alseqs[0].getLength(); i < alseq_l; i++)
185 if (w < alseqs[i].getLength())
187 w = alseqs[i].getLength();
189 t_alseqs[i] = alseqs[i];
193 // check that aligned width is at least as wide as emptySeqs width.
195 for (i = 0, w = emptySeqs.size(); i < w; i++)
197 String[] es = (String[]) emptySeqs.get(i);
198 if (es != null && es[1] != null)
200 int sw = es[1].length();
207 // make a gapped string.
208 StringBuffer insbuff = new StringBuffer(w);
209 for (i = 0; i < nw; i++)
211 insbuff.append(alseq_gapchar);
215 for (i = 0; i < alseq_l; i++)
217 int sw = t_alseqs[i].getLength();
221 alseqs[i].setSequence(t_alseqs[i].getSequenceAsString()
222 + insbuff.substring(0, sw - nw));
226 for (i = 0, w = emptySeqs.size(); i < w; i++)
228 String[] es = (String[]) emptySeqs.get(i);
231 t_alseqs[i + alseq_l] = new jalview.datamodel.Sequence(es[0],
232 insbuff.toString(), 1, 0);
236 if (es[1].length() < nw)
238 t_alseqs[i + alseq_l] = new jalview.datamodel.Sequence(
240 es[1] + insbuff.substring(0, nw - es[1].length()),
241 1, 1 + es[1].length());
245 t_alseqs[i + alseq_l] = new jalview.datamodel.Sequence(
252 AlignmentOrder msaorder = new AlignmentOrder(alseqs);
253 // always recover the order - makes parseResult()'s life easier.
254 jalview.analysis.AlignmentSorter.recoverOrder(alseqs);
255 // account for any missing sequences
256 jalview.analysis.SeqsetUtils.deuniquify(SeqNames, alseqs);
257 return new Object[] { alseqs, msaorder };
263 * mark subjob as cancelled and set result object appropriatly
268 subjobComplete = true;
274 * @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 Alignment 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, Alignment 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);
362 public boolean isCancellable()
367 public void cancelJob()
369 if (!jobComplete && jobs != null)
371 boolean cancelled = true;
372 for (int job = 0; job < jobs.length; job++)
374 if (jobs[job].isSubmitted() && !jobs[job].isSubjobComplete())
376 String cancelledMessage = "";
379 vamsas.objects.simple.WsJobId cancelledJob = server
380 .cancel(jobs[job].getJobId());
381 if (cancelledJob.getStatus() == 2)
384 cancelledMessage = "Job cancelled.";
385 ((MsaWSJob) jobs[job]).cancel();
386 wsInfo.setStatus(jobs[job].getJobnum(),
387 WebserviceInfo.STATE_CANCELLED_OK);
389 else if (cancelledJob.getStatus() == 3)
391 // VALID UNSTOPPABLE JOB
392 cancelledMessage += "Server cannot cancel this job. just close the window.\n";
394 // wsInfo.setStatus(jobs[job].jobnum,
395 // WebserviceInfo.STATE_RUNNING);
398 if (cancelledJob.getJobId() != null)
400 cancelledMessage += ("[" + cancelledJob.getJobId() + "]");
403 cancelledMessage += "\n";
404 } catch (Exception exc)
406 cancelledMessage += ("\nProblems cancelling the job : Exception received...\n"
409 "Exception whilst cancelling " + jobs[job].getJobId(),
412 wsInfo.setProgressText(jobs[job].getJobnum(), OutputHeader
413 + cancelledMessage + "\n");
418 wsInfo.setStatus(WebserviceInfo.STATE_CANCELLED_OK);
421 this.interrupt(); // kick thread to update job states.
427 wsInfo.setProgressText(OutputHeader
428 + "Server cannot cancel this job because it has not been submitted properly. just close the window.\n");
433 public void pollJob(AWsJob job) throws Exception
435 ((MsaWSJob) job).result = server.getResult(((MsaWSJob) job).getJobId());
438 public void StartJob(AWsJob job)
440 if (!(job instanceof MsaWSJob))
442 throw new Error(MessageManager.formatMessage(
443 "error.implementation_error_msawbjob_called",
444 new String[] { job.getClass().toString() }));
446 MsaWSJob j = (MsaWSJob) job;
449 if (Cache.log.isDebugEnabled())
451 Cache.log.debug("Tried to submit an already submitted job "
456 if (j.seqs.getSeqs() == null)
458 // special case - selection consisted entirely of empty sequences...
459 j.setSubmitted(true);
460 j.result = new MsaResult();
461 j.result.setFinished(true);
462 j.result.setStatus(MessageManager
463 .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(
482 "exception.web_service_returned_null_try_later",
483 new String[] { WsUrl }));
486 throw new Exception(jobsubmit.getJobId());
488 } catch (Exception e)
490 // TODO: JBPNote catch timeout or other fault types explicitly
491 // For unexpected errors
493 .println(WebServiceName
494 + "Client: Failed to submit the sequences for alignment (probably a server side problem)\n"
495 + "When contacting Server:" + WsUrl + "\n"
496 + e.toString() + "\n");
497 j.setAllowedServerExceptions(0);
498 wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_SERVERERROR);
499 wsInfo.setStatus(j.getJobnum(),
500 WebserviceInfo.STATE_STOPPED_SERVERERROR);
501 wsInfo.appendProgressText(j.getJobnum(), MessageManager
502 .getString("info.failed_to_submit_sequences_for_alignment"));
504 // e.printStackTrace(); // TODO: JBPNote DEBUG
508 private jalview.datamodel.Sequence[] getVamsasAlignment(
509 vamsas.objects.simple.Alignment valign)
511 // TODO: refactor to helper class for vamsas.objects.simple objects
512 vamsas.objects.simple.Sequence[] seqs = valign.getSeqs().getSeqs();
513 jalview.datamodel.Sequence[] msa = new jalview.datamodel.Sequence[seqs.length];
515 for (int i = 0, j = seqs.length; i < j; i++)
517 msa[i] = new jalview.datamodel.Sequence(seqs[i].getId(),
524 public void parseResult()
526 int results = 0; // number of result sets received
527 JobStateSummary finalState = new JobStateSummary();
530 for (int j = 0; j < jobs.length; j++)
532 finalState.updateJobPanelState(wsInfo, OutputHeader, jobs[j]);
533 if (jobs[j].isSubmitted() && jobs[j].isSubjobComplete()
534 && jobs[j].hasResults())
537 // if (Cache.log.isDebugEnabled())
539 // System.out.println("Job lob for job "+jobs[j].getJobId()+":"+jobs[j].getJobnum());
540 // System.out.println(jobs[j].getStatus());
543 vamsas.objects.simple.Alignment valign = ((MsaResult) ((MsaWSJob) jobs[j]).result)
547 wsInfo.appendProgressText(jobs[j].getJobnum(), MessageManager
548 .getString("info.alignment_object_method_notes"));
549 String[] lines = valign.getMethod();
550 for (int line = 0; line < lines.length; line++)
552 wsInfo.appendProgressText(jobs[j].getJobnum(), lines[line]
555 // JBPNote The returned files from a webservice could be
556 // hidden behind icons in the monitor window that,
557 // when clicked, pop up their corresponding data
562 } catch (Exception ex)
565 Cache.log.error("Unexpected exception when processing results for "
567 wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_ERROR);
571 wsInfo.showResultsNewFrame
572 .addActionListener(new java.awt.event.ActionListener()
574 public void actionPerformed(java.awt.event.ActionEvent evt)
576 displayResults(true);
580 .addActionListener(new java.awt.event.ActionListener()
582 public void actionPerformed(java.awt.event.ActionEvent evt)
584 displayResults(false);
587 wsInfo.setResultsReady();
591 wsInfo.setFinishedNoResults();
595 void displayResults(boolean newFrame)
597 // view input or result data for each block
598 Vector alorders = new Vector();
599 SequenceI[][] results = new SequenceI[jobs.length][];
600 AlignmentOrder[] orders = new AlignmentOrder[jobs.length];
601 for (int j = 0; j < jobs.length; j++)
603 if (jobs[j].hasResults())
605 Object[] res = ((MsaWSJob) jobs[j]).getAlignment();
606 alorders.add(res[1]);
607 results[j] = (SequenceI[]) res[0];
608 orders[j] = (AlignmentOrder) res[1];
610 // SequenceI[] alignment = input.getUpdated
617 Object[] newview = input.getUpdatedView(results, orders, getGapChar());
618 // trash references to original result data
619 for (int j = 0; j < jobs.length; j++)
624 SequenceI[] alignment = (SequenceI[]) newview[0];
625 ColumnSelection columnselection = (ColumnSelection) newview[1];
626 Alignment al = new Alignment(alignment);
627 // TODO: add 'provenance' property to alignment from the method notes
628 // accompanying each subjob
631 al.setDataset(dataset);
634 propagateDatasetMappings(al);
635 // JBNote- TODO: warn user if a block is input rather than aligned data ?
639 AlignFrame af = new AlignFrame(al, columnselection,
640 AlignFrame.DEFAULT_WIDTH, AlignFrame.DEFAULT_HEIGHT);
642 // initialise with same renderer settings as in parent alignframe.
643 af.getFeatureRenderer().transferSettings(this.featureSettings);
645 if (alorders.size() > 0)
647 if (alorders.size() == 1)
649 af.addSortByOrderMenuItem(WebServiceName + " Ordering",
650 (AlignmentOrder) alorders.get(0));
654 // construct a non-redundant ordering set
655 Vector names = new Vector();
656 for (int i = 0, l = alorders.size(); i < l; i++)
658 String orderName = new String(" Region " + i);
663 if (((AlignmentOrder) alorders.get(i))
664 .equals(((AlignmentOrder) alorders.get(j))))
668 orderName += "," + j;
676 if (i == 0 && j == 1)
678 names.add(new String(""));
682 names.add(orderName);
685 for (int i = 0, l = alorders.size(); i < l; i++)
687 af.addSortByOrderMenuItem(
688 WebServiceName + ((String) names.get(i)) + " Ordering",
689 (AlignmentOrder) alorders.get(i));
694 Desktop.addInternalFrame(af, alTitle, AlignFrame.DEFAULT_WIDTH,
695 AlignFrame.DEFAULT_HEIGHT);
700 System.out.println("MERGE WITH OLD FRAME");
701 // TODO: modify alignment in original frame, replacing old for new
702 // alignment using the commands.EditCommand model to ensure the update can
707 public boolean canMergeResults()