2 * Jalview - A Sequence Alignment Editor and Viewer (Development Version 2.4.1)
\r
3 * Copyright (C) 2009 AM Waterhouse, J Procter, G Barton, M Clamp, S Searle
\r
5 * This program is free software; you can redistribute it and/or
\r
6 * modify it under the terms of the GNU General Public License
\r
7 * as published by the Free Software Foundation; either version 2
\r
8 * of the License, or (at your option) any later version.
\r
10 * This program is distributed in the hope that it will be useful,
\r
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
\r
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
\r
13 * GNU General Public License for more details.
\r
15 * You should have received a copy of the GNU General Public License
\r
16 * along with this program; if not, write to the Free Software
\r
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
\r
23 import jalview.analysis.*;
\r
24 import jalview.bin.*;
\r
25 import jalview.datamodel.*;
\r
26 import jalview.datamodel.Alignment;
\r
27 import jalview.gui.*;
\r
28 import vamsas.objects.simple.MsaResult;
\r
40 * Copyright: Copyright (c) 2004
\r
44 * Company: Dundee University
\r
47 * @author not attributable
\r
50 class MsaWSThread extends WSThread implements WSClientI
\r
52 boolean submitGaps = false; // pass sequences including gaps to alignment
\r
56 boolean preserveOrder = true; // and always store and recover sequence
\r
60 class MsaWSJob extends WSThread.WSJob
\r
62 // hold special input for this
\r
63 vamsas.objects.simple.SequenceSet seqs = new vamsas.objects.simple.SequenceSet();
\r
73 public MsaWSJob(int jobNum, SequenceI[] inSeqs)
\r
75 this.jobnum = jobNum;
\r
76 if (!prepareInput(inSeqs, 2))
\r
79 subjobComplete = true;
\r
80 result = new MsaResult();
\r
81 result.setFinished(true);
\r
82 result.setStatus("Job never ran - input returned to user.");
\r
87 Hashtable SeqNames = new Hashtable();
\r
89 Vector emptySeqs = new Vector();
\r
92 * prepare input sequences for MsaWS service
\r
95 * jalview sequences to be prepared
\r
97 * minimum number of residues required for this MsaWS service
\r
98 * @return true if seqs contains sequences to be submitted to service.
\r
100 private boolean prepareInput(SequenceI[] seqs, int minlen)
\r
106 "Implementation error: minlen must be zero or more.");
\r
108 for (int i = 0; i < seqs.length; i++)
\r
110 if (seqs[i].getEnd() - seqs[i].getStart() > minlen - 1)
\r
115 boolean valid = nseqs > 1; // need at least two seqs
\r
116 vamsas.objects.simple.Sequence[] seqarray = (valid) ? new vamsas.objects.simple.Sequence[nseqs]
\r
118 for (int i = 0, n = 0; i < seqs.length; i++)
\r
121 String newname = jalview.analysis.SeqsetUtils.unique_name(i); // same
\r
125 SeqNames.put(newname, jalview.analysis.SeqsetUtils
\r
126 .SeqCharacterHash(seqs[i]));
\r
127 if (valid && seqs[i].getEnd() - seqs[i].getStart() > minlen - 1)
\r
129 seqarray[n] = new vamsas.objects.simple.Sequence();
\r
130 seqarray[n].setId(newname);
\r
131 seqarray[n++].setSeq((submitGaps) ? seqs[i].getSequenceAsString()
\r
132 : AlignSeq.extractGaps(jalview.util.Comparison.GapChars,
\r
133 seqs[i].getSequenceAsString()));
\r
137 String empty = null;
\r
138 if (seqs[i].getEnd() >= seqs[i].getStart())
\r
140 empty = (submitGaps) ? seqs[i].getSequenceAsString() : AlignSeq
\r
141 .extractGaps(jalview.util.Comparison.GapChars, seqs[i]
\r
142 .getSequenceAsString());
\r
144 emptySeqs.add(new String[]
\r
145 { newname, empty });
\r
148 this.seqs = new vamsas.objects.simple.SequenceSet();
\r
149 this.seqs.setSeqs(seqarray);
\r
155 * @return true if getAlignment will return a valid alignment result.
\r
157 public boolean hasResults()
\r
159 if (subjobComplete && result != null && result.isFinished()
\r
160 && ((MsaResult) result).getMsa() != null
\r
161 && ((MsaResult) result).getMsa().getSeqs() != null)
\r
168 public Object[] getAlignment()
\r
171 if (result != null && result.isFinished())
\r
173 SequenceI[] alseqs = null;
\r
174 char alseq_gapchar = '-';
\r
176 if (((MsaResult) result).getMsa() != null)
\r
178 alseqs = getVamsasAlignment(((MsaResult) result).getMsa());
\r
179 alseq_gapchar = ((MsaResult) result).getMsa().getGapchar()
\r
181 alseq_l = alseqs.length;
\r
183 if (emptySeqs.size() > 0)
\r
185 SequenceI[] t_alseqs = new SequenceI[alseq_l + emptySeqs.size()];
\r
190 for (i = 0, w = alseqs[0].getLength(); i < alseq_l; i++)
\r
192 if (w < alseqs[i].getLength())
\r
194 w = alseqs[i].getLength();
\r
196 t_alseqs[i] = alseqs[i];
\r
200 // check that aligned width is at least as wide as emptySeqs width.
\r
201 int ow = w, nw = w;
\r
202 for (i = 0, w = emptySeqs.size(); i < w; i++)
\r
204 String[] es = (String[]) emptySeqs.get(i);
\r
205 if (es != null && es[1] != null)
\r
207 int sw = es[1].length();
\r
214 // make a gapped string.
\r
215 StringBuffer insbuff = new StringBuffer(w);
\r
216 for (i = 0; i < nw; i++)
\r
218 insbuff.append(alseq_gapchar);
\r
222 for (i = 0; i < alseq_l; i++)
\r
224 int sw = t_alseqs[i].getLength();
\r
228 alseqs[i].setSequence(t_alseqs[i].getSequenceAsString()
\r
229 + insbuff.substring(0, sw - nw));
\r
233 for (i = 0, w = emptySeqs.size(); i < w; i++)
\r
235 String[] es = (String[]) emptySeqs.get(i);
\r
238 t_alseqs[i + alseq_l] = new jalview.datamodel.Sequence(es[0],
\r
239 insbuff.toString(), 1, 0);
\r
243 if (es[1].length() < nw)
\r
245 t_alseqs[i + alseq_l] = new jalview.datamodel.Sequence(
\r
247 es[1] + insbuff.substring(0, nw - es[1].length()),
\r
248 1, 1 + es[1].length());
\r
252 t_alseqs[i + alseq_l] = new jalview.datamodel.Sequence(
\r
259 AlignmentOrder msaorder = new AlignmentOrder(alseqs);
\r
260 // always recover the order - makes parseResult()'s life easier.
\r
261 jalview.analysis.AlignmentSorter.recoverOrder(alseqs);
\r
262 // account for any missing sequences
\r
263 jalview.analysis.SeqsetUtils.deuniquify(SeqNames, alseqs);
\r
264 return new Object[]
\r
265 { alseqs, msaorder };
\r
271 * mark subjob as cancelled and set result object appropriatly
\r
276 subjobComplete = true;
\r
282 * @return boolean true if job can be submitted.
\r
284 boolean hasValidInput()
\r
286 if (seqs.getSeqs() != null)
\r
294 String alTitle; // name which will be used to form new alignment window.
\r
296 Alignment dataset; // dataset to which the new alignment will be
\r
300 ext.vamsas.MuscleWS server = null;
\r
303 * set basic options for this (group) of Msa jobs
\r
310 MsaWSThread(ext.vamsas.MuscleWS server, String wsUrl,
\r
311 WebserviceInfo wsinfo, jalview.gui.AlignFrame alFrame,
\r
312 AlignmentView alview, String wsname, boolean subgaps,
\r
315 super(alFrame, wsinfo, alview, wsname, wsUrl);
\r
316 this.server = server;
\r
317 this.submitGaps = subgaps;
\r
318 this.preserveOrder = presorder;
\r
322 * create one or more Msa jobs to align visible seuqences in _msa
\r
335 MsaWSThread(ext.vamsas.MuscleWS server, String wsUrl,
\r
336 WebserviceInfo wsinfo, jalview.gui.AlignFrame alFrame,
\r
337 String wsname, String title, AlignmentView _msa, boolean subgaps,
\r
338 boolean presorder, Alignment seqset)
\r
340 this(server, wsUrl, wsinfo, alFrame, _msa, wsname, subgaps, presorder);
\r
341 OutputHeader = wsInfo.getProgressText();
\r
345 SequenceI[][] conmsa = _msa.getVisibleContigs('-');
\r
346 if (conmsa != null)
\r
348 int njobs = conmsa.length;
\r
349 jobs = new MsaWSJob[njobs];
\r
350 for (int j = 0; j < njobs; j++)
\r
354 jobs[j] = new MsaWSJob(wsinfo.addJobPane(), conmsa[j]);
\r
358 jobs[j] = new MsaWSJob(0, conmsa[j]);
\r
363 .setProgressName("region " + jobs[j].jobnum,
\r
366 wsinfo.setProgressText(jobs[j].jobnum, OutputHeader);
\r
371 public boolean isCancellable()
\r
376 public void cancelJob()
\r
378 if (!jobComplete && jobs != null)
\r
380 boolean cancelled = true;
\r
381 for (int job = 0; job < jobs.length; job++)
\r
383 if (jobs[job].submitted && !jobs[job].subjobComplete)
\r
385 String cancelledMessage = "";
\r
388 vamsas.objects.simple.WsJobId cancelledJob = server
\r
389 .cancel(jobs[job].jobId);
\r
390 if (cancelledJob.getStatus() == 2)
\r
393 cancelledMessage = "Job cancelled.";
\r
394 ((MsaWSJob) jobs[job]).cancel();
\r
395 wsInfo.setStatus(jobs[job].jobnum,
\r
396 WebserviceInfo.STATE_CANCELLED_OK);
\r
398 else if (cancelledJob.getStatus() == 3)
\r
400 // VALID UNSTOPPABLE JOB
\r
401 cancelledMessage += "Server cannot cancel this job. just close the window.\n";
\r
403 // wsInfo.setStatus(jobs[job].jobnum,
\r
404 // WebserviceInfo.STATE_RUNNING);
\r
407 if (cancelledJob.getJobId() != null)
\r
409 cancelledMessage += ("[" + cancelledJob.getJobId() + "]");
\r
412 cancelledMessage += "\n";
\r
413 } catch (Exception exc)
\r
415 cancelledMessage += ("\nProblems cancelling the job : Exception received...\n"
\r
418 "Exception whilst cancelling " + jobs[job].jobId, exc);
\r
420 wsInfo.setProgressText(jobs[job].jobnum, OutputHeader
\r
421 + cancelledMessage + "\n");
\r
426 wsInfo.setStatus(WebserviceInfo.STATE_CANCELLED_OK);
\r
427 jobComplete = true;
\r
429 this.interrupt(); // kick thread to update job states.
\r
436 .setProgressText(OutputHeader
\r
437 + "Server cannot cancel this job because it has not been submitted properly. just close the window.\n");
\r
442 void pollJob(WSJob job) throws Exception
\r
444 ((MsaWSJob) job).result = server.getResult(((MsaWSJob) job).jobId);
\r
447 void StartJob(WSJob job)
\r
449 if (!(job instanceof MsaWSJob))
\r
451 throw new Error("StartJob(MsaWSJob) called on a WSJobInstance "
\r
454 MsaWSJob j = (MsaWSJob) job;
\r
457 if (Cache.log.isDebugEnabled())
\r
459 Cache.log.debug("Tried to submit an already submitted job "
\r
464 if (j.seqs.getSeqs() == null)
\r
466 // special case - selection consisted entirely of empty sequences...
\r
467 j.submitted = true;
\r
468 j.result = new MsaResult();
\r
469 j.result.setFinished(true);
\r
470 j.result.setStatus("Empty Alignment Job");
\r
471 ((MsaResult) j.result).setMsa(null);
\r
475 vamsas.objects.simple.WsJobId jobsubmit = server.align(j.seqs);
\r
477 if ((jobsubmit != null) && (jobsubmit.getStatus() == 1))
\r
479 j.jobId = jobsubmit.getJobId();
\r
480 j.submitted = true;
\r
481 j.subjobComplete = false;
\r
482 // System.out.println(WsURL + " Job Id '" + jobId + "'");
\r
486 if (jobsubmit == null)
\r
488 throw new Exception(
\r
491 + " returned null object, it probably cannot be contacted. Try again later ?");
\r
494 throw new Exception(jobsubmit.getJobId());
\r
496 } catch (Exception e)
\r
498 // TODO: JBPNote catch timeout or other fault types explicitly
\r
499 // For unexpected errors
\r
501 .println(WebServiceName
\r
502 + "Client: Failed to submit the sequences for alignment (probably a server side problem)\n"
\r
503 + "When contacting Server:" + WsUrl + "\n"
\r
504 + e.toString() + "\n");
\r
505 j.allowedServerExceptions = 0;
\r
506 wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_SERVERERROR);
\r
507 wsInfo.setStatus(j.jobnum, WebserviceInfo.STATE_STOPPED_SERVERERROR);
\r
509 .appendProgressText(
\r
511 "Failed to submit sequences for alignment.\n"
\r
512 + "It is most likely that there is a problem with the server.\n"
\r
513 + "Just close the window\n");
\r
515 // e.printStackTrace(); // TODO: JBPNote DEBUG
\r
519 private jalview.datamodel.Sequence[] getVamsasAlignment(
\r
520 vamsas.objects.simple.Alignment valign)
\r
522 // TODO: refactor to helper class for vamsas.objects.simple objects
\r
523 vamsas.objects.simple.Sequence[] seqs = valign.getSeqs().getSeqs();
\r
524 jalview.datamodel.Sequence[] msa = new jalview.datamodel.Sequence[seqs.length];
\r
526 for (int i = 0, j = seqs.length; i < j; i++)
\r
528 msa[i] = new jalview.datamodel.Sequence(seqs[i].getId(), seqs[i]
\r
537 int results = 0; // number of result sets received
\r
538 JobStateSummary finalState = new JobStateSummary();
\r
541 for (int j = 0; j < jobs.length; j++)
\r
543 finalState.updateJobPanelState(wsInfo, OutputHeader, jobs[j]);
\r
544 if (jobs[j].submitted && jobs[j].subjobComplete
\r
545 && jobs[j].hasResults())
\r
548 vamsas.objects.simple.Alignment valign = ((MsaResult) jobs[j].result)
\r
550 if (valign != null)
\r
552 wsInfo.appendProgressText(jobs[j].jobnum,
\r
553 "\nAlignment Object Method Notes\n");
\r
554 String[] lines = valign.getMethod();
\r
555 for (int line = 0; line < lines.length; line++)
\r
557 wsInfo.appendProgressText(jobs[j].jobnum, lines[line] + "\n");
\r
559 // JBPNote The returned files from a webservice could be
\r
560 // hidden behind icons in the monitor window that,
\r
561 // when clicked, pop up their corresponding data
\r
565 } catch (Exception ex)
\r
568 Cache.log.error("Unexpected exception when processing results for "
\r
570 wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_ERROR);
\r
574 wsInfo.showResultsNewFrame
\r
575 .addActionListener(new java.awt.event.ActionListener()
\r
577 public void actionPerformed(java.awt.event.ActionEvent evt)
\r
579 displayResults(true);
\r
582 wsInfo.mergeResults
\r
583 .addActionListener(new java.awt.event.ActionListener()
\r
585 public void actionPerformed(java.awt.event.ActionEvent evt)
\r
587 displayResults(false);
\r
590 wsInfo.setResultsReady();
\r
594 wsInfo.setFinishedNoResults();
\r
598 void displayResults(boolean newFrame)
\r
600 // view input or result data for each block
\r
601 Vector alorders = new Vector();
\r
602 SequenceI[][] results = new SequenceI[jobs.length][];
\r
603 AlignmentOrder[] orders = new AlignmentOrder[jobs.length];
\r
604 for (int j = 0; j < jobs.length; j++)
\r
606 if (jobs[j].hasResults())
\r
608 Object[] res = ((MsaWSJob) jobs[j]).getAlignment();
\r
609 alorders.add(res[1]);
\r
610 results[j] = (SequenceI[]) res[0];
\r
611 orders[j] = (AlignmentOrder) res[1];
\r
613 // SequenceI[] alignment = input.getUpdated
\r
620 Object[] newview = input.getUpdatedView(results, orders, getGapChar());
\r
621 // trash references to original result data
\r
622 for (int j = 0; j < jobs.length; j++)
\r
627 SequenceI[] alignment = (SequenceI[]) newview[0];
\r
628 ColumnSelection columnselection = (ColumnSelection) newview[1];
\r
629 Alignment al = new Alignment(alignment);
\r
630 // TODO: add 'provenance' property to alignment from the method notes
\r
631 // accompanying each subjob
\r
632 if (dataset != null)
\r
634 al.setDataset(dataset);
\r
637 propagateDatasetMappings(al);
\r
638 // JBNote- TODO: warn user if a block is input rather than aligned data ?
\r
642 AlignFrame af = new AlignFrame(al, columnselection,
\r
643 AlignFrame.DEFAULT_WIDTH, AlignFrame.DEFAULT_HEIGHT);
\r
645 // initialise with same renderer settings as in parent alignframe.
\r
646 af.getFeatureRenderer().transferSettings(this.featureSettings);
\r
648 if (alorders.size() > 0)
\r
650 if (alorders.size() == 1)
\r
652 af.addSortByOrderMenuItem(WebServiceName + " Ordering",
\r
653 (AlignmentOrder) alorders.get(0));
\r
657 // construct a non-redundant ordering set
\r
658 Vector names = new Vector();
\r
659 for (int i = 0, l = alorders.size(); i < l; i++)
\r
661 String orderName = new String(" Region " + i);
\r
666 if (((AlignmentOrder) alorders.get(i))
\r
667 .equals(((AlignmentOrder) alorders.get(j))))
\r
669 alorders.remove(j);
\r
671 orderName += "," + j;
\r
679 if (i == 0 && j == 1)
\r
681 names.add(new String(""));
\r
685 names.add(orderName);
\r
688 for (int i = 0, l = alorders.size(); i < l; i++)
\r
690 af.addSortByOrderMenuItem(WebServiceName
\r
691 + ((String) names.get(i)) + " Ordering",
\r
692 (AlignmentOrder) alorders.get(i));
\r
697 Desktop.addInternalFrame(af, alTitle, AlignFrame.DEFAULT_WIDTH,
\r
698 AlignFrame.DEFAULT_HEIGHT);
\r
703 System.out.println("MERGE WITH OLD FRAME");
\r
704 // TODO: modify alignment in original frame, replacing old for new
\r
705 // alignment using the commands.EditCommand model to ensure the update can
\r
710 public boolean canMergeResults()
\r