2 * Jalview - A Sequence Alignment Editor and Viewer (Version 2.8.0b1)
3 * Copyright (C) 2014 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 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/>.
17 * The Jalview Authors are detailed in the 'AUTHORS' file.
19 package jalview.ws.jws1;
23 import jalview.analysis.*;
25 import jalview.datamodel.*;
27 import jalview.io.NewickFile;
28 import jalview.ws.AWsJob;
29 import jalview.ws.JobStateSummary;
30 import jalview.ws.WSClientI;
31 import vamsas.objects.simple.MsaResult;
32 import vamsas.objects.simple.SeqSearchResult;
34 class SeqSearchWSThread extends JWS1Thread implements WSClientI
38 boolean profile = false;
40 class SeqSearchWSJob extends WSJob
42 // hold special input for this
43 vamsas.objects.simple.SequenceSet seqs = new vamsas.objects.simple.SequenceSet();
53 public SeqSearchWSJob(int jobNum, SequenceI[] inSeqs)
56 if (!prepareInput(inSeqs, 2))
59 subjobComplete = true;
60 result = new MsaResult();
61 result.setFinished(true);
62 result.setStatus("Job never ran - input returned to user.");
67 Hashtable SeqNames = new Hashtable();
69 Vector emptySeqs = new Vector();
72 * prepare input sequences for service
75 * jalview sequences to be prepared
77 * minimum number of residues required for this MsaWS service
78 * @return true if seqs contains sequences to be submitted to service.
80 private boolean prepareInput(SequenceI[] seqs, int minlen)
86 "Implementation error: minlen must be zero or more.");
88 for (int i = 0; i < seqs.length; i++)
90 if (seqs[i].getEnd() - seqs[i].getStart() > minlen - 1)
95 boolean valid = nseqs >= 1; // need at least one sequence for valid input
97 vamsas.objects.simple.Sequence[] seqarray = (valid) ? new vamsas.objects.simple.Sequence[nseqs]
99 boolean submitGaps = (nseqs == 1) ? false : true; // profile is submitted
101 for (int i = 0, n = 0; i < seqs.length; i++)
104 String newname = jalview.analysis.SeqsetUtils.unique_name(i); // same
108 SeqNames.put(newname,
109 jalview.analysis.SeqsetUtils.SeqCharacterHash(seqs[i]));
110 if (valid && seqs[i].getEnd() - seqs[i].getStart() > minlen - 1)
112 seqarray[n] = new vamsas.objects.simple.Sequence();
113 seqarray[n].setId(newname);
114 seqarray[n++].setSeq((submitGaps) ? seqs[i].getSequenceAsString()
115 : AlignSeq.extractGaps(jalview.util.Comparison.GapChars,
116 seqs[i].getSequenceAsString()));
121 if (seqs[i].getEnd() >= seqs[i].getStart())
123 empty = (submitGaps) ? seqs[i].getSequenceAsString() : AlignSeq
124 .extractGaps(jalview.util.Comparison.GapChars,
125 seqs[i].getSequenceAsString());
127 emptySeqs.add(new String[]
133 // almost certainly have to remove gapped columns here
135 this.seqs = new vamsas.objects.simple.SequenceSet();
136 this.seqs.setSeqs(seqarray);
142 * @return true if getAlignment will return a valid alignment result.
144 public boolean hasResults()
148 && result.isFinished()
149 && ((SeqSearchResult) result).getAlignment() != null
150 && ((SeqSearchResult) result).getAlignment().getSeqs() != null)
158 * return sequence search results for display
160 * @return null or { Alignment(+features and annotation), NewickFile)}
162 public Object[] getAlignment(Alignment dataset, Hashtable featureColours)
165 if (result != null && result.isFinished())
167 SequenceI[] alseqs = null;
168 // char alseq_gapchar = '-';
170 if (((SeqSearchResult) result).getAlignment() != null)
172 alseqs = getVamsasAlignment(((SeqSearchResult) result)
174 // alseq_gapchar = ( (SeqSearchResult)
175 // result).getAlignment().getGapchar().charAt(0);
176 // alseq_l = alseqs.length;
179 * what has to be done. 1 - annotate returned alignment with annotation
180 * file and sequence features file, and associate any tree-nodes. 2.
181 * connect alignment back to any associated dataset: 2.a. deuniquify
182 * recovers sequence information - but additionally, relocations must be
183 * made from the returned aligned sequence back to the dataset.
185 // construct annotated alignment as it would be done by the jalview
187 jalview.datamodel.Alignment al = new Alignment(alseqs);
188 // al.setDataset(dataset);
190 String inFile = null;
193 inFile = ((SeqSearchResult) result).getAnnotation();
194 if (inFile != null && inFile.length() > 0)
196 new jalview.io.AnnotationFile().readAnnotationFile(al, inFile,
197 jalview.io.AppletFormatAdapter.PASTE);
199 } catch (Exception e)
202 .println("Failed to parse the annotation file associated with the alignment.");
203 System.err.println(">>>EOF" + inFile + "\n<<<EOF\n");
204 e.printStackTrace(System.err);
209 inFile = ((SeqSearchResult) result).getFeatures();
210 if (inFile != null && inFile.length() > 0)
212 jalview.io.FeaturesFile ff = new jalview.io.FeaturesFile(
213 inFile, jalview.io.AppletFormatAdapter.PASTE);
214 ff.parse(al, featureColours, false);
216 } catch (Exception e)
219 .println("Failed to parse the Features file associated with the alignment.");
220 System.err.println(">>>EOF" + inFile + "\n<<<EOF\n");
221 e.printStackTrace(System.err);
223 jalview.io.NewickFile nf = null;
226 inFile = ((SeqSearchResult) result).getNewickTree();
227 if (inFile != null && inFile.length() > 0)
229 nf = new jalview.io.NewickFile(inFile,
230 jalview.io.AppletFormatAdapter.PASTE);
237 } catch (Exception e)
240 .println("Failed to parse the treeFile associated with the alignment.");
241 System.err.println(">>>EOF" + inFile + "\n<<<EOF\n");
242 e.printStackTrace(System.err);
246 * TODO: housekeeping w.r.t. recovery of dataset and annotation
247 * references for input sequences, and then dataset sequence creation
248 * for new sequences retrieved from service // finally, attempt to
249 * de-uniquify to recover input sequence identity, and try to map back
250 * onto dataset Note: this
251 * jalview.analysis.SeqsetUtils.deuniquify(SeqNames, alseqs, true); will
252 * NOT WORK - the returned alignment may contain multiple versions of
253 * the input sequence, each being a subsequence of the original.
254 * deuniquify also removes existing annotation and features added in the
255 * previous step... al.setDataset(dataset); // add in new sequences
256 * retrieved from sequence search which are not already in dataset. //
257 * trigger a 'fetchDBids' to annotate sequences with database ids...
267 * mark subjob as cancelled and set result object appropriatly
272 subjobComplete = true;
278 * @return boolean true if job can be submitted.
280 public boolean hasValidInput()
282 if (seqs.getSeqs() != null)
290 String alTitle; // name which will be used to form new alignment window.
292 Alignment dataset; // dataset to which the new alignment will be
296 ext.vamsas.SeqSearchI server = null;
298 private String dbArg;
301 * set basic options for this (group) of Msa jobs
308 SeqSearchWSThread(ext.vamsas.SeqSearchI server, String wsUrl,
309 WebserviceInfo wsinfo, jalview.gui.AlignFrame alFrame,
310 AlignmentView alview, String wsname, String db)
312 super(alFrame, wsinfo, alview, wsname, wsUrl);
313 this.server = server;
318 * create one or more Msa jobs to align visible seuqences in _msa
331 SeqSearchWSThread(ext.vamsas.SeqSearchI server, String wsUrl,
332 WebserviceInfo wsinfo, jalview.gui.AlignFrame alFrame,
333 String wsname, String title, AlignmentView _msa, String db,
336 this(server, wsUrl, wsinfo, alFrame, _msa, wsname, db);
337 OutputHeader = wsInfo.getProgressText();
341 SequenceI[][] conmsa = _msa.getVisibleContigs('-');
344 int njobs = conmsa.length;
345 jobs = new SeqSearchWSJob[njobs];
346 for (int j = 0; j < njobs; j++)
350 jobs[j] = new SeqSearchWSJob(wsinfo.addJobPane(), conmsa[j]);
354 jobs[j] = new SeqSearchWSJob(0, conmsa[j]);
358 wsinfo.setProgressName("region " + jobs[j].getJobnum(),
359 jobs[j].getJobnum());
361 wsinfo.setProgressText(jobs[j].getJobnum(), OutputHeader);
366 public boolean isCancellable()
371 public void cancelJob()
373 if (!jobComplete && jobs != null)
375 boolean cancelled = true;
376 for (int job = 0; job < jobs.length; job++)
378 if (jobs[job].isSubmitted() && !jobs[job].isSubjobComplete())
380 String cancelledMessage = "";
383 vamsas.objects.simple.WsJobId cancelledJob = server
384 .cancel(jobs[job].getJobId());
385 if (cancelledJob.getStatus() == 2)
388 cancelledMessage = "Job cancelled.";
389 ((SeqSearchWSJob) jobs[job]).cancel();
390 wsInfo.setStatus(jobs[job].getJobnum(),
391 WebserviceInfo.STATE_CANCELLED_OK);
393 else if (cancelledJob.getStatus() == 3)
395 // VALID UNSTOPPABLE JOB
396 cancelledMessage += "Server cannot cancel this job. just close the window.\n";
398 // wsInfo.setStatus(jobs[job].jobnum,
399 // WebserviceInfo.STATE_RUNNING);
402 if (cancelledJob.getJobId() != null)
404 cancelledMessage += ("[" + cancelledJob.getJobId() + "]");
407 cancelledMessage += "\n";
408 } catch (Exception exc)
410 cancelledMessage += ("\nProblems cancelling the job : Exception received...\n"
413 "Exception whilst cancelling " + jobs[job].getJobId(),
416 wsInfo.setProgressText(jobs[job].getJobnum(), OutputHeader
417 + cancelledMessage + "\n");
422 wsInfo.setStatus(WebserviceInfo.STATE_CANCELLED_OK);
425 this.interrupt(); // kick thread to update job states.
431 wsInfo.setProgressText(OutputHeader
432 + "Server cannot cancel this job because it has not been submitted properly. just close the window.\n");
437 public void pollJob(AWsJob job) throws Exception
439 ((SeqSearchWSJob) job).result = server.getResult(((SeqSearchWSJob) job)
443 public void StartJob(AWsJob job)
445 if (!(job instanceof SeqSearchWSJob))
447 throw new Error("StartJob(MsaWSJob) called on a WSJobInstance "
450 SeqSearchWSJob j = (SeqSearchWSJob) job;
453 if (Cache.log.isDebugEnabled())
455 Cache.log.debug("Tried to submit an already submitted job "
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);
466 j.result.setStatus("Empty Alignment Job");
467 ((MsaResult) j.result).setMsa(null);
471 vamsas.objects.simple.WsJobId jobsubmit = server.search(
472 j.seqs.getSeqs()[0], dbArg);
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)
488 + " returned null object, it probably cannot be contacted. Try again 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
498 .println(WebServiceName
499 + "Client: Failed to submit the sequences for alignment (probably a server side problem)\n"
500 + "When contacting Server:" + WsUrl + "\n"
501 + e.toString() + "\n");
502 j.setAllowedServerExceptions(0);
503 wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_SERVERERROR);
504 wsInfo.setStatus(j.getJobnum(),
505 WebserviceInfo.STATE_STOPPED_SERVERERROR);
506 wsInfo.appendProgressText(
508 "Failed to submit sequences for alignment.\n"
509 + "It is most likely that there is a problem with the server.\n"
510 + "Just close the window\n");
512 // e.printStackTrace(); // TODO: JBPNote DEBUG
516 private jalview.datamodel.Sequence[] getVamsasAlignment(
517 vamsas.objects.simple.Alignment valign)
519 vamsas.objects.simple.Sequence[] seqs = valign.getSeqs().getSeqs();
520 jalview.datamodel.Sequence[] msa = new jalview.datamodel.Sequence[seqs.length];
522 for (int i = 0, j = seqs.length; i < j; i++)
524 msa[i] = new jalview.datamodel.Sequence(seqs[i].getId(),
531 public void parseResult()
533 int results = 0; // number of result sets received
534 JobStateSummary finalState = new JobStateSummary();
537 for (int j = 0; j < jobs.length; j++)
539 finalState.updateJobPanelState(wsInfo, OutputHeader, jobs[j]);
540 if (jobs[j].isSubmitted() && jobs[j].isSubjobComplete()
541 && jobs[j].hasResults())
544 vamsas.objects.simple.Alignment valign = ((SeqSearchResult) ((SeqSearchWSJob) jobs[j]).result)
548 wsInfo.appendProgressText(jobs[j].getJobnum(),
549 "\nAlignment Object Method Notes\n");
550 String[] lines = valign.getMethod();
551 for (int line = 0; line < lines.length; line++)
553 wsInfo.appendProgressText(jobs[j].getJobnum(), lines[line]
556 // JBPNote The returned files from a webservice could be
557 // hidden behind icons in the monitor window that,
558 // 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)
599 System.err.println("MERGE WITH OLD FRAME NOT IMPLEMENTED");
602 // each subjob is an independent alignment for the moment
603 // Alignment al[] = new Alignment[jobs.length];
604 // NewickFile nf[] = new NewickFile[jobs.length];
605 for (int j = 0; j < jobs.length; j++)
607 Hashtable featureColours = new Hashtable();
609 NewickFile nf = null;
610 if (jobs[j].hasResults())
612 Object[] res = ((SeqSearchWSJob) jobs[j]).getAlignment(dataset,
619 al = (Alignment) res[0];
620 nf = (NewickFile) res[1];
629 * We can't map new alignment back with insertions from input's hidden
630 * regions until dataset mapping is sorted out... but basically it goes
631 * like this: 1. Merge each domain hit back onto the visible segments in
632 * the same way as a Jnet prediction is mapped back
634 * Object[] newview = input.getUpdatedView(results, orders, getGapChar());
635 * // trash references to original result data for (int j = 0; j <
636 * jobs.length; j++) { results[j] = null; orders[j] = null; } SequenceI[]
637 * alignment = (SequenceI[]) newview[0]; ColumnSelection columnselection =
638 * (ColumnSelection) newview[1]; Alignment al = new Alignment(alignment);
640 * if (dataset != null) { al.setDataset(dataset); }
642 * propagateDatasetMappings(al); }
645 AlignFrame af = new AlignFrame(al,// columnselection,
646 AlignFrame.DEFAULT_WIDTH, AlignFrame.DEFAULT_HEIGHT);
649 af.ShowNewickTree(nf, "Tree from " + this.alTitle);
651 // initialise with same renderer settings as in parent alignframe.
652 af.getFeatureRenderer().transferSettings(this.featureSettings);
653 Desktop.addInternalFrame(af, alTitle, AlignFrame.DEFAULT_WIDTH,
654 AlignFrame.DEFAULT_HEIGHT);
658 public boolean canMergeResults()