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
74 List<Argument> arguments = null;
79 ArrayList<compbio.data.sequence.FastaSequence> seqs = new ArrayList<compbio.data.sequence.FastaSequence>();
84 compbio.data.sequence.Alignment alignment;
86 // set if the job didn't get run - then the input is simply returned to the
88 private boolean returnInput = false;
98 public MsaWSJob(int jobNum, SequenceI[] inSeqs)
100 this.jobnum = jobNum;
101 if (!prepareInput(inSeqs, 2))
104 subjobComplete = true;
110 Hashtable<String, Map> SeqNames = new Hashtable();
112 Vector<String[]> emptySeqs = new Vector();
115 * prepare input sequences for MsaWS service
118 * jalview sequences to be prepared
120 * minimum number of residues required for this MsaWS service
121 * @return true if seqs contains sequences to be submitted to service.
123 // TODO: return compbio.seqs list or nothing to indicate validity.
124 private boolean prepareInput(SequenceI[] seqs, int minlen)
130 "Implementation error: minlen must be zero or more.");
132 for (int i = 0; i < seqs.length; i++)
134 if (seqs[i].getEnd() - seqs[i].getStart() > minlen - 1)
139 boolean valid = nseqs > 1; // need at least two seqs
140 compbio.data.sequence.FastaSequence seq;
141 for (int i = 0, n = 0; i < seqs.length; i++)
144 String newname = jalview.analysis.SeqsetUtils.unique_name(i); // same
148 SeqNames.put(newname,
149 jalview.analysis.SeqsetUtils.SeqCharacterHash(seqs[i]));
150 if (valid && seqs[i].getEnd() - seqs[i].getStart() > minlen - 1)
152 // make new input sequence with or without gaps
153 seq = new compbio.data.sequence.FastaSequence(newname,
154 (submitGaps) ? seqs[i].getSequenceAsString()
155 : AlignSeq.extractGaps(
156 jalview.util.Comparison.GapChars,
157 seqs[i].getSequenceAsString()));
163 if (seqs[i].getEnd() >= seqs[i].getStart())
165 empty = (submitGaps) ? seqs[i].getSequenceAsString() : AlignSeq
166 .extractGaps(jalview.util.Comparison.GapChars,
167 seqs[i].getSequenceAsString());
169 emptySeqs.add(new String[]
178 * @return true if getAlignment will return a valid alignment result.
180 public boolean hasResults()
184 && (alignment != null || (emptySeqs != null && emptySeqs
194 * get the alignment including any empty sequences in the original order
195 * with original ids. Caller must access the alignment.getMetadata() object
196 * to annotate the final result passsed to the user.
198 * @return { SequenceI[], AlignmentOrder }
200 public Object[] getAlignment()
202 // is this a generic subjob or a Jws2 specific Object[] return signature
205 SequenceI[] alseqs = null;
206 char alseq_gapchar = '-';
208 if (alignment.getSequences().size() > 0)
210 alseqs = new SequenceI[alignment.getSequences().size()];
211 for (compbio.data.sequence.FastaSequence seq : alignment
214 alseqs[alseq_l++] = new Sequence(seq.getId(), seq.getSequence());
216 alseq_gapchar = alignment.getMetadata().getGapchar();
219 // add in the empty seqs.
220 if (emptySeqs.size() > 0)
222 SequenceI[] t_alseqs = new SequenceI[alseq_l + emptySeqs.size()];
227 for (i = 0, w = alseqs[0].getLength(); i < alseq_l; i++)
229 if (w < alseqs[i].getLength())
231 w = alseqs[i].getLength();
233 t_alseqs[i] = alseqs[i];
237 // check that aligned width is at least as wide as emptySeqs width.
239 for (i = 0, w = emptySeqs.size(); i < w; i++)
241 String[] es = (String[]) emptySeqs.get(i);
242 if (es != null && es[1] != null)
244 int sw = es[1].length();
251 // make a gapped string.
252 StringBuffer insbuff = new StringBuffer(w);
253 for (i = 0; i < nw; i++)
255 insbuff.append(alseq_gapchar);
259 for (i = 0; i < alseq_l; i++)
261 int sw = t_alseqs[i].getLength();
265 alseqs[i].setSequence(t_alseqs[i].getSequenceAsString()
266 + insbuff.substring(0, sw - nw));
270 for (i = 0, w = emptySeqs.size(); i < w; i++)
272 String[] es = (String[]) emptySeqs.get(i);
275 t_alseqs[i + alseq_l] = new jalview.datamodel.Sequence(es[0],
276 insbuff.toString(), 1, 0);
280 if (es[1].length() < nw)
282 t_alseqs[i + alseq_l] = new jalview.datamodel.Sequence(
284 es[1] + insbuff.substring(0, nw - es[1].length()),
285 1, 1 + es[1].length());
289 t_alseqs[i + alseq_l] = new jalview.datamodel.Sequence(
296 AlignmentOrder msaorder = new AlignmentOrder(alseqs);
297 // always recover the order - makes parseResult()'s life easier.
298 jalview.analysis.AlignmentSorter.recoverOrder(alseqs);
299 // account for any missing sequences
300 jalview.analysis.SeqsetUtils.deuniquify(SeqNames, alseqs);
302 { alseqs, msaorder };
308 * mark subjob as cancelled and set result object appropriatly
313 subjobComplete = true;
319 * @return boolean true if job can be submitted.
321 public boolean hasValidInput()
323 // TODO: get attributes for this MsaWS instance to check if it can do two
324 // sequence alignment.
325 if (seqs != null && seqs.size() >= 2) // two or more sequences is valid ?
332 StringBuffer jobProgress = new StringBuffer();
334 public void setStatus(String string)
336 jobProgress.setLength(0);
337 jobProgress.append(string);
341 public String getStatus()
343 return jobProgress.toString();
347 public boolean hasStatus()
349 return jobProgress != null;
353 * @return the lastChunk
355 public long getLastChunk()
362 * the lastChunk to set
364 public void setLastChunk(long lastChunk)
366 this.lastChunk = lastChunk;
369 String alignmentProgram = null;
371 public String getAlignmentProgram()
373 return alignmentProgram;
378 String alTitle; // name which will be used to form new alignment window.
380 Alignment dataset; // dataset to which the new alignment will be
384 @SuppressWarnings("unchecked")
388 * set basic options for this (group) of Msa jobs
395 MsaWSThread(MsaWS server, String wsUrl, WebserviceInfo wsinfo,
396 jalview.gui.AlignFrame alFrame, AlignmentView alview,
397 String wsname, boolean subgaps, boolean presorder)
399 super(alFrame, wsinfo, alview, wsname, wsUrl);
400 this.server = server;
401 this.submitGaps = subgaps;
402 this.preserveOrder = presorder;
406 * create one or more Msa jobs to align visible seuqences in _msa
419 MsaWSThread(MsaWS server2, Preset preset, List<Argument> paramset,
420 String wsUrl, WebserviceInfo wsinfo,
421 jalview.gui.AlignFrame alFrame, String wsname, String title,
422 AlignmentView _msa, boolean subgaps, boolean presorder,
425 this(server2, wsUrl, wsinfo, alFrame, _msa, wsname, subgaps, presorder);
426 OutputHeader = wsInfo.getProgressText();
430 SequenceI[][] conmsa = _msa.getVisibleContigs('-');
433 int njobs = conmsa.length;
434 jobs = new MsaWSJob[njobs];
435 for (int j = 0; j < njobs; j++)
439 jobs[j] = new MsaWSJob(wsinfo.addJobPane(), conmsa[j]);
443 jobs[j] = new MsaWSJob(0, conmsa[j]);
445 ((MsaWSJob) jobs[j]).preset = preset;
446 ((MsaWSJob) jobs[j]).arguments = paramset;
447 ((MsaWSJob) jobs[j]).alignmentProgram = wsname;
450 wsinfo.setProgressName("region " + jobs[j].getJobnum(),
451 jobs[j].getJobnum());
453 wsinfo.setProgressText(jobs[j].getJobnum(), OutputHeader);
458 public boolean isCancellable()
463 public void cancelJob()
465 if (!jobComplete && jobs != null)
467 boolean cancelled = true;
468 for (int job = 0; job < jobs.length; job++)
470 if (jobs[job].isSubmitted() && !jobs[job].isSubjobComplete())
472 String cancelledMessage = "";
475 boolean cancelledJob = server.cancelJob(jobs[job].getJobId());
476 if (cancelledJob || true)
479 // if the Jaba server indicates the job can't be cancelled, its
480 // because its running on the server's local execution engine
481 // so we just close the window anyway.
482 cancelledMessage = "Job cancelled.";
483 ((MsaWSJob) jobs[job]).cancel(); // TODO: refactor to avoid this
485 wsInfo.setStatus(jobs[job].getJobnum(),
486 WebserviceInfo.STATE_CANCELLED_OK);
490 // VALID UNSTOPPABLE JOB
491 cancelledMessage += "Server cannot cancel this job. just close the window.\n";
493 // wsInfo.setStatus(jobs[job].jobnum,
494 // WebserviceInfo.STATE_RUNNING);
496 } catch (Exception exc)
498 cancelledMessage += ("\nProblems cancelling the job : Exception received...\n"
501 "Exception whilst cancelling " + jobs[job].getJobId(),
504 wsInfo.setProgressText(jobs[job].getJobnum(), OutputHeader
505 + cancelledMessage + "\n");
510 wsInfo.setStatus(WebserviceInfo.STATE_CANCELLED_OK);
513 this.interrupt(); // kick thread to update job states.
519 wsInfo.setProgressText(OutputHeader
520 + "Server cannot cancel this job because it has not been submitted properly. just close the window.\n");
525 public void pollJob(AWsJob job) throws Exception
527 // TODO: investigate if we still need to cast here in J1.6
528 MsaWSJob j = ((MsaWSJob) job);
529 // this is standard code, but since the interface doesn't comprise of a
530 // basic one that implements (getJobStatus, pullExecStatistics) we have to
531 // repeat the code for all jw2s services.
532 j.setjobStatus(server.getJobStatus(job.getJobId()));
533 updateJobProgress(j);
536 protected void updateJobProgress(MsaWSJob j) throws Exception
538 StringBuffer response = j.jobProgress;
539 long lastchunk = j.getLastChunk();
542 j.setLastChunk(lastchunk);
543 ChunkHolder chunk = server
544 .pullExecStatistics(j.getJobId(), lastchunk);
547 response.append(chunk.getChunk());
548 lastchunk = chunk.getNextPosition();
551 } while (lastchunk >= 0 && j.getLastChunk() != lastchunk);
554 public void StartJob(AWsJob job)
556 Exception lex = null;
557 // boiler plate template
558 if (!(job instanceof MsaWSJob))
560 throw new Error("StartJob(MsaWSJob) called on a WSJobInstance "
563 MsaWSJob j = (MsaWSJob) job;
566 if (Cache.log.isDebugEnabled())
568 Cache.log.debug("Tried to submit an already submitted job "
575 if (j.seqs == null || j.seqs.size() == 0)
577 // special case - selection consisted entirely of empty sequences...
578 j.setjobStatus(JobStatus.FINISHED);
579 j.setStatus("Empty Alignment Job");
583 // TODO: get the parameters (if any) for this job and submit the job
584 if (j.arguments != null && j.arguments.size() > 0)
586 StringBuffer pset = new StringBuffer();
587 for (Argument arg : j.arguments)
589 pset.append(arg.getName() + " " + arg.getDefaultValue() + "\n");
591 j.setStatus("Custom Parameters:\n" + pset.toString()
592 + "\nJob Output:\n");
593 j.setJobId(server.customAlign(j.seqs, j.arguments));
595 else if (j.preset != null)
597 j.setJobId(server.presetAlign(j.seqs, j.preset));
601 j.setJobId(server.align(j.seqs));
604 if (j.getJobId() != null)
606 j.setSubmitted(true);
607 j.setSubjobComplete(false);
608 // System.out.println(WsURL + " Job Id '" + jobId + "'");
616 + " returned null string for job id, it probably cannot be contacted. Try again later ?");
618 } catch (compbio.metadata.UnsupportedRuntimeException _lex)
621 wsInfo.appendProgressText("Job could not be run because the server doesn't support this program.\n"
622 + _lex.getMessage());
623 wsInfo.warnUser(_lex.getMessage(), "Service not supported!");
624 wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_SERVERERROR);
625 wsInfo.setStatus(j.getJobnum(),
626 WebserviceInfo.STATE_STOPPED_SERVERERROR);
627 } catch (compbio.metadata.LimitExceededException _lex)
630 wsInfo.appendProgressText("Job could not be run because it exceeded a hard limit on the server.\n"
631 + _lex.getMessage());
632 wsInfo.warnUser(_lex.getMessage(), "Input is too big!");
633 wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_ERROR);
634 wsInfo.setStatus(j.getJobnum(), WebserviceInfo.STATE_STOPPED_ERROR);
635 } catch (compbio.metadata.WrongParameterException _lex)
638 wsInfo.warnUser(_lex.getMessage(), "Invalid job parameter set!");
639 wsInfo.appendProgressText("Job could not be run because some of the parameter settings are not supported by the server.\n"
641 + "\nPlease check to make sure you have used the correct parameter set for this service!\n");
642 wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_ERROR);
643 wsInfo.setStatus(j.getJobnum(), WebserviceInfo.STATE_STOPPED_ERROR);
646 // For unexpected errors
648 .println(WebServiceName
649 + "Client: Failed to submit the sequences for alignment (probably a server side problem)\n"
650 + "When contacting Server:" + WsUrl + "\n");
651 e.printStackTrace(System.err);
652 wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_SERVERERROR);
653 wsInfo.setStatus(j.getJobnum(),
654 WebserviceInfo.STATE_STOPPED_SERVERERROR);
655 } catch (Exception e)
657 // For unexpected errors
659 .println(WebServiceName
660 + "Client: Failed to submit the sequences for alignment (probably a server side problem)\n"
661 + "When contacting Server:" + WsUrl + "\n");
662 e.printStackTrace(System.err);
663 wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_SERVERERROR);
664 wsInfo.setStatus(j.getJobnum(),
665 WebserviceInfo.STATE_STOPPED_SERVERERROR);
668 if (!j.isSubmitted())
670 // Boilerplate code here
671 // TODO: JBPNote catch timeout or other fault types explicitly
673 j.setAllowedServerExceptions(0);
674 wsInfo.appendProgressText(j.getJobnum(),
675 "Failed to submit sequences for alignment.\n"
676 + "Just close the window\n");
681 public void parseResult()
683 int results = 0; // number of result sets received
684 JobStateSummary finalState = new JobStateSummary();
687 for (int j = 0; j < jobs.length; j++)
689 MsaWSJob msjob = ((MsaWSJob) jobs[j]);
690 if (jobs[j].isFinished() && msjob.alignment == null)
694 updateJobProgress(msjob);
695 } catch (Exception e)
698 .warn("Exception when retrieving remaining Job progress data for job "
699 + msjob.getJobId() + " on server " + WsUrl);
702 if (Cache.log.isDebugEnabled())
704 System.out.println("Job Execution file for job: "
705 + msjob.getJobId() + " on server " + WsUrl);
706 System.out.println(msjob.getStatus());
707 System.out.println("*** End of status");
712 msjob.alignment = server.getResult(msjob.getJobId());
713 } catch (compbio.metadata.ResultNotAvailableException e)
715 // job has failed for some reason - probably due to invalid
718 .debug("Results not available for finished job - marking as broken job.",
720 msjob.setjobStatus(JobStatus.FAILED);
721 } catch (Exception e)
723 Cache.log.error("Couldn't get Alignment for job.", e);
724 // TODO: Increment count and retry ?
725 msjob.setjobStatus(JobStatus.UNDEFINED);
728 finalState.updateJobPanelState(wsInfo, OutputHeader, jobs[j]);
729 if (jobs[j].isSubmitted() && jobs[j].isSubjobComplete()
730 && jobs[j].hasResults())
733 compbio.data.sequence.Alignment alignment = ((MsaWSJob) jobs[j]).alignment;
734 if (alignment != null)
736 // server.close(jobs[j].getJobnum());
737 // wsInfo.appendProgressText(jobs[j].getJobnum(),
738 // "\nAlignment Object Method Notes\n");
739 // wsInfo.appendProgressText(jobs[j].getJobnum(),
740 // "Calculated with "+alignment.getMetadata().getProgram().toString());
741 // JBPNote The returned files from a webservice could be
742 // hidden behind icons in the monitor window that,
743 // when clicked, pop up their corresponding data
747 } catch (Exception ex)
750 Cache.log.error("Unexpected exception when processing results for "
752 wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_ERROR);
756 wsInfo.showResultsNewFrame
757 .addActionListener(new java.awt.event.ActionListener()
759 public void actionPerformed(java.awt.event.ActionEvent evt)
761 displayResults(true);
765 .addActionListener(new java.awt.event.ActionListener()
767 public void actionPerformed(java.awt.event.ActionEvent evt)
769 displayResults(false);
772 wsInfo.setResultsReady();
776 wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_ERROR);
777 wsInfo.setFinishedNoResults();
781 void displayResults(boolean newFrame)
783 // view input or result data for each block
784 Vector alorders = new Vector();
785 SequenceI[][] results = new SequenceI[jobs.length][];
786 AlignmentOrder[] orders = new AlignmentOrder[jobs.length];
787 String lastProgram = null;
789 for (int j = 0; j < jobs.length; j++)
791 if (jobs[j].hasResults())
793 msjob = (MsaWSJob) jobs[j];
794 Object[] res = msjob.getAlignment();
795 lastProgram = msjob.getAlignmentProgram();
796 alorders.add(res[1]);
797 results[j] = (SequenceI[]) res[0];
798 orders[j] = (AlignmentOrder) res[1];
800 // SequenceI[] alignment = input.getUpdated
807 Object[] newview = input.getUpdatedView(results, orders, getGapChar());
808 // trash references to original result data
809 for (int j = 0; j < jobs.length; j++)
814 SequenceI[] alignment = (SequenceI[]) newview[0];
815 ColumnSelection columnselection = (ColumnSelection) newview[1];
816 Alignment al = new Alignment(alignment);
817 // TODO: add 'provenance' property to alignment from the method notes
818 if (lastProgram != null)
820 al.setProperty("Alignment Program", lastProgram);
822 // accompanying each subjob
825 al.setDataset(dataset);
828 propagateDatasetMappings(al);
829 // JBNote- TODO: warn user if a block is input rather than aligned data ?
833 AlignFrame af = new AlignFrame(al, columnselection,
834 AlignFrame.DEFAULT_WIDTH, AlignFrame.DEFAULT_HEIGHT);
836 // initialise with same renderer settings as in parent alignframe.
837 af.getFeatureRenderer().transferSettings(this.featureSettings);
839 if (alorders.size() > 0)
841 if (alorders.size() == 1)
843 af.addSortByOrderMenuItem(WebServiceName + " Ordering",
844 (AlignmentOrder) alorders.get(0));
848 // construct a non-redundant ordering set
849 Vector names = new Vector();
850 for (int i = 0, l = alorders.size(); i < l; i++)
852 String orderName = new String(" Region " + i);
857 if (((AlignmentOrder) alorders.get(i))
858 .equals(((AlignmentOrder) alorders.get(j))))
862 orderName += "," + j;
870 if (i == 0 && j == 1)
872 names.add(new String(""));
876 names.add(orderName);
879 for (int i = 0, l = alorders.size(); i < l; i++)
881 af.addSortByOrderMenuItem(
882 WebServiceName + ((String) names.get(i)) + " Ordering",
883 (AlignmentOrder) alorders.get(i));
888 Desktop.addInternalFrame(af, alTitle, AlignFrame.DEFAULT_WIDTH,
889 AlignFrame.DEFAULT_HEIGHT);
894 System.out.println("MERGE WITH OLD FRAME");
895 // TODO: modify alignment in original frame, replacing old for new
896 // alignment using the commands.EditCommand model to ensure the update can
901 public boolean canMergeResults()