2 * Jalview - A Sequence Alignment Editor and Viewer (Version 2.5)
3 * Copyright (C) 2010 J Procter, AM Waterhouse, 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.jws2;
22 import compbio.data.msa.MsaWS;
23 import compbio.data.sequence.AlignmentMetadata;
24 import compbio.data.sequence.Program;
25 import compbio.metadata.Argument;
26 import compbio.metadata.ChunkHolder;
27 import compbio.metadata.JobStatus;
28 import compbio.metadata.Preset;
30 import jalview.analysis.*;
32 import jalview.datamodel.*;
34 import jalview.ws.AWsJob;
35 import jalview.ws.WSClientI;
36 import jalview.ws.JobStateSummary;
48 * Copyright: Copyright (c) 2004
52 * Company: Dundee University
55 * @author not attributable
58 class MsaWSThread extends AWS2Thread implements WSClientI
60 boolean submitGaps = false; // pass sequences including gaps to alignment
64 boolean preserveOrder = true; // and always store and recover sequence
68 class MsaWSJob extends JWs2Job
72 List<Argument> arguments = null;
76 ArrayList<compbio.data.sequence.FastaSequence> seqs = new ArrayList<compbio.data.sequence.FastaSequence>();
80 compbio.data.sequence.Alignment alignment;
81 // set if the job didn't get run - then the input is simply returned to the user
82 private boolean returnInput=false;
92 public MsaWSJob(int jobNum, SequenceI[] inSeqs)
95 if (!prepareInput(inSeqs, 2))
98 subjobComplete = true;
104 Hashtable<String,Map> SeqNames = new Hashtable();
106 Vector<String[]> emptySeqs = new Vector();
109 * prepare input sequences for MsaWS service
112 * jalview sequences to be prepared
114 * minimum number of residues required for this MsaWS service
115 * @return true if seqs contains sequences to be submitted to service.
117 // TODO: return compbio.seqs list or nothing to indicate validity.
118 private boolean prepareInput(SequenceI[] seqs, int minlen)
124 "Implementation error: minlen must be zero or more.");
126 for (int i = 0; i < seqs.length; i++)
128 if (seqs[i].getEnd() - seqs[i].getStart() > minlen - 1)
133 boolean valid = nseqs > 1; // need at least two seqs
134 compbio.data.sequence.FastaSequence seq;
135 for (int i = 0, n = 0; i < seqs.length; i++)
138 String newname = jalview.analysis.SeqsetUtils.unique_name(i); // same
142 SeqNames.put(newname, jalview.analysis.SeqsetUtils
143 .SeqCharacterHash(seqs[i]));
144 if (valid && seqs[i].getEnd() - seqs[i].getStart() > minlen - 1)
146 // make new input sequence with or without gaps
147 seq = new compbio.data.sequence.FastaSequence(newname,
148 (submitGaps) ? seqs[i].getSequenceAsString()
149 : AlignSeq.extractGaps(jalview.util.Comparison.GapChars,
150 seqs[i].getSequenceAsString()));
156 if (seqs[i].getEnd() >= seqs[i].getStart())
158 empty = (submitGaps) ? seqs[i].getSequenceAsString() : AlignSeq
159 .extractGaps(jalview.util.Comparison.GapChars, seqs[i]
160 .getSequenceAsString());
162 emptySeqs.add(new String[]
171 * @return true if getAlignment will return a valid alignment result.
173 public boolean hasResults()
175 if (subjobComplete && isFinished() && (alignment!=null || (emptySeqs!=null && emptySeqs.size()>0)))
184 * get the alignment including any empty sequences in the original order with original ids. Caller must access the alignment.getMetadata() object to annotate the final result passsed to the user.
185 * @return { SequenceI[], AlignmentOrder }
187 public Object[] getAlignment()
189 // is this a generic subjob or a Jws2 specific Object[] return signature
192 SequenceI[] alseqs = null;
193 char alseq_gapchar = '-';
195 if (alignment.getSequences().size()>0)
197 alseqs = new SequenceI[alignment.getSequences().size()];
198 for (compbio.data.sequence.FastaSequence seq: alignment.getSequences())
200 alseqs[alseq_l++] = new Sequence(seq.getId(),seq.getSequence());
202 alseq_gapchar = alignment.getMetadata().getGapchar();
205 // add in the empty seqs.
206 if (emptySeqs.size() > 0)
208 SequenceI[] t_alseqs = new SequenceI[alseq_l + emptySeqs.size()];
213 for (i = 0, w = alseqs[0].getLength(); i < alseq_l; i++)
215 if (w < alseqs[i].getLength())
217 w = alseqs[i].getLength();
219 t_alseqs[i] = alseqs[i];
223 // check that aligned width is at least as wide as emptySeqs width.
225 for (i = 0, w = emptySeqs.size(); i < w; i++)
227 String[] es = (String[]) emptySeqs.get(i);
228 if (es != null && es[1] != null)
230 int sw = es[1].length();
237 // make a gapped string.
238 StringBuffer insbuff = new StringBuffer(w);
239 for (i = 0; i < nw; i++)
241 insbuff.append(alseq_gapchar);
245 for (i = 0; i < alseq_l; i++)
247 int sw = t_alseqs[i].getLength();
251 alseqs[i].setSequence(t_alseqs[i].getSequenceAsString()
252 + insbuff.substring(0, sw - nw));
256 for (i = 0, w = emptySeqs.size(); i < w; i++)
258 String[] es = (String[]) emptySeqs.get(i);
261 t_alseqs[i + alseq_l] = new jalview.datamodel.Sequence(es[0],
262 insbuff.toString(), 1, 0);
266 if (es[1].length() < nw)
268 t_alseqs[i + alseq_l] = new jalview.datamodel.Sequence(
270 es[1] + insbuff.substring(0, nw - es[1].length()),
271 1, 1 + es[1].length());
275 t_alseqs[i + alseq_l] = new jalview.datamodel.Sequence(
282 AlignmentOrder msaorder = new AlignmentOrder(alseqs);
283 // always recover the order - makes parseResult()'s life easier.
284 jalview.analysis.AlignmentSorter.recoverOrder(alseqs);
285 // account for any missing sequences
286 jalview.analysis.SeqsetUtils.deuniquify(SeqNames, alseqs);
288 { alseqs, msaorder };
294 * mark subjob as cancelled and set result object appropriatly
299 subjobComplete = true;
305 * @return boolean true if job can be submitted.
307 public boolean hasValidInput()
309 // TODO: get attributes for this MsaWS instance to check if it can do two sequence alignment.
310 if (seqs != null && seqs.size()>=2) // two or more sequences is valid ?
316 StringBuffer jobProgress=new StringBuffer();
317 public void setStatus(String string)
319 jobProgress.setLength(0);
320 jobProgress.append(string);
324 public String getStatus()
326 return jobProgress.toString();
330 public boolean hasStatus()
332 return jobProgress!=null;
336 * @return the lastChunk
338 public long getLastChunk()
344 * @param lastChunk the lastChunk to set
346 public void setLastChunk(long lastChunk)
348 this.lastChunk = lastChunk;
351 String alignmentProgram=null;
352 public String getAlignmentProgram()
354 return alignmentProgram;
359 String alTitle; // name which will be used to form new alignment window.
361 Alignment dataset; // dataset to which the new alignment will be
365 @SuppressWarnings("unchecked")
369 * set basic options for this (group) of Msa jobs
376 MsaWSThread(MsaWS server, String wsUrl,
377 WebserviceInfo wsinfo, jalview.gui.AlignFrame alFrame,
378 AlignmentView alview, String wsname, boolean subgaps,
381 super(alFrame, wsinfo, alview, wsname, wsUrl);
382 this.server = server;
383 this.submitGaps = subgaps;
384 this.preserveOrder = presorder;
388 * create one or more Msa jobs to align visible seuqences in _msa
401 MsaWSThread(MsaWS server2, Preset preset, List<Argument> paramset, String wsUrl,
402 WebserviceInfo wsinfo, jalview.gui.AlignFrame alFrame,
403 String wsname, String title, AlignmentView _msa, boolean subgaps,
404 boolean presorder, Alignment seqset)
406 this(server2,wsUrl, wsinfo, alFrame, _msa, wsname, subgaps, presorder);
407 OutputHeader = wsInfo.getProgressText();
411 SequenceI[][] conmsa = _msa.getVisibleContigs('-');
414 int njobs = conmsa.length;
415 jobs = new MsaWSJob[njobs];
416 for (int j = 0; j < njobs; j++)
420 jobs[j] = new MsaWSJob(wsinfo.addJobPane(), conmsa[j]);
424 jobs[j] = new MsaWSJob(0, conmsa[j]);
426 ((MsaWSJob) jobs[j]).preset = preset;
427 ((MsaWSJob) jobs[j]).arguments = paramset;
428 ((MsaWSJob) jobs[j]).alignmentProgram = wsname;
432 .setProgressName("region " + jobs[j].getJobnum(),
433 jobs[j].getJobnum());
435 wsinfo.setProgressText(jobs[j].getJobnum(), OutputHeader);
440 public boolean isCancellable()
445 public void cancelJob()
447 if (!jobComplete && jobs != null)
449 boolean cancelled = true;
450 for (int job = 0; job < jobs.length; job++)
452 if (jobs[job].isSubmitted() && !jobs[job].isSubjobComplete())
454 String cancelledMessage = "";
457 boolean cancelledJob = server
458 .cancelJob(jobs[job].getJobId());
462 cancelledMessage = "Job cancelled.";
463 ((MsaWSJob) jobs[job]).cancel(); // TODO: refactor to avoid this ugliness -
464 wsInfo.setStatus(jobs[job].getJobnum(),
465 WebserviceInfo.STATE_CANCELLED_OK);
469 // VALID UNSTOPPABLE JOB
470 cancelledMessage += "Server cannot cancel this job. just close the window.\n";
472 // wsInfo.setStatus(jobs[job].jobnum,
473 // WebserviceInfo.STATE_RUNNING);
475 } catch (Exception exc)
477 cancelledMessage += ("\nProblems cancelling the job : Exception received...\n"
480 "Exception whilst cancelling " + jobs[job].getJobId(), exc);
482 wsInfo.setProgressText(jobs[job].getJobnum(), OutputHeader
483 + cancelledMessage + "\n");
488 wsInfo.setStatus(WebserviceInfo.STATE_CANCELLED_OK);
491 this.interrupt(); // kick thread to update job states.
498 .setProgressText(OutputHeader
499 + "Server cannot cancel this job because it has not been submitted properly. just close the window.\n");
504 public void pollJob(AWsJob job) throws Exception
506 // TODO: investigate if we still need to cast here in J1.6
507 MsaWSJob j=((MsaWSJob) job);
508 // this is standard code, but since the interface doesn't comprise of a basic one that implements (getJobStatus, pullExecStatistics) we have to repeat the code for all jw2s services.
509 j.setjobStatus(server.getJobStatus(job.getJobId()));
510 updateJobProgress(j);
512 protected void updateJobProgress(MsaWSJob j) throws Exception {
513 StringBuffer response = j.jobProgress;
514 long lastchunk = j.getLastChunk();
516 j.setLastChunk(lastchunk);
517 ChunkHolder chunk = server.pullExecStatistics(j.getJobId(), lastchunk);
519 response.append(chunk.getChunk());
520 lastchunk = chunk.getNextPosition();
522 } while (lastchunk>=0 && j.getLastChunk()!=lastchunk);
525 public void StartJob(AWsJob job)
527 // boiler plate template
528 if (!(job instanceof MsaWSJob))
530 throw new Error("StartJob(MsaWSJob) called on a WSJobInstance "
533 MsaWSJob j = (MsaWSJob) job;
536 if (Cache.log.isDebugEnabled())
538 Cache.log.debug("Tried to submit an already submitted job "
545 if (j.seqs == null || j.seqs.size()==0)
547 // special case - selection consisted entirely of empty sequences...
548 j.setjobStatus(JobStatus.FINISHED);
549 j.setStatus("Empty Alignment Job");
553 // TODO: get the parameters (if any) for this job and submit the job
554 if (j.arguments!=null && j.arguments.size()>0)
556 StringBuffer pset = new StringBuffer();
557 for (Argument arg: j.arguments) {
558 pset.append(arg.getName()+" "+arg.getDefaultValue()+"\n");
560 j.setStatus("Custom Parameters:\n"+pset.toString()+"\nJob Output:\n");
561 j.setJobId(server.customAlign(j.seqs, j.arguments));
565 j.setJobId(server.presetAlign(j.seqs, j.preset));
567 j.setJobId(server.align(j.seqs));
570 if (j.getJobId()!= null)
572 j.setSubmitted(true);
573 j.setSubjobComplete(false);
574 // System.out.println(WsURL + " Job Id '" + jobId + "'");
581 + " returned null string for job id, it probably cannot be contacted. Try again later ?");
583 } catch (Exception e)
585 // Boilerplate code here
586 // TODO: JBPNote catch timeout or other fault types explicitly
587 // For unexpected errors
589 .println(WebServiceName
590 + "Client: Failed to submit the sequences for alignment (probably a server side problem)\n"
591 + "When contacting Server:" + WsUrl + "\n");
592 e.printStackTrace(System.err);
593 j.setAllowedServerExceptions(0);
594 wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_SERVERERROR);
595 wsInfo.setStatus(j.getJobnum(), WebserviceInfo.STATE_STOPPED_SERVERERROR);
599 "Failed to submit sequences for alignment.\n"
600 + "It is most likely that there is a problem with the server.\n"
601 + "Just close the window\n");
603 // e.printStackTrace(); // TODO: JBPNote DEBUG
608 public void parseResult()
610 int results = 0; // number of result sets received
611 JobStateSummary finalState = new JobStateSummary();
614 for (int j = 0; j < jobs.length; j++)
616 MsaWSJob msjob = ((MsaWSJob) jobs[j]);
617 if (jobs[j].isFinished() && msjob.alignment==null)
620 updateJobProgress(msjob);
621 } catch (Exception e)
623 Cache.log.warn("Exception when retrieving remaining Job progress data for job "+msjob.getJobId()+" on server "+WsUrl);
626 if (Cache.log.isDebugEnabled())
628 System.out.println("Job Execution file for job: "+msjob.getJobId()+" on server "+WsUrl);
629 System.out.println(msjob.getStatus());
630 System.out.println("*** End of status");
634 msjob.alignment = server.getResult(msjob.getJobId());
638 Cache.log.error("Couldn't get Alignment for job.",e);
641 finalState.updateJobPanelState(wsInfo, OutputHeader, jobs[j]);
642 if (jobs[j].isSubmitted() && jobs[j].isSubjobComplete()
643 && jobs[j].hasResults())
646 compbio.data.sequence.Alignment alignment = ((MsaWSJob) jobs[j]).alignment;
647 if (alignment != null)
649 // server.close(jobs[j].getJobnum());
650 // wsInfo.appendProgressText(jobs[j].getJobnum(),
651 // "\nAlignment Object Method Notes\n");
652 // wsInfo.appendProgressText(jobs[j].getJobnum(), "Calculated with "+alignment.getMetadata().getProgram().toString());
653 // JBPNote The returned files from a webservice could be
654 // hidden behind icons in the monitor window that,
655 // when clicked, pop up their corresponding data
659 } catch (Exception ex)
662 Cache.log.error("Unexpected exception when processing results for "
664 wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_ERROR);
668 wsInfo.showResultsNewFrame
669 .addActionListener(new java.awt.event.ActionListener()
671 public void actionPerformed(java.awt.event.ActionEvent evt)
673 displayResults(true);
677 .addActionListener(new java.awt.event.ActionListener()
679 public void actionPerformed(java.awt.event.ActionEvent evt)
681 displayResults(false);
684 wsInfo.setResultsReady();
688 wsInfo.setFinishedNoResults();
692 void displayResults(boolean newFrame)
694 // view input or result data for each block
695 Vector alorders = new Vector();
696 SequenceI[][] results = new SequenceI[jobs.length][];
697 AlignmentOrder[] orders = new AlignmentOrder[jobs.length];
698 String lastProgram = null;
700 for (int j = 0; j < jobs.length; j++)
702 if (jobs[j].hasResults())
704 msjob = (MsaWSJob)jobs[j];
705 Object[] res = msjob.getAlignment();
706 lastProgram = msjob.getAlignmentProgram();
707 alorders.add(res[1]);
708 results[j] = (SequenceI[]) res[0];
709 orders[j] = (AlignmentOrder) res[1];
711 // SequenceI[] alignment = input.getUpdated
718 Object[] newview = input.getUpdatedView(results, orders, getGapChar());
719 // trash references to original result data
720 for (int j = 0; j < jobs.length; j++)
725 SequenceI[] alignment = (SequenceI[]) newview[0];
726 ColumnSelection columnselection = (ColumnSelection) newview[1];
727 Alignment al = new Alignment(alignment);
728 // TODO: add 'provenance' property to alignment from the method notes
729 if (lastProgram!=null) {
730 al.setProperty("Alignment Program", lastProgram);
732 // accompanying each subjob
735 al.setDataset(dataset);
738 propagateDatasetMappings(al);
739 // JBNote- TODO: warn user if a block is input rather than aligned data ?
743 AlignFrame af = new AlignFrame(al, columnselection,
744 AlignFrame.DEFAULT_WIDTH, AlignFrame.DEFAULT_HEIGHT);
746 // initialise with same renderer settings as in parent alignframe.
747 af.getFeatureRenderer().transferSettings(this.featureSettings);
749 if (alorders.size() > 0)
751 if (alorders.size() == 1)
753 af.addSortByOrderMenuItem(WebServiceName + " Ordering",
754 (AlignmentOrder) alorders.get(0));
758 // construct a non-redundant ordering set
759 Vector names = new Vector();
760 for (int i = 0, l = alorders.size(); i < l; i++)
762 String orderName = new String(" Region " + i);
767 if (((AlignmentOrder) alorders.get(i))
768 .equals(((AlignmentOrder) alorders.get(j))))
772 orderName += "," + j;
780 if (i == 0 && j == 1)
782 names.add(new String(""));
786 names.add(orderName);
789 for (int i = 0, l = alorders.size(); i < l; i++)
791 af.addSortByOrderMenuItem(WebServiceName
792 + ((String) names.get(i)) + " Ordering",
793 (AlignmentOrder) alorders.get(i));
798 Desktop.addInternalFrame(af, alTitle, AlignFrame.DEFAULT_WIDTH,
799 AlignFrame.DEFAULT_HEIGHT);
804 System.out.println("MERGE WITH OLD FRAME");
805 // TODO: modify alignment in original frame, replacing old for new
806 // alignment using the commands.EditCommand model to ensure the update can
811 public boolean canMergeResults()