2 * Jalview - A Sequence Alignment Editor and Viewer (Version 2.8)
3 * Copyright (C) 2012 J Procter, AM Waterhouse, LM Lui, J Engelhardt, 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.io.NewickFile;
27 import jalview.ws.AWsJob;
28 import jalview.ws.JobStateSummary;
29 import jalview.ws.WSClientI;
30 import vamsas.objects.simple.MsaResult;
31 import vamsas.objects.simple.SeqSearchResult;
33 class SeqSearchWSThread extends JWS1Thread implements WSClientI
37 boolean profile = false;
39 class SeqSearchWSJob extends WSJob
41 // hold special input for this
42 vamsas.objects.simple.SequenceSet seqs = new vamsas.objects.simple.SequenceSet();
52 public SeqSearchWSJob(int jobNum, SequenceI[] inSeqs)
55 if (!prepareInput(inSeqs, 2))
58 subjobComplete = true;
59 result = new MsaResult();
60 result.setFinished(true);
61 result.setStatus("Job never ran - input returned to user.");
66 Hashtable SeqNames = new Hashtable();
68 Vector emptySeqs = new Vector();
71 * prepare input sequences for service
74 * jalview sequences to be prepared
76 * minimum number of residues required for this MsaWS service
77 * @return true if seqs contains sequences to be submitted to service.
79 private boolean prepareInput(SequenceI[] seqs, int minlen)
85 "Implementation error: minlen must be zero or more.");
87 for (int i = 0; i < seqs.length; i++)
89 if (seqs[i].getEnd() - seqs[i].getStart() > minlen - 1)
94 boolean valid = nseqs >= 1; // need at least one sequence for valid input
96 vamsas.objects.simple.Sequence[] seqarray = (valid) ? new vamsas.objects.simple.Sequence[nseqs]
98 boolean submitGaps = (nseqs == 1) ? false : true; // profile is submitted
100 for (int i = 0, n = 0; i < seqs.length; i++)
103 String newname = jalview.analysis.SeqsetUtils.unique_name(i); // same
107 SeqNames.put(newname,
108 jalview.analysis.SeqsetUtils.SeqCharacterHash(seqs[i]));
109 if (valid && seqs[i].getEnd() - seqs[i].getStart() > minlen - 1)
111 seqarray[n] = new vamsas.objects.simple.Sequence();
112 seqarray[n].setId(newname);
113 seqarray[n++].setSeq((submitGaps) ? seqs[i].getSequenceAsString()
114 : AlignSeq.extractGaps(jalview.util.Comparison.GapChars,
115 seqs[i].getSequenceAsString()));
120 if (seqs[i].getEnd() >= seqs[i].getStart())
122 empty = (submitGaps) ? seqs[i].getSequenceAsString() : AlignSeq
123 .extractGaps(jalview.util.Comparison.GapChars,
124 seqs[i].getSequenceAsString());
126 emptySeqs.add(new String[]
132 // almost certainly have to remove gapped columns here
134 this.seqs = new vamsas.objects.simple.SequenceSet();
135 this.seqs.setSeqs(seqarray);
141 * @return true if getAlignment will return a valid alignment result.
143 public boolean hasResults()
147 && result.isFinished()
148 && ((SeqSearchResult) result).getAlignment() != null
149 && ((SeqSearchResult) result).getAlignment().getSeqs() != null)
157 * return sequence search results for display
159 * @return null or { Alignment(+features and annotation), NewickFile)}
161 public Object[] getAlignment(Alignment dataset, Hashtable featureColours)
164 if (result != null && result.isFinished())
166 SequenceI[] alseqs = null;
167 // char alseq_gapchar = '-';
169 if (((SeqSearchResult) result).getAlignment() != null)
171 alseqs = getVamsasAlignment(((SeqSearchResult) result)
173 // alseq_gapchar = ( (SeqSearchResult)
174 // result).getAlignment().getGapchar().charAt(0);
175 // alseq_l = alseqs.length;
178 * what has to be done. 1 - annotate returned alignment with annotation
179 * file and sequence features file, and associate any tree-nodes. 2.
180 * connect alignment back to any associated dataset: 2.a. deuniquify
181 * recovers sequence information - but additionally, relocations must be
182 * made from the returned aligned sequence back to the dataset.
184 // construct annotated alignment as it would be done by the jalview
186 jalview.datamodel.Alignment al = new Alignment(alseqs);
187 // al.setDataset(dataset);
189 String inFile = null;
192 inFile = ((SeqSearchResult) result).getAnnotation();
193 if (inFile != null && inFile.length() > 0)
195 new jalview.io.AnnotationFile().readAnnotationFile(al, inFile,
196 jalview.io.AppletFormatAdapter.PASTE);
198 } catch (Exception e)
201 .println("Failed to parse the annotation file associated with the alignment.");
202 System.err.println(">>>EOF" + inFile + "\n<<<EOF\n");
203 e.printStackTrace(System.err);
208 inFile = ((SeqSearchResult) result).getFeatures();
209 if (inFile != null && inFile.length() > 0)
211 jalview.io.FeaturesFile ff = new jalview.io.FeaturesFile(
212 inFile, jalview.io.AppletFormatAdapter.PASTE);
213 ff.parse(al, featureColours, false);
215 } catch (Exception e)
218 .println("Failed to parse the Features file associated with the alignment.");
219 System.err.println(">>>EOF" + inFile + "\n<<<EOF\n");
220 e.printStackTrace(System.err);
222 jalview.io.NewickFile nf = null;
225 inFile = ((SeqSearchResult) result).getNewickTree();
226 if (inFile != null && inFile.length() > 0)
228 nf = new jalview.io.NewickFile(inFile,
229 jalview.io.AppletFormatAdapter.PASTE);
236 } catch (Exception e)
239 .println("Failed to parse the treeFile associated with the alignment.");
240 System.err.println(">>>EOF" + inFile + "\n<<<EOF\n");
241 e.printStackTrace(System.err);
245 * TODO: housekeeping w.r.t. recovery of dataset and annotation
246 * references for input sequences, and then dataset sequence creation
247 * for new sequences retrieved from service // finally, attempt to
248 * de-uniquify to recover input sequence identity, and try to map back
249 * onto dataset Note: this
250 * jalview.analysis.SeqsetUtils.deuniquify(SeqNames, alseqs, true); will
251 * NOT WORK - the returned alignment may contain multiple versions of
252 * the input sequence, each being a subsequence of the original.
253 * deuniquify also removes existing annotation and features added in the
254 * previous step... al.setDataset(dataset); // add in new sequences
255 * retrieved from sequence search which are not already in dataset. //
256 * trigger a 'fetchDBids' to annotate sequences with database ids...
266 * mark subjob as cancelled and set result object appropriatly
271 subjobComplete = true;
277 * @return boolean true if job can be submitted.
279 public boolean hasValidInput()
281 if (seqs.getSeqs() != null)
289 String alTitle; // name which will be used to form new alignment window.
291 Alignment dataset; // dataset to which the new alignment will be
295 ext.vamsas.SeqSearchI server = null;
297 private String dbArg;
300 * set basic options for this (group) of Msa jobs
307 SeqSearchWSThread(ext.vamsas.SeqSearchI server, String wsUrl,
308 WebserviceInfo wsinfo, jalview.gui.AlignFrame alFrame,
309 AlignmentView alview, String wsname, String db)
311 super(alFrame, wsinfo, alview, wsname, wsUrl);
312 this.server = server;
317 * create one or more Msa jobs to align visible seuqences in _msa
330 SeqSearchWSThread(ext.vamsas.SeqSearchI server, String wsUrl,
331 WebserviceInfo wsinfo, jalview.gui.AlignFrame alFrame,
332 String wsname, String title, AlignmentView _msa, String db,
335 this(server, wsUrl, wsinfo, alFrame, _msa, wsname, db);
336 OutputHeader = wsInfo.getProgressText();
340 SequenceI[][] conmsa = _msa.getVisibleContigs('-');
343 int njobs = conmsa.length;
344 jobs = new SeqSearchWSJob[njobs];
345 for (int j = 0; j < njobs; j++)
349 jobs[j] = new SeqSearchWSJob(wsinfo.addJobPane(), conmsa[j]);
353 jobs[j] = new SeqSearchWSJob(0, conmsa[j]);
357 wsinfo.setProgressName("region " + jobs[j].getJobnum(),
358 jobs[j].getJobnum());
360 wsinfo.setProgressText(jobs[j].getJobnum(), OutputHeader);
365 public boolean isCancellable()
370 public void cancelJob()
372 if (!jobComplete && jobs != null)
374 boolean cancelled = true;
375 for (int job = 0; job < jobs.length; job++)
377 if (jobs[job].isSubmitted() && !jobs[job].isSubjobComplete())
379 String cancelledMessage = "";
382 vamsas.objects.simple.WsJobId cancelledJob = server
383 .cancel(jobs[job].getJobId());
384 if (cancelledJob.getStatus() == 2)
387 cancelledMessage = "Job cancelled.";
388 ((SeqSearchWSJob) jobs[job]).cancel();
389 wsInfo.setStatus(jobs[job].getJobnum(),
390 WebserviceInfo.STATE_CANCELLED_OK);
392 else if (cancelledJob.getStatus() == 3)
394 // VALID UNSTOPPABLE JOB
395 cancelledMessage += "Server cannot cancel this job. just close the window.\n";
397 // wsInfo.setStatus(jobs[job].jobnum,
398 // WebserviceInfo.STATE_RUNNING);
401 if (cancelledJob.getJobId() != null)
403 cancelledMessage += ("[" + cancelledJob.getJobId() + "]");
406 cancelledMessage += "\n";
407 } catch (Exception exc)
409 cancelledMessage += ("\nProblems cancelling the job : Exception received...\n"
412 "Exception whilst cancelling " + jobs[job].getJobId(),
415 wsInfo.setProgressText(jobs[job].getJobnum(), OutputHeader
416 + cancelledMessage + "\n");
421 wsInfo.setStatus(WebserviceInfo.STATE_CANCELLED_OK);
424 this.interrupt(); // kick thread to update job states.
430 wsInfo.setProgressText(OutputHeader
431 + "Server cannot cancel this job because it has not been submitted properly. just close the window.\n");
436 public void pollJob(AWsJob job) throws Exception
438 ((SeqSearchWSJob) job).result = server.getResult(((SeqSearchWSJob) job)
442 public void StartJob(AWsJob job)
444 if (!(job instanceof SeqSearchWSJob))
446 throw new Error("StartJob(MsaWSJob) called on a WSJobInstance "
449 SeqSearchWSJob j = (SeqSearchWSJob) job;
452 if (Cache.log.isDebugEnabled())
454 Cache.log.debug("Tried to submit an already submitted job "
459 if (j.seqs.getSeqs() == null)
461 // special case - selection consisted entirely of empty sequences...
462 j.setSubmitted(true);
463 j.result = new MsaResult();
464 j.result.setFinished(true);
465 j.result.setStatus("Empty Alignment Job");
466 ((MsaResult) j.result).setMsa(null);
470 vamsas.objects.simple.WsJobId jobsubmit = server.search(
471 j.seqs.getSeqs()[0], dbArg);
473 if ((jobsubmit != null) && (jobsubmit.getStatus() == 1))
475 j.setJobId(jobsubmit.getJobId());
476 j.setSubmitted(true);
477 j.setSubjobComplete(false);
478 // System.out.println(WsURL + " Job Id '" + jobId + "'");
482 if (jobsubmit == null)
487 + " returned null object, it probably cannot be contacted. Try again later ?");
490 throw new Exception(jobsubmit.getJobId());
492 } catch (Exception e)
494 // TODO: JBPNote catch timeout or other fault types explicitly
495 // For unexpected errors
497 .println(WebServiceName
498 + "Client: Failed to submit the sequences for alignment (probably a server side problem)\n"
499 + "When contacting Server:" + WsUrl + "\n"
500 + e.toString() + "\n");
501 j.setAllowedServerExceptions(0);
502 wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_SERVERERROR);
503 wsInfo.setStatus(j.getJobnum(),
504 WebserviceInfo.STATE_STOPPED_SERVERERROR);
505 wsInfo.appendProgressText(
507 "Failed to submit sequences for alignment.\n"
508 + "It is most likely that there is a problem with the server.\n"
509 + "Just close the window\n");
511 // e.printStackTrace(); // TODO: JBPNote DEBUG
515 private jalview.datamodel.Sequence[] getVamsasAlignment(
516 vamsas.objects.simple.Alignment valign)
518 vamsas.objects.simple.Sequence[] seqs = valign.getSeqs().getSeqs();
519 jalview.datamodel.Sequence[] msa = new jalview.datamodel.Sequence[seqs.length];
521 for (int i = 0, j = seqs.length; i < j; i++)
523 msa[i] = new jalview.datamodel.Sequence(seqs[i].getId(),
530 public void parseResult()
532 int results = 0; // number of result sets received
533 JobStateSummary finalState = new JobStateSummary();
536 for (int j = 0; j < jobs.length; j++)
538 finalState.updateJobPanelState(wsInfo, OutputHeader, jobs[j]);
539 if (jobs[j].isSubmitted() && jobs[j].isSubjobComplete()
540 && jobs[j].hasResults())
543 vamsas.objects.simple.Alignment valign = ((SeqSearchResult) ((SeqSearchWSJob) jobs[j]).result)
547 wsInfo.appendProgressText(jobs[j].getJobnum(),
548 "\nAlignment Object Method Notes\n");
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
561 } catch (Exception ex)
564 Cache.log.error("Unexpected exception when processing results for "
566 wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_ERROR);
570 wsInfo.showResultsNewFrame
571 .addActionListener(new java.awt.event.ActionListener()
573 public void actionPerformed(java.awt.event.ActionEvent evt)
575 displayResults(true);
579 .addActionListener(new java.awt.event.ActionListener()
581 public void actionPerformed(java.awt.event.ActionEvent evt)
583 displayResults(false);
586 wsInfo.setResultsReady();
590 wsInfo.setFinishedNoResults();
594 void displayResults(boolean newFrame)
598 System.err.println("MERGE WITH OLD FRAME NOT IMPLEMENTED");
601 // each subjob is an independent alignment for the moment
602 // Alignment al[] = new Alignment[jobs.length];
603 // NewickFile nf[] = new NewickFile[jobs.length];
604 for (int j = 0; j < jobs.length; j++)
606 Hashtable featureColours = new Hashtable();
608 NewickFile nf = null;
609 if (jobs[j].hasResults())
611 Object[] res = ((SeqSearchWSJob) jobs[j]).getAlignment(dataset,
618 al = (Alignment) res[0];
619 nf = (NewickFile) res[1];
628 * We can't map new alignment back with insertions from input's hidden
629 * regions until dataset mapping is sorted out... but basically it goes
630 * like this: 1. Merge each domain hit back onto the visible segments in
631 * the same way as a Jnet prediction is mapped back
633 * Object[] newview = input.getUpdatedView(results, orders, getGapChar());
634 * // trash references to original result data for (int j = 0; j <
635 * jobs.length; j++) { results[j] = null; orders[j] = null; } SequenceI[]
636 * alignment = (SequenceI[]) newview[0]; ColumnSelection columnselection =
637 * (ColumnSelection) newview[1]; Alignment al = new Alignment(alignment);
639 * if (dataset != null) { al.setDataset(dataset); }
641 * propagateDatasetMappings(al); }
644 AlignFrame af = new AlignFrame(al,// columnselection,
645 AlignFrame.DEFAULT_WIDTH, AlignFrame.DEFAULT_HEIGHT);
648 af.ShowNewickTree(nf, "Tree from " + this.alTitle);
650 // initialise with same renderer settings as in parent alignframe.
651 af.getFeatureRenderer().transferSettings(this.featureSettings);
652 Desktop.addInternalFrame(af, alTitle, AlignFrame.DEFAULT_WIDTH,
653 AlignFrame.DEFAULT_HEIGHT);
657 public boolean canMergeResults()