2 * Jalview - A Sequence Alignment Editor and Viewer (Version 2.5)
\r
3 * Copyright (C) 2010 J Procter, AM Waterhouse, G Barton, M Clamp, S Searle
\r
5 * This file is part of Jalview.
\r
7 * Jalview is free software: you can redistribute it and/or
\r
8 * modify it under the terms of the GNU General Public License
\r
9 * as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
\r
11 * Jalview is distributed in the hope that it will be useful, but
\r
12 * WITHOUT ANY WARRANTY; without even the implied warranty
\r
13 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR
\r
14 * PURPOSE. See the GNU General Public License for more details.
\r
16 * You should have received a copy of the GNU General Public License along with Jalview. If not, see <http://www.gnu.org/licenses/>.
\r
22 import jalview.analysis.*;
\r
23 import jalview.bin.*;
\r
24 import jalview.datamodel.*;
\r
25 import jalview.datamodel.Alignment;
\r
26 import jalview.gui.*;
\r
27 import vamsas.objects.simple.MsaResult;
\r
39 * Copyright: Copyright (c) 2004
\r
43 * Company: Dundee University
\r
46 * @author not attributable
\r
49 class MsaWSThread extends WSThread implements WSClientI
\r
51 boolean submitGaps = false; // pass sequences including gaps to alignment
\r
55 boolean preserveOrder = true; // and always store and recover sequence
\r
59 class MsaWSJob extends WSThread.WSJob
\r
61 // hold special input for this
\r
62 vamsas.objects.simple.SequenceSet seqs = new vamsas.objects.simple.SequenceSet();
\r
72 public MsaWSJob(int jobNum, SequenceI[] inSeqs)
\r
74 this.jobnum = jobNum;
\r
75 if (!prepareInput(inSeqs, 2))
\r
78 subjobComplete = true;
\r
79 result = new MsaResult();
\r
80 result.setFinished(true);
\r
81 result.setStatus("Job never ran - input returned to user.");
\r
86 Hashtable SeqNames = new Hashtable();
\r
88 Vector emptySeqs = new Vector();
\r
91 * prepare input sequences for MsaWS service
\r
94 * jalview sequences to be prepared
\r
96 * minimum number of residues required for this MsaWS service
\r
97 * @return true if seqs contains sequences to be submitted to service.
\r
99 private boolean prepareInput(SequenceI[] seqs, int minlen)
\r
105 "Implementation error: minlen must be zero or more.");
\r
107 for (int i = 0; i < seqs.length; i++)
\r
109 if (seqs[i].getEnd() - seqs[i].getStart() > minlen - 1)
\r
114 boolean valid = nseqs > 1; // need at least two seqs
\r
115 vamsas.objects.simple.Sequence[] seqarray = (valid) ? new vamsas.objects.simple.Sequence[nseqs]
\r
117 for (int i = 0, n = 0; i < seqs.length; i++)
\r
120 String newname = jalview.analysis.SeqsetUtils.unique_name(i); // same
\r
124 SeqNames.put(newname, jalview.analysis.SeqsetUtils
\r
125 .SeqCharacterHash(seqs[i]));
\r
126 if (valid && seqs[i].getEnd() - seqs[i].getStart() > minlen - 1)
\r
128 seqarray[n] = new vamsas.objects.simple.Sequence();
\r
129 seqarray[n].setId(newname);
\r
130 seqarray[n++].setSeq((submitGaps) ? seqs[i].getSequenceAsString()
\r
131 : AlignSeq.extractGaps(jalview.util.Comparison.GapChars,
\r
132 seqs[i].getSequenceAsString()));
\r
136 String empty = null;
\r
137 if (seqs[i].getEnd() >= seqs[i].getStart())
\r
139 empty = (submitGaps) ? seqs[i].getSequenceAsString() : AlignSeq
\r
140 .extractGaps(jalview.util.Comparison.GapChars, seqs[i]
\r
141 .getSequenceAsString());
\r
143 emptySeqs.add(new String[]
\r
144 { newname, empty });
\r
147 this.seqs = new vamsas.objects.simple.SequenceSet();
\r
148 this.seqs.setSeqs(seqarray);
\r
154 * @return true if getAlignment will return a valid alignment result.
\r
156 public boolean hasResults()
\r
158 if (subjobComplete && result != null && result.isFinished()
\r
159 && ((MsaResult) result).getMsa() != null
\r
160 && ((MsaResult) result).getMsa().getSeqs() != null)
\r
167 public Object[] getAlignment()
\r
170 if (result != null && result.isFinished())
\r
172 SequenceI[] alseqs = null;
\r
173 char alseq_gapchar = '-';
\r
175 if (((MsaResult) result).getMsa() != null)
\r
177 alseqs = getVamsasAlignment(((MsaResult) result).getMsa());
\r
178 alseq_gapchar = ((MsaResult) result).getMsa().getGapchar()
\r
180 alseq_l = alseqs.length;
\r
182 if (emptySeqs.size() > 0)
\r
184 SequenceI[] t_alseqs = new SequenceI[alseq_l + emptySeqs.size()];
\r
189 for (i = 0, w = alseqs[0].getLength(); i < alseq_l; i++)
\r
191 if (w < alseqs[i].getLength())
\r
193 w = alseqs[i].getLength();
\r
195 t_alseqs[i] = alseqs[i];
\r
199 // check that aligned width is at least as wide as emptySeqs width.
\r
200 int ow = w, nw = w;
\r
201 for (i = 0, w = emptySeqs.size(); i < w; i++)
\r
203 String[] es = (String[]) emptySeqs.get(i);
\r
204 if (es != null && es[1] != null)
\r
206 int sw = es[1].length();
\r
213 // make a gapped string.
\r
214 StringBuffer insbuff = new StringBuffer(w);
\r
215 for (i = 0; i < nw; i++)
\r
217 insbuff.append(alseq_gapchar);
\r
221 for (i = 0; i < alseq_l; i++)
\r
223 int sw = t_alseqs[i].getLength();
\r
227 alseqs[i].setSequence(t_alseqs[i].getSequenceAsString()
\r
228 + insbuff.substring(0, sw - nw));
\r
232 for (i = 0, w = emptySeqs.size(); i < w; i++)
\r
234 String[] es = (String[]) emptySeqs.get(i);
\r
237 t_alseqs[i + alseq_l] = new jalview.datamodel.Sequence(es[0],
\r
238 insbuff.toString(), 1, 0);
\r
242 if (es[1].length() < nw)
\r
244 t_alseqs[i + alseq_l] = new jalview.datamodel.Sequence(
\r
246 es[1] + insbuff.substring(0, nw - es[1].length()),
\r
247 1, 1 + es[1].length());
\r
251 t_alseqs[i + alseq_l] = new jalview.datamodel.Sequence(
\r
258 AlignmentOrder msaorder = new AlignmentOrder(alseqs);
\r
259 // always recover the order - makes parseResult()'s life easier.
\r
260 jalview.analysis.AlignmentSorter.recoverOrder(alseqs);
\r
261 // account for any missing sequences
\r
262 jalview.analysis.SeqsetUtils.deuniquify(SeqNames, alseqs);
\r
263 return new Object[]
\r
264 { alseqs, msaorder };
\r
270 * mark subjob as cancelled and set result object appropriatly
\r
275 subjobComplete = true;
\r
281 * @return boolean true if job can be submitted.
\r
283 boolean hasValidInput()
\r
285 if (seqs.getSeqs() != null)
\r
293 String alTitle; // name which will be used to form new alignment window.
\r
295 Alignment dataset; // dataset to which the new alignment will be
\r
299 ext.vamsas.MuscleWS server = null;
\r
302 * set basic options for this (group) of Msa jobs
\r
309 MsaWSThread(ext.vamsas.MuscleWS server, String wsUrl,
\r
310 WebserviceInfo wsinfo, jalview.gui.AlignFrame alFrame,
\r
311 AlignmentView alview, String wsname, boolean subgaps,
\r
314 super(alFrame, wsinfo, alview, wsname, wsUrl);
\r
315 this.server = server;
\r
316 this.submitGaps = subgaps;
\r
317 this.preserveOrder = presorder;
\r
321 * create one or more Msa jobs to align visible seuqences in _msa
\r
334 MsaWSThread(ext.vamsas.MuscleWS server, String wsUrl,
\r
335 WebserviceInfo wsinfo, jalview.gui.AlignFrame alFrame,
\r
336 String wsname, String title, AlignmentView _msa, boolean subgaps,
\r
337 boolean presorder, Alignment seqset)
\r
339 this(server, wsUrl, wsinfo, alFrame, _msa, wsname, subgaps, presorder);
\r
340 OutputHeader = wsInfo.getProgressText();
\r
344 SequenceI[][] conmsa = _msa.getVisibleContigs('-');
\r
345 if (conmsa != null)
\r
347 int njobs = conmsa.length;
\r
348 jobs = new MsaWSJob[njobs];
\r
349 for (int j = 0; j < njobs; j++)
\r
353 jobs[j] = new MsaWSJob(wsinfo.addJobPane(), conmsa[j]);
\r
357 jobs[j] = new MsaWSJob(0, conmsa[j]);
\r
362 .setProgressName("region " + jobs[j].jobnum,
\r
365 wsinfo.setProgressText(jobs[j].jobnum, OutputHeader);
\r
370 public boolean isCancellable()
\r
375 public void cancelJob()
\r
377 if (!jobComplete && jobs != null)
\r
379 boolean cancelled = true;
\r
380 for (int job = 0; job < jobs.length; job++)
\r
382 if (jobs[job].submitted && !jobs[job].subjobComplete)
\r
384 String cancelledMessage = "";
\r
387 vamsas.objects.simple.WsJobId cancelledJob = server
\r
388 .cancel(jobs[job].jobId);
\r
389 if (cancelledJob.getStatus() == 2)
\r
392 cancelledMessage = "Job cancelled.";
\r
393 ((MsaWSJob) jobs[job]).cancel();
\r
394 wsInfo.setStatus(jobs[job].jobnum,
\r
395 WebserviceInfo.STATE_CANCELLED_OK);
\r
397 else if (cancelledJob.getStatus() == 3)
\r
399 // VALID UNSTOPPABLE JOB
\r
400 cancelledMessage += "Server cannot cancel this job. just close the window.\n";
\r
402 // wsInfo.setStatus(jobs[job].jobnum,
\r
403 // WebserviceInfo.STATE_RUNNING);
\r
406 if (cancelledJob.getJobId() != null)
\r
408 cancelledMessage += ("[" + cancelledJob.getJobId() + "]");
\r
411 cancelledMessage += "\n";
\r
412 } catch (Exception exc)
\r
414 cancelledMessage += ("\nProblems cancelling the job : Exception received...\n"
\r
417 "Exception whilst cancelling " + jobs[job].jobId, exc);
\r
419 wsInfo.setProgressText(jobs[job].jobnum, OutputHeader
\r
420 + cancelledMessage + "\n");
\r
425 wsInfo.setStatus(WebserviceInfo.STATE_CANCELLED_OK);
\r
426 jobComplete = true;
\r
428 this.interrupt(); // kick thread to update job states.
\r
435 .setProgressText(OutputHeader
\r
436 + "Server cannot cancel this job because it has not been submitted properly. just close the window.\n");
\r
441 void pollJob(WSJob job) throws Exception
\r
443 ((MsaWSJob) job).result = server.getResult(((MsaWSJob) job).jobId);
\r
446 void StartJob(WSJob job)
\r
448 if (!(job instanceof MsaWSJob))
\r
450 throw new Error("StartJob(MsaWSJob) called on a WSJobInstance "
\r
453 MsaWSJob j = (MsaWSJob) job;
\r
456 if (Cache.log.isDebugEnabled())
\r
458 Cache.log.debug("Tried to submit an already submitted job "
\r
463 if (j.seqs.getSeqs() == null)
\r
465 // special case - selection consisted entirely of empty sequences...
\r
466 j.submitted = true;
\r
467 j.result = new MsaResult();
\r
468 j.result.setFinished(true);
\r
469 j.result.setStatus("Empty Alignment Job");
\r
470 ((MsaResult) j.result).setMsa(null);
\r
474 vamsas.objects.simple.WsJobId jobsubmit = server.align(j.seqs);
\r
476 if ((jobsubmit != null) && (jobsubmit.getStatus() == 1))
\r
478 j.jobId = jobsubmit.getJobId();
\r
479 j.submitted = true;
\r
480 j.subjobComplete = false;
\r
481 // System.out.println(WsURL + " Job Id '" + jobId + "'");
\r
485 if (jobsubmit == null)
\r
487 throw new Exception(
\r
490 + " returned null object, it probably cannot be contacted. Try again later ?");
\r
493 throw new Exception(jobsubmit.getJobId());
\r
495 } catch (Exception e)
\r
497 // TODO: JBPNote catch timeout or other fault types explicitly
\r
498 // For unexpected errors
\r
500 .println(WebServiceName
\r
501 + "Client: Failed to submit the sequences for alignment (probably a server side problem)\n"
\r
502 + "When contacting Server:" + WsUrl + "\n"
\r
503 + e.toString() + "\n");
\r
504 j.allowedServerExceptions = 0;
\r
505 wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_SERVERERROR);
\r
506 wsInfo.setStatus(j.jobnum, WebserviceInfo.STATE_STOPPED_SERVERERROR);
\r
508 .appendProgressText(
\r
510 "Failed to submit sequences for alignment.\n"
\r
511 + "It is most likely that there is a problem with the server.\n"
\r
512 + "Just close the window\n");
\r
514 // e.printStackTrace(); // TODO: JBPNote DEBUG
\r
518 private jalview.datamodel.Sequence[] getVamsasAlignment(
\r
519 vamsas.objects.simple.Alignment valign)
\r
521 // TODO: refactor to helper class for vamsas.objects.simple objects
\r
522 vamsas.objects.simple.Sequence[] seqs = valign.getSeqs().getSeqs();
\r
523 jalview.datamodel.Sequence[] msa = new jalview.datamodel.Sequence[seqs.length];
\r
525 for (int i = 0, j = seqs.length; i < j; i++)
\r
527 msa[i] = new jalview.datamodel.Sequence(seqs[i].getId(), seqs[i]
\r
536 int results = 0; // number of result sets received
\r
537 JobStateSummary finalState = new JobStateSummary();
\r
540 for (int j = 0; j < jobs.length; j++)
\r
542 finalState.updateJobPanelState(wsInfo, OutputHeader, jobs[j]);
\r
543 if (jobs[j].submitted && jobs[j].subjobComplete
\r
544 && jobs[j].hasResults())
\r
547 vamsas.objects.simple.Alignment valign = ((MsaResult) jobs[j].result)
\r
549 if (valign != null)
\r
551 wsInfo.appendProgressText(jobs[j].jobnum,
\r
552 "\nAlignment Object Method Notes\n");
\r
553 String[] lines = valign.getMethod();
\r
554 for (int line = 0; line < lines.length; line++)
\r
556 wsInfo.appendProgressText(jobs[j].jobnum, lines[line] + "\n");
\r
558 // JBPNote The returned files from a webservice could be
\r
559 // hidden behind icons in the monitor window that,
\r
560 // when clicked, pop up their corresponding data
\r
564 } catch (Exception ex)
\r
567 Cache.log.error("Unexpected exception when processing results for "
\r
569 wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_ERROR);
\r
573 wsInfo.showResultsNewFrame
\r
574 .addActionListener(new java.awt.event.ActionListener()
\r
576 public void actionPerformed(java.awt.event.ActionEvent evt)
\r
578 displayResults(true);
\r
581 wsInfo.mergeResults
\r
582 .addActionListener(new java.awt.event.ActionListener()
\r
584 public void actionPerformed(java.awt.event.ActionEvent evt)
\r
586 displayResults(false);
\r
589 wsInfo.setResultsReady();
\r
593 wsInfo.setFinishedNoResults();
\r
597 void displayResults(boolean newFrame)
\r
599 // view input or result data for each block
\r
600 Vector alorders = new Vector();
\r
601 SequenceI[][] results = new SequenceI[jobs.length][];
\r
602 AlignmentOrder[] orders = new AlignmentOrder[jobs.length];
\r
603 for (int j = 0; j < jobs.length; j++)
\r
605 if (jobs[j].hasResults())
\r
607 Object[] res = ((MsaWSJob) jobs[j]).getAlignment();
\r
608 alorders.add(res[1]);
\r
609 results[j] = (SequenceI[]) res[0];
\r
610 orders[j] = (AlignmentOrder) res[1];
\r
612 // SequenceI[] alignment = input.getUpdated
\r
619 Object[] newview = input.getUpdatedView(results, orders, getGapChar());
\r
620 // trash references to original result data
\r
621 for (int j = 0; j < jobs.length; j++)
\r
626 SequenceI[] alignment = (SequenceI[]) newview[0];
\r
627 ColumnSelection columnselection = (ColumnSelection) newview[1];
\r
628 Alignment al = new Alignment(alignment);
\r
629 // TODO: add 'provenance' property to alignment from the method notes
\r
630 // accompanying each subjob
\r
631 if (dataset != null)
\r
633 al.setDataset(dataset);
\r
636 propagateDatasetMappings(al);
\r
637 // JBNote- TODO: warn user if a block is input rather than aligned data ?
\r
641 AlignFrame af = new AlignFrame(al, columnselection,
\r
642 AlignFrame.DEFAULT_WIDTH, AlignFrame.DEFAULT_HEIGHT);
\r
644 // initialise with same renderer settings as in parent alignframe.
\r
645 af.getFeatureRenderer().transferSettings(this.featureSettings);
\r
647 if (alorders.size() > 0)
\r
649 if (alorders.size() == 1)
\r
651 af.addSortByOrderMenuItem(WebServiceName + " Ordering",
\r
652 (AlignmentOrder) alorders.get(0));
\r
656 // construct a non-redundant ordering set
\r
657 Vector names = new Vector();
\r
658 for (int i = 0, l = alorders.size(); i < l; i++)
\r
660 String orderName = new String(" Region " + i);
\r
665 if (((AlignmentOrder) alorders.get(i))
\r
666 .equals(((AlignmentOrder) alorders.get(j))))
\r
668 alorders.remove(j);
\r
670 orderName += "," + j;
\r
678 if (i == 0 && j == 1)
\r
680 names.add(new String(""));
\r
684 names.add(orderName);
\r
687 for (int i = 0, l = alorders.size(); i < l; i++)
\r
689 af.addSortByOrderMenuItem(WebServiceName
\r
690 + ((String) names.get(i)) + " Ordering",
\r
691 (AlignmentOrder) alorders.get(i));
\r
696 Desktop.addInternalFrame(af, alTitle, AlignFrame.DEFAULT_WIDTH,
\r
697 AlignFrame.DEFAULT_HEIGHT);
\r
702 System.out.println("MERGE WITH OLD FRAME");
\r
703 // TODO: modify alignment in original frame, replacing old for new
\r
704 // alignment using the commands.EditCommand model to ensure the update can
\r
709 public boolean canMergeResults()
\r