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