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 j.setJobId(server.customAlign(j.seqs, j.arguments));
560 j.setJobId(server.presetAlign(j.seqs, j.preset));
562 j.setJobId(server.align(j.seqs));
565 if (j.getJobId()!= null)
567 j.setSubmitted(true);
568 j.setSubjobComplete(false);
569 // System.out.println(WsURL + " Job Id '" + jobId + "'");
576 + " returned null string for job id, it probably cannot be contacted. Try again later ?");
578 } catch (Exception e)
580 // Boilerplate code here
581 // TODO: JBPNote catch timeout or other fault types explicitly
582 // For unexpected errors
584 .println(WebServiceName
585 + "Client: Failed to submit the sequences for alignment (probably a server side problem)\n"
586 + "When contacting Server:" + WsUrl + "\n");
587 e.printStackTrace(System.err);
588 j.setAllowedServerExceptions(0);
589 wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_SERVERERROR);
590 wsInfo.setStatus(j.getJobnum(), WebserviceInfo.STATE_STOPPED_SERVERERROR);
594 "Failed to submit sequences for alignment.\n"
595 + "It is most likely that there is a problem with the server.\n"
596 + "Just close the window\n");
598 // e.printStackTrace(); // TODO: JBPNote DEBUG
603 public void parseResult()
605 int results = 0; // number of result sets received
606 JobStateSummary finalState = new JobStateSummary();
609 for (int j = 0; j < jobs.length; j++)
611 MsaWSJob msjob = ((MsaWSJob) jobs[j]);
612 if (jobs[j].isFinished() && msjob.alignment==null)
615 updateJobProgress(msjob);
616 } catch (Exception e)
618 Cache.log.warn("Exception when retrieving remaining Job progress data for job "+msjob.getJobId()+" on server "+WsUrl);
621 if (Cache.log.isDebugEnabled())
623 System.out.println("Job Execution file for job: "+msjob.getJobId()+" on server "+WsUrl);
624 System.out.println(msjob.getStatus());
625 System.out.println("*** End of status");
629 msjob.alignment = server.getResult(msjob.getJobId());
633 Cache.log.error("Couldn't get Alignment for job.",e);
636 finalState.updateJobPanelState(wsInfo, OutputHeader, jobs[j]);
637 if (jobs[j].isSubmitted() && jobs[j].isSubjobComplete()
638 && jobs[j].hasResults())
641 compbio.data.sequence.Alignment alignment = ((MsaWSJob) jobs[j]).alignment;
642 if (alignment != null)
644 wsInfo.appendProgressText(jobs[j].getJobnum(),
645 "\nAlignment Object Method Notes\n");
646 wsInfo.appendProgressText(jobs[j].getJobnum(), "Calculated with "+alignment.getMetadata().getProgram().toString());
647 // JBPNote The returned files from a webservice could be
648 // hidden behind icons in the monitor window that,
649 // when clicked, pop up their corresponding data
653 } catch (Exception ex)
656 Cache.log.error("Unexpected exception when processing results for "
658 wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_ERROR);
662 wsInfo.showResultsNewFrame
663 .addActionListener(new java.awt.event.ActionListener()
665 public void actionPerformed(java.awt.event.ActionEvent evt)
667 displayResults(true);
671 .addActionListener(new java.awt.event.ActionListener()
673 public void actionPerformed(java.awt.event.ActionEvent evt)
675 displayResults(false);
678 wsInfo.setResultsReady();
682 wsInfo.setFinishedNoResults();
686 void displayResults(boolean newFrame)
688 // view input or result data for each block
689 Vector alorders = new Vector();
690 SequenceI[][] results = new SequenceI[jobs.length][];
691 AlignmentOrder[] orders = new AlignmentOrder[jobs.length];
692 String lastProgram = null;
694 for (int j = 0; j < jobs.length; j++)
696 if (jobs[j].hasResults())
698 msjob = (MsaWSJob)jobs[j];
699 Object[] res = msjob.getAlignment();
700 lastProgram = msjob.getAlignmentProgram();
701 alorders.add(res[1]);
702 results[j] = (SequenceI[]) res[0];
703 orders[j] = (AlignmentOrder) res[1];
705 // SequenceI[] alignment = input.getUpdated
712 Object[] newview = input.getUpdatedView(results, orders, getGapChar());
713 // trash references to original result data
714 for (int j = 0; j < jobs.length; j++)
719 SequenceI[] alignment = (SequenceI[]) newview[0];
720 ColumnSelection columnselection = (ColumnSelection) newview[1];
721 Alignment al = new Alignment(alignment);
722 // TODO: add 'provenance' property to alignment from the method notes
723 if (lastProgram!=null) {
724 al.setProperty("Alignment Program", lastProgram);
726 // accompanying each subjob
729 al.setDataset(dataset);
732 propagateDatasetMappings(al);
733 // JBNote- TODO: warn user if a block is input rather than aligned data ?
737 AlignFrame af = new AlignFrame(al, columnselection,
738 AlignFrame.DEFAULT_WIDTH, AlignFrame.DEFAULT_HEIGHT);
740 // initialise with same renderer settings as in parent alignframe.
741 af.getFeatureRenderer().transferSettings(this.featureSettings);
743 if (alorders.size() > 0)
745 if (alorders.size() == 1)
747 af.addSortByOrderMenuItem(WebServiceName + " Ordering",
748 (AlignmentOrder) alorders.get(0));
752 // construct a non-redundant ordering set
753 Vector names = new Vector();
754 for (int i = 0, l = alorders.size(); i < l; i++)
756 String orderName = new String(" Region " + i);
761 if (((AlignmentOrder) alorders.get(i))
762 .equals(((AlignmentOrder) alorders.get(j))))
766 orderName += "," + j;
774 if (i == 0 && j == 1)
776 names.add(new String(""));
780 names.add(orderName);
783 for (int i = 0, l = alorders.size(); i < l; i++)
785 af.addSortByOrderMenuItem(WebServiceName
786 + ((String) names.get(i)) + " Ordering",
787 (AlignmentOrder) alorders.get(i));
792 Desktop.addInternalFrame(af, alTitle, AlignFrame.DEFAULT_WIDTH,
793 AlignFrame.DEFAULT_HEIGHT);
798 System.out.println("MERGE WITH OLD FRAME");
799 // TODO: modify alignment in original frame, replacing old for new
800 // alignment using the commands.EditCommand model to ensure the update can
805 public boolean canMergeResults()