2 * Jalview - A Sequence Alignment Editor and Viewer (Version 2.7)
3 * Copyright (C) 2011 J Procter, AM Waterhouse, J Engelhardt, LM Lui, G Barton, M Clamp, S Searle
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 of the License, or (at your option) any later version.
11 * Jalview is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty
13 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR
14 * PURPOSE. See the GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License along with Jalview. If not, see <http://www.gnu.org/licenses/>.
18 package jalview.ws.jws1;
22 import jalview.analysis.*;
24 import jalview.datamodel.*;
26 import jalview.ws.AWsJob;
27 import jalview.ws.JobStateSummary;
28 import jalview.ws.WSClientI;
29 import vamsas.objects.simple.MsaResult;
31 class MsaWSThread extends JWS1Thread implements WSClientI
33 boolean submitGaps = false; // pass sequences including gaps to alignment
37 boolean preserveOrder = true; // and always store and recover sequence
41 class MsaWSJob extends WSJob
43 // hold special input for this
44 vamsas.objects.simple.SequenceSet seqs = new vamsas.objects.simple.SequenceSet();
54 public MsaWSJob(int jobNum, SequenceI[] inSeqs)
57 if (!prepareInput(inSeqs, 2))
60 subjobComplete = true;
61 result = new MsaResult();
62 result.setFinished(true);
63 result.setStatus("Job never ran - input returned to user.");
68 Hashtable SeqNames = new Hashtable();
70 Vector emptySeqs = new Vector();
73 * prepare input sequences for MsaWS service
76 * jalview sequences to be prepared
78 * minimum number of residues required for this MsaWS service
79 * @return true if seqs contains sequences to be submitted to service.
81 private boolean prepareInput(SequenceI[] seqs, int minlen)
87 "Implementation error: minlen must be zero or more.");
89 for (int i = 0; i < seqs.length; i++)
91 if (seqs[i].getEnd() - seqs[i].getStart() > minlen - 1)
96 boolean valid = nseqs > 1; // need at least two seqs
97 vamsas.objects.simple.Sequence[] seqarray = (valid) ? new vamsas.objects.simple.Sequence[nseqs]
99 for (int i = 0, n = 0; i < seqs.length; i++)
102 String newname = jalview.analysis.SeqsetUtils.unique_name(i); // same
106 SeqNames.put(newname,
107 jalview.analysis.SeqsetUtils.SeqCharacterHash(seqs[i]));
108 if (valid && seqs[i].getEnd() - seqs[i].getStart() > minlen - 1)
110 seqarray[n] = new vamsas.objects.simple.Sequence();
111 seqarray[n].setId(newname);
112 seqarray[n++].setSeq((submitGaps) ? seqs[i].getSequenceAsString()
113 : AlignSeq.extractGaps(jalview.util.Comparison.GapChars,
114 seqs[i].getSequenceAsString()));
119 if (seqs[i].getEnd() >= seqs[i].getStart())
121 empty = (submitGaps) ? seqs[i].getSequenceAsString() : AlignSeq
122 .extractGaps(jalview.util.Comparison.GapChars,
123 seqs[i].getSequenceAsString());
125 emptySeqs.add(new String[]
129 this.seqs = new vamsas.objects.simple.SequenceSet();
130 this.seqs.setSeqs(seqarray);
136 * @return true if getAlignment will return a valid alignment result.
138 public boolean hasResults()
140 if (subjobComplete && result != null && result.isFinished()
141 && ((MsaResult) result).getMsa() != null
142 && ((MsaResult) result).getMsa().getSeqs() != null)
149 public Object[] getAlignment()
152 if (result != null && result.isFinished())
154 SequenceI[] alseqs = null;
155 char alseq_gapchar = '-';
157 if (((MsaResult) result).getMsa() != null)
159 alseqs = getVamsasAlignment(((MsaResult) result).getMsa());
160 alseq_gapchar = ((MsaResult) result).getMsa().getGapchar()
162 alseq_l = alseqs.length;
164 if (emptySeqs.size() > 0)
166 SequenceI[] t_alseqs = new SequenceI[alseq_l + emptySeqs.size()];
171 for (i = 0, w = alseqs[0].getLength(); i < alseq_l; i++)
173 if (w < alseqs[i].getLength())
175 w = alseqs[i].getLength();
177 t_alseqs[i] = alseqs[i];
181 // check that aligned width is at least as wide as emptySeqs width.
183 for (i = 0, w = emptySeqs.size(); i < w; i++)
185 String[] es = (String[]) emptySeqs.get(i);
186 if (es != null && es[1] != null)
188 int sw = es[1].length();
195 // make a gapped string.
196 StringBuffer insbuff = new StringBuffer(w);
197 for (i = 0; i < nw; i++)
199 insbuff.append(alseq_gapchar);
203 for (i = 0; i < alseq_l; i++)
205 int sw = t_alseqs[i].getLength();
209 alseqs[i].setSequence(t_alseqs[i].getSequenceAsString()
210 + insbuff.substring(0, sw - nw));
214 for (i = 0, w = emptySeqs.size(); i < w; i++)
216 String[] es = (String[]) emptySeqs.get(i);
219 t_alseqs[i + alseq_l] = new jalview.datamodel.Sequence(es[0],
220 insbuff.toString(), 1, 0);
224 if (es[1].length() < nw)
226 t_alseqs[i + alseq_l] = new jalview.datamodel.Sequence(
228 es[1] + insbuff.substring(0, nw - es[1].length()),
229 1, 1 + es[1].length());
233 t_alseqs[i + alseq_l] = new jalview.datamodel.Sequence(
240 AlignmentOrder msaorder = new AlignmentOrder(alseqs);
241 // always recover the order - makes parseResult()'s life easier.
242 jalview.analysis.AlignmentSorter.recoverOrder(alseqs);
243 // account for any missing sequences
244 jalview.analysis.SeqsetUtils.deuniquify(SeqNames, alseqs);
246 { alseqs, msaorder };
252 * mark subjob as cancelled and set result object appropriatly
257 subjobComplete = true;
263 * @return boolean true if job can be submitted.
265 public boolean hasValidInput()
267 if (seqs.getSeqs() != null)
275 String alTitle; // name which will be used to form new alignment window.
277 Alignment dataset; // dataset to which the new alignment will be
281 ext.vamsas.MuscleWS server = null;
284 * set basic options for this (group) of Msa jobs
291 MsaWSThread(ext.vamsas.MuscleWS server, String wsUrl,
292 WebserviceInfo wsinfo, jalview.gui.AlignFrame alFrame,
293 AlignmentView alview, String wsname, boolean subgaps,
296 super(alFrame, wsinfo, alview, wsname, wsUrl);
297 this.server = server;
298 this.submitGaps = subgaps;
299 this.preserveOrder = presorder;
303 * create one or more Msa jobs to align visible seuqences in _msa
316 MsaWSThread(ext.vamsas.MuscleWS server, String wsUrl,
317 WebserviceInfo wsinfo, jalview.gui.AlignFrame alFrame,
318 String wsname, String title, AlignmentView _msa, boolean subgaps,
319 boolean presorder, Alignment seqset)
321 this(server, wsUrl, wsinfo, alFrame, _msa, wsname, subgaps, presorder);
322 OutputHeader = wsInfo.getProgressText();
326 SequenceI[][] conmsa = _msa.getVisibleContigs('-');
329 int njobs = conmsa.length;
330 jobs = new MsaWSJob[njobs];
331 for (int j = 0; j < njobs; j++)
335 jobs[j] = new MsaWSJob(wsinfo.addJobPane(), conmsa[j]);
339 jobs[j] = new MsaWSJob(0, conmsa[j]);
343 wsinfo.setProgressName("region " + jobs[j].getJobnum(),
344 jobs[j].getJobnum());
346 wsinfo.setProgressText(jobs[j].getJobnum(), OutputHeader);
351 public boolean isCancellable()
356 public void cancelJob()
358 if (!jobComplete && jobs != null)
360 boolean cancelled = true;
361 for (int job = 0; job < jobs.length; job++)
363 if (jobs[job].isSubmitted() && !jobs[job].isSubjobComplete())
365 String cancelledMessage = "";
368 vamsas.objects.simple.WsJobId cancelledJob = server
369 .cancel(jobs[job].getJobId());
370 if (cancelledJob.getStatus() == 2)
373 cancelledMessage = "Job cancelled.";
374 ((MsaWSJob) jobs[job]).cancel();
375 wsInfo.setStatus(jobs[job].getJobnum(),
376 WebserviceInfo.STATE_CANCELLED_OK);
378 else if (cancelledJob.getStatus() == 3)
380 // VALID UNSTOPPABLE JOB
381 cancelledMessage += "Server cannot cancel this job. just close the window.\n";
383 // wsInfo.setStatus(jobs[job].jobnum,
384 // WebserviceInfo.STATE_RUNNING);
387 if (cancelledJob.getJobId() != null)
389 cancelledMessage += ("[" + cancelledJob.getJobId() + "]");
392 cancelledMessage += "\n";
393 } catch (Exception exc)
395 cancelledMessage += ("\nProblems cancelling the job : Exception received...\n"
398 "Exception whilst cancelling " + jobs[job].getJobId(),
401 wsInfo.setProgressText(jobs[job].getJobnum(), OutputHeader
402 + cancelledMessage + "\n");
407 wsInfo.setStatus(WebserviceInfo.STATE_CANCELLED_OK);
410 this.interrupt(); // kick thread to update job states.
416 wsInfo.setProgressText(OutputHeader
417 + "Server cannot cancel this job because it has not been submitted properly. just close the window.\n");
422 public void pollJob(AWsJob job) throws Exception
424 ((MsaWSJob) job).result = server.getResult(((MsaWSJob) job).getJobId());
427 public void StartJob(AWsJob job)
429 if (!(job instanceof MsaWSJob))
431 throw new Error("StartJob(MsaWSJob) called on a WSJobInstance "
434 MsaWSJob j = (MsaWSJob) job;
437 if (Cache.log.isDebugEnabled())
439 Cache.log.debug("Tried to submit an already submitted job "
444 if (j.seqs.getSeqs() == null)
446 // special case - selection consisted entirely of empty sequences...
447 j.setSubmitted(true);
448 j.result = new MsaResult();
449 j.result.setFinished(true);
450 j.result.setStatus("Empty Alignment Job");
451 ((MsaResult) j.result).setMsa(null);
455 vamsas.objects.simple.WsJobId jobsubmit = server.align(j.seqs);
457 if ((jobsubmit != null) && (jobsubmit.getStatus() == 1))
459 j.setJobId(jobsubmit.getJobId());
460 j.setSubmitted(true);
461 j.setSubjobComplete(false);
462 // System.out.println(WsURL + " Job Id '" + jobId + "'");
466 if (jobsubmit == null)
471 + " returned null object, it probably cannot be contacted. Try again later ?");
474 throw new Exception(jobsubmit.getJobId());
476 } catch (Exception e)
478 // TODO: JBPNote catch timeout or other fault types explicitly
479 // For unexpected errors
481 .println(WebServiceName
482 + "Client: Failed to submit the sequences for alignment (probably a server side problem)\n"
483 + "When contacting Server:" + WsUrl + "\n"
484 + e.toString() + "\n");
485 j.setAllowedServerExceptions(0);
486 wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_SERVERERROR);
487 wsInfo.setStatus(j.getJobnum(),
488 WebserviceInfo.STATE_STOPPED_SERVERERROR);
489 wsInfo.appendProgressText(
491 "Failed to submit sequences for alignment.\n"
492 + "It is most likely that there is a problem with the server.\n"
493 + "Just close the window\n");
495 // e.printStackTrace(); // TODO: JBPNote DEBUG
499 private jalview.datamodel.Sequence[] getVamsasAlignment(
500 vamsas.objects.simple.Alignment valign)
502 // TODO: refactor to helper class for vamsas.objects.simple objects
503 vamsas.objects.simple.Sequence[] seqs = valign.getSeqs().getSeqs();
504 jalview.datamodel.Sequence[] msa = new jalview.datamodel.Sequence[seqs.length];
506 for (int i = 0, j = seqs.length; i < j; i++)
508 msa[i] = new jalview.datamodel.Sequence(seqs[i].getId(),
515 public void parseResult()
517 int results = 0; // number of result sets received
518 JobStateSummary finalState = new JobStateSummary();
521 for (int j = 0; j < jobs.length; j++)
523 finalState.updateJobPanelState(wsInfo, OutputHeader, jobs[j]);
524 if (jobs[j].isSubmitted() && jobs[j].isSubjobComplete()
525 && jobs[j].hasResults())
528 // if (Cache.log.isDebugEnabled())
530 // System.out.println("Job lob for job "+jobs[j].getJobId()+":"+jobs[j].getJobnum());
531 // System.out.println(jobs[j].getStatus());
534 vamsas.objects.simple.Alignment valign = ((MsaResult) ((MsaWSJob) jobs[j]).result)
538 wsInfo.appendProgressText(jobs[j].getJobnum(),
539 "\nAlignment Object Method Notes\n");
540 String[] lines = valign.getMethod();
541 for (int line = 0; line < lines.length; line++)
543 wsInfo.appendProgressText(jobs[j].getJobnum(), lines[line]
546 // JBPNote The returned files from a webservice could be
547 // hidden behind icons in the monitor window that,
548 // when clicked, pop up their corresponding data
553 } catch (Exception ex)
556 Cache.log.error("Unexpected exception when processing results for "
558 wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_ERROR);
562 wsInfo.showResultsNewFrame
563 .addActionListener(new java.awt.event.ActionListener()
565 public void actionPerformed(java.awt.event.ActionEvent evt)
567 displayResults(true);
571 .addActionListener(new java.awt.event.ActionListener()
573 public void actionPerformed(java.awt.event.ActionEvent evt)
575 displayResults(false);
578 wsInfo.setResultsReady();
582 wsInfo.setFinishedNoResults();
586 void displayResults(boolean newFrame)
588 // view input or result data for each block
589 Vector alorders = new Vector();
590 SequenceI[][] results = new SequenceI[jobs.length][];
591 AlignmentOrder[] orders = new AlignmentOrder[jobs.length];
592 for (int j = 0; j < jobs.length; j++)
594 if (jobs[j].hasResults())
596 Object[] res = ((MsaWSJob) jobs[j]).getAlignment();
597 alorders.add(res[1]);
598 results[j] = (SequenceI[]) res[0];
599 orders[j] = (AlignmentOrder) res[1];
601 // SequenceI[] alignment = input.getUpdated
608 Object[] newview = input.getUpdatedView(results, orders, getGapChar());
609 // trash references to original result data
610 for (int j = 0; j < jobs.length; j++)
615 SequenceI[] alignment = (SequenceI[]) newview[0];
616 ColumnSelection columnselection = (ColumnSelection) newview[1];
617 Alignment al = new Alignment(alignment);
618 // TODO: add 'provenance' property to alignment from the method notes
619 // accompanying each subjob
622 al.setDataset(dataset);
625 propagateDatasetMappings(al);
626 // JBNote- TODO: warn user if a block is input rather than aligned data ?
630 AlignFrame af = new AlignFrame(al, columnselection,
631 AlignFrame.DEFAULT_WIDTH, AlignFrame.DEFAULT_HEIGHT);
633 // initialise with same renderer settings as in parent alignframe.
634 af.getFeatureRenderer().transferSettings(this.featureSettings);
636 if (alorders.size() > 0)
638 if (alorders.size() == 1)
640 af.addSortByOrderMenuItem(WebServiceName + " Ordering",
641 (AlignmentOrder) alorders.get(0));
645 // construct a non-redundant ordering set
646 Vector names = new Vector();
647 for (int i = 0, l = alorders.size(); i < l; i++)
649 String orderName = new String(" Region " + i);
654 if (((AlignmentOrder) alorders.get(i))
655 .equals(((AlignmentOrder) alorders.get(j))))
659 orderName += "," + j;
667 if (i == 0 && j == 1)
669 names.add(new String(""));
673 names.add(orderName);
676 for (int i = 0, l = alorders.size(); i < l; i++)
678 af.addSortByOrderMenuItem(
679 WebServiceName + ((String) names.get(i)) + " Ordering",
680 (AlignmentOrder) alorders.get(i));
685 Desktop.addInternalFrame(af, alTitle, AlignFrame.DEFAULT_WIDTH,
686 AlignFrame.DEFAULT_HEIGHT);
691 System.out.println("MERGE WITH OLD FRAME");
692 // TODO: modify alignment in original frame, replacing old for new
693 // alignment using the commands.EditCommand model to ensure the update can
698 public boolean canMergeResults()