2 * Jalview - A Sequence Alignment Editor and Viewer (Version 2.4)
\r
3 * Copyright (C) 2008 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.gui.*;
\r
27 import jalview.io.NewickFile;
\r
28 import vamsas.objects.simple.MsaResult;
\r
29 import vamsas.objects.simple.SeqSearchResult;
\r
41 * Copyright: Copyright (c) 2004
\r
45 * Company: Dundee University
\r
48 * @author not attributable
\r
51 class SeqSearchWSThread
\r
52 extends WSThread implements WSClientI
\r
55 boolean profile=false;
\r
57 class SeqSearchWSJob
\r
58 extends WSThread.WSJob
\r
60 // hold special input for this
\r
61 vamsas.objects.simple.SequenceSet seqs = new vamsas.objects.simple.
\r
72 public SeqSearchWSJob(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
87 Vector emptySeqs = new Vector();
\r
89 * prepare input sequences for service
\r
90 * @param seqs jalview sequences to be prepared
\r
91 * @param minlen minimum number of residues required for this MsaWS service
\r
92 * @return true if seqs contains sequences to be submitted to service.
\r
94 private boolean prepareInput(SequenceI[] seqs, int minlen)
\r
99 throw new Error("Implementation error: minlen must be zero or more.");
\r
101 for (int i = 0; i < seqs.length; i++)
\r
103 if (seqs[i].getEnd() - seqs[i].getStart() > minlen - 1)
\r
108 boolean valid = nseqs >= 1; // need at least one sequence for valid input TODO: generalise
\r
109 vamsas.objects.simple.Sequence[] seqarray =
\r
111 ? new vamsas.objects.simple.Sequence[nseqs]
\r
113 boolean submitGaps = (nseqs==1) ? false : true; // profile is submitted with gaps
\r
114 for (int i = 0, n = 0; i < seqs.length; i++)
\r
117 String newname = jalview.analysis.SeqsetUtils.unique_name(i); // same
\r
121 SeqNames.put(newname, jalview.analysis.SeqsetUtils
\r
122 .SeqCharacterHash(seqs[i]));
\r
123 if (valid && seqs[i].getEnd() - seqs[i].getStart() > minlen - 1)
\r
125 seqarray[n] = new vamsas.objects.simple.Sequence();
\r
126 seqarray[n].setId(newname);
\r
127 seqarray[n++].setSeq( (submitGaps) ? seqs[i].getSequenceAsString()
\r
128 : AlignSeq.extractGaps(
\r
129 jalview.util.Comparison.GapChars, seqs[i]
\r
130 .getSequenceAsString()));
\r
134 String empty = null;
\r
135 if (seqs[i].getEnd() >= seqs[i].getStart())
\r
137 empty = (submitGaps) ? seqs[i].getSequenceAsString()
\r
138 : AlignSeq.extractGaps(
\r
139 jalview.util.Comparison.GapChars, seqs[i]
\r
140 .getSequenceAsString());
\r
142 emptySeqs.add(new String[]
\r
148 // almost certainly have to remove gapped columns here
\r
150 this.seqs = new vamsas.objects.simple.SequenceSet();
\r
151 this.seqs.setSeqs(seqarray);
\r
157 * @return true if getAlignment will return a valid alignment result.
\r
159 public boolean hasResults()
\r
161 if (subjobComplete && result != null && result.isFinished()
\r
162 && ( (SeqSearchResult) result).getAlignment() != null &&
\r
163 ( (SeqSearchResult) result).getAlignment().getSeqs() != null)
\r
171 * return sequence search results for display
\r
172 * @return null or { Alignment(+features and annotation), NewickFile)}
\r
174 public Object[] getAlignment(Alignment dataset, Hashtable featureColours)
\r
177 if (result != null && result.isFinished())
\r
179 SequenceI[] alseqs = null;
\r
180 //char alseq_gapchar = '-';
\r
182 if ( ( (SeqSearchResult) result).getAlignment() != null)
\r
184 alseqs = getVamsasAlignment( ( (SeqSearchResult) result).getAlignment());
\r
185 //alseq_gapchar = ( (SeqSearchResult) result).getAlignment().getGapchar().charAt(0);
\r
186 //alseq_l = alseqs.length;
\r
189 * what has to be done. 1 - annotate returned alignment with annotation file and sequence features file, and associate any tree-nodes.
\r
190 * 2. connect alignment back to any associated dataset: 2.a. deuniquify recovers sequence information - but additionally,
\r
191 * relocations must be made from the returned aligned sequence back to the dataset.
\r
193 // construct annotated alignment as it would be done by the jalview applet
\r
194 jalview.datamodel.Alignment al = new Alignment(alseqs);
\r
195 // al.setDataset(dataset);
\r
197 String inFile=null;
\r
199 inFile = ((SeqSearchResult) result).getAnnotation();
\r
200 if (inFile!=null && inFile.length()>0)
\r
202 new jalview.io.AnnotationFile().readAnnotationFile(al, inFile, jalview.io.AppletFormatAdapter.PASTE);
\r
205 catch (Exception e)
\r
207 System.err.println("Failed to parse the annotation file associated with the alignment.");
\r
208 System.err.println(">>>EOF"+inFile+"\n<<<EOF\n");
\r
209 e.printStackTrace(System.err);
\r
213 inFile = ((SeqSearchResult) result).getFeatures();
\r
214 if (inFile!=null && inFile.length()>0)
\r
216 jalview.io.FeaturesFile ff = new jalview.io.FeaturesFile(inFile, jalview.io.AppletFormatAdapter.PASTE);
\r
217 ff.parse(al, featureColours, false);
\r
220 catch (Exception e)
\r
222 System.err.println("Failed to parse the Features file associated with the alignment.");
\r
223 System.err.println(">>>EOF"+inFile+"\n<<<EOF\n");
\r
224 e.printStackTrace(System.err);
\r
226 jalview.io.NewickFile nf=null;
\r
228 inFile = ((SeqSearchResult) result).getNewickTree();
\r
229 if (inFile!=null && inFile.length()>0)
\r
231 nf = new jalview.io.NewickFile(inFile, jalview.io.AppletFormatAdapter.PASTE);
\r
232 if (!nf.isValid()) {
\r
238 catch (Exception e)
\r
240 System.err.println("Failed to parse the treeFile associated with the alignment.");
\r
241 System.err.println(">>>EOF"+inFile+"\n<<<EOF\n");
\r
242 e.printStackTrace(System.err);
\r
245 /* TODO: housekeeping w.r.t. recovery of dataset and annotation references for input sequences, and then dataset sequence creation for new sequences retrieved from service
\r
246 * // finally, attempt to de-uniquify to recover input sequence identity, and try to map back onto dataset
\r
248 jalview.analysis.SeqsetUtils.deuniquify(SeqNames, alseqs, true);
\r
249 will NOT WORK - the returned alignment may contain multiple versions of the input sequence, each being a subsequence of the original.
\r
250 deuniquify also removes existing annotation and features added in the previous step...
\r
251 al.setDataset(dataset);
\r
252 // add in new sequences retrieved from sequence search which are not already in dataset.
\r
253 // trigger a 'fetchDBids' to annotate sequences with database ids...
\r
256 return new Object[]
\r
264 * mark subjob as cancelled and set result object appropriatly
\r
269 subjobComplete = true;
\r
275 * @return boolean true if job can be submitted.
\r
277 boolean hasValidInput()
\r
279 if (seqs.getSeqs() != null)
\r
287 String alTitle; // name which will be used to form new alignment window.
\r
288 Alignment dataset; // dataset to which the new alignment will be
\r
292 ext.vamsas.SeqSearchI server = null;
\r
294 private String dbArg;
\r
296 * set basic options for this (group) of Msa jobs
\r
303 SeqSearchWSThread(ext.vamsas.SeqSearchI server, String wsUrl,
\r
304 WebserviceInfo wsinfo, jalview.gui.AlignFrame alFrame,
\r
305 AlignmentView alview,
\r
306 String wsname, String db)
\r
308 super(alFrame, wsinfo, alview, wsname, wsUrl);
\r
309 this.server = server;
\r
314 * create one or more Msa jobs to align visible seuqences in _msa
\r
327 SeqSearchWSThread(ext.vamsas.SeqSearchI server, String wsUrl,
\r
328 WebserviceInfo wsinfo, jalview.gui.AlignFrame alFrame,
\r
329 String wsname, String title, AlignmentView _msa, String db, Alignment seqset)
\r
331 this(server, wsUrl, wsinfo, alFrame, _msa, wsname, db);
\r
332 OutputHeader = wsInfo.getProgressText();
\r
336 SequenceI[][] conmsa = _msa.getVisibleContigs('-');
\r
337 if (conmsa != null)
\r
339 int njobs = conmsa.length;
\r
340 jobs = new SeqSearchWSJob[njobs];
\r
341 for (int j = 0; j < njobs; j++)
\r
345 jobs[j] = new SeqSearchWSJob(wsinfo.addJobPane(), conmsa[j]);
\r
349 jobs[j] = new SeqSearchWSJob(0, conmsa[j]);
\r
353 wsinfo.setProgressName("region " + jobs[j].jobnum, jobs[j].jobnum);
\r
355 wsinfo.setProgressText(jobs[j].jobnum, OutputHeader);
\r
360 public boolean isCancellable()
\r
365 public void cancelJob()
\r
367 if (!jobComplete && jobs != null)
\r
369 boolean cancelled = true;
\r
370 for (int job = 0; job < jobs.length; job++)
\r
372 if (jobs[job].submitted && !jobs[job].subjobComplete)
\r
374 String cancelledMessage = "";
\r
377 vamsas.objects.simple.WsJobId cancelledJob = server
\r
378 .cancel(jobs[job].jobId);
\r
379 if (cancelledJob.getStatus() == 2)
\r
382 cancelledMessage = "Job cancelled.";
\r
383 ( (SeqSearchWSJob) jobs[job]).cancel();
\r
384 wsInfo.setStatus(jobs[job].jobnum,
\r
385 WebserviceInfo.STATE_CANCELLED_OK);
\r
387 else if (cancelledJob.getStatus() == 3)
\r
389 // VALID UNSTOPPABLE JOB
\r
390 cancelledMessage +=
\r
391 "Server cannot cancel this job. just close the window.\n";
\r
393 // wsInfo.setStatus(jobs[job].jobnum,
\r
394 // WebserviceInfo.STATE_RUNNING);
\r
397 if (cancelledJob.getJobId() != null)
\r
399 cancelledMessage += ("[" + cancelledJob.getJobId() + "]");
\r
402 cancelledMessage += "\n";
\r
404 catch (Exception exc)
\r
406 cancelledMessage +=
\r
407 ("\nProblems cancelling the job : Exception received...\n"
\r
409 Cache.log.warn("Exception whilst cancelling " + jobs[job].jobId,
\r
412 wsInfo.setProgressText(jobs[job].jobnum, OutputHeader
\r
413 + cancelledMessage + "\n");
\r
418 wsInfo.setStatus(WebserviceInfo.STATE_CANCELLED_OK);
\r
419 jobComplete = true;
\r
421 this.interrupt(); // kick thread to update job states.
\r
428 .setProgressText(OutputHeader
\r
429 + "Server cannot cancel this job because it has not been submitted properly. just close the window.\n");
\r
434 void pollJob(WSJob job)
\r
437 ( (SeqSearchWSJob) job).result = server.getResult( ( (SeqSearchWSJob) job).jobId);
\r
440 void StartJob(WSJob job)
\r
442 if (! (job instanceof SeqSearchWSJob))
\r
444 throw new Error("StartJob(MsaWSJob) called on a WSJobInstance " +
\r
447 SeqSearchWSJob j = (SeqSearchWSJob) job;
\r
450 if (Cache.log.isDebugEnabled())
\r
452 Cache.log.debug("Tried to submit an already submitted job " + j.jobId);
\r
456 if (j.seqs.getSeqs() == null)
\r
458 // special case - selection consisted entirely of empty sequences...
\r
459 j.submitted = true;
\r
460 j.result = new MsaResult();
\r
461 j.result.setFinished(true);
\r
462 j.result.setStatus("Empty Alignment Job");
\r
463 ( (MsaResult) j.result).setMsa(null);
\r
467 vamsas.objects.simple.WsJobId jobsubmit = server.search(j.seqs.getSeqs()[0], dbArg);
\r
469 if ( (jobsubmit != null) && (jobsubmit.getStatus() == 1))
\r
471 j.jobId = jobsubmit.getJobId();
\r
472 j.submitted = true;
\r
473 j.subjobComplete = false;
\r
474 // System.out.println(WsURL + " Job Id '" + jobId + "'");
\r
478 if (jobsubmit == null)
\r
480 throw new Exception(
\r
484 " returned null object, it probably cannot be contacted. Try again later ?");
\r
487 throw new Exception(jobsubmit.getJobId());
\r
490 catch (Exception e)
\r
492 // TODO: JBPNote catch timeout or other fault types explicitly
\r
493 // For unexpected errors
\r
495 .println(WebServiceName
\r
496 + "Client: Failed to submit the sequences for alignment (probably a server side problem)\n"
\r
497 + "When contacting Server:" + WsUrl + "\n"
\r
498 + e.toString() + "\n");
\r
499 j.allowedServerExceptions = 0;
\r
500 wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_SERVERERROR);
\r
501 wsInfo.setStatus(j.jobnum, WebserviceInfo.STATE_STOPPED_SERVERERROR);
\r
503 .appendProgressText(
\r
505 "Failed to submit sequences for alignment.\n"
\r
506 + "It is most likely that there is a problem with the server.\n"
\r
507 + "Just close the window\n");
\r
509 // e.printStackTrace(); // TODO: JBPNote DEBUG
\r
513 private jalview.datamodel.Sequence[] getVamsasAlignment(
\r
514 vamsas.objects.simple.Alignment valign)
\r
516 vamsas.objects.simple.Sequence[] seqs = valign.getSeqs().getSeqs();
\r
517 jalview.datamodel.Sequence[] msa = new jalview.datamodel.Sequence[seqs.
\r
520 for (int i = 0, j = seqs.length; i < j; i++)
\r
522 msa[i] = new jalview.datamodel.Sequence(seqs[i].getId(), seqs[i]
\r
531 int results = 0; // number of result sets received
\r
532 JobStateSummary finalState = new JobStateSummary();
\r
535 for (int j = 0; j < jobs.length; j++)
\r
537 finalState.updateJobPanelState(wsInfo, OutputHeader, jobs[j]);
\r
538 if (jobs[j].submitted && jobs[j].subjobComplete && jobs[j].hasResults())
\r
541 vamsas.objects.simple.Alignment valign = ( (SeqSearchResult) jobs[j].result).
\r
543 if (valign != null)
\r
545 wsInfo.appendProgressText(jobs[j].jobnum,
\r
546 "\nAlignment Object Method Notes\n");
\r
547 String[] lines = valign.getMethod();
\r
548 for (int line = 0; line < lines.length; line++)
\r
550 wsInfo.appendProgressText(jobs[j].jobnum, lines[line] + "\n");
\r
552 // JBPNote The returned files from a webservice could be
\r
553 // hidden behind icons in the monitor window that,
\r
554 // when clicked, pop up their corresponding data
\r
559 catch (Exception ex)
\r
562 Cache.log.error("Unexpected exception when processing results for " +
\r
564 wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_ERROR);
\r
568 wsInfo.showResultsNewFrame
\r
569 .addActionListener(new java.awt.event.ActionListener()
\r
571 public void actionPerformed(
\r
572 java.awt.event.ActionEvent evt)
\r
574 displayResults(true);
\r
577 wsInfo.mergeResults
\r
578 .addActionListener(new java.awt.event.ActionListener()
\r
580 public void actionPerformed(
\r
581 java.awt.event.ActionEvent evt)
\r
583 displayResults(false);
\r
586 wsInfo.setResultsReady();
\r
590 wsInfo.setFinishedNoResults();
\r
594 void displayResults(boolean newFrame)
\r
598 System.err.println("MERGE WITH OLD FRAME NOT IMPLEMENTED");
\r
601 // each subjob is an independent alignment for the moment
\r
602 //Alignment al[] = new Alignment[jobs.length];
\r
603 //NewickFile nf[] = new NewickFile[jobs.length];
\r
604 for (int j = 0; j < jobs.length; j++)
\r
606 Hashtable featureColours = new Hashtable();
\r
608 NewickFile nf = null;
\r
609 if (jobs[j].hasResults())
\r
611 Object[] res = ( (SeqSearchWSJob) jobs[j]).getAlignment(dataset, featureColours);
\r
612 if (res==null) { continue; };
\r
613 al = (Alignment) res[0];
\r
614 nf = (NewickFile) res[1];
\r
623 * We can't map new alignment back with insertions from input's hidden regions until dataset mapping is sorted out...
\r
624 * but basically it goes like this:
\r
625 1. Merge each domain hit back onto the visible segments in the same way as a Jnet prediction is mapped back
\r
627 Object[] newview = input.getUpdatedView(results, orders, getGapChar());
\r
628 // trash references to original result data
\r
629 for (int j = 0; j < jobs.length; j++)
\r
634 SequenceI[] alignment = (SequenceI[]) newview[0];
\r
635 ColumnSelection columnselection = (ColumnSelection) newview[1];
\r
636 Alignment al = new Alignment(alignment);
\r
638 if (dataset != null)
\r
640 al.setDataset(dataset);
\r
643 propagateDatasetMappings(al);
\r
647 AlignFrame af = new AlignFrame(al,// columnselection,
\r
648 AlignFrame.DEFAULT_WIDTH,
\r
649 AlignFrame.DEFAULT_HEIGHT);
\r
652 af.ShowNewickTree(nf, "Tree from "+this.alTitle);
\r
654 // initialise with same renderer settings as in parent alignframe.
\r
655 af.getFeatureRenderer().transferSettings(
\r
656 this.featureSettings);
\r
657 Desktop.addInternalFrame(af, alTitle,
\r
658 AlignFrame.DEFAULT_WIDTH,
\r
659 AlignFrame.DEFAULT_HEIGHT);
\r
663 public boolean canMergeResults()
\r