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;
353 String alTitle; // name which will be used to form new alignment window.
355 Alignment dataset; // dataset to which the new alignment will be
359 @SuppressWarnings("unchecked")
363 * set basic options for this (group) of Msa jobs
370 MsaWSThread(MsaWS server, String wsUrl,
371 WebserviceInfo wsinfo, jalview.gui.AlignFrame alFrame,
372 AlignmentView alview, String wsname, boolean subgaps,
375 super(alFrame, wsinfo, alview, wsname, wsUrl);
376 this.server = server;
377 this.submitGaps = subgaps;
378 this.preserveOrder = presorder;
382 * create one or more Msa jobs to align visible seuqences in _msa
395 MsaWSThread(MsaWS server2, Preset preset, List<Argument> paramset, String wsUrl,
396 WebserviceInfo wsinfo, jalview.gui.AlignFrame alFrame,
397 String wsname, String title, AlignmentView _msa, boolean subgaps,
398 boolean presorder, Alignment seqset)
400 this(server2,wsUrl, wsinfo, alFrame, _msa, wsname, subgaps, presorder);
401 OutputHeader = wsInfo.getProgressText();
405 SequenceI[][] conmsa = _msa.getVisibleContigs('-');
408 int njobs = conmsa.length;
409 jobs = new MsaWSJob[njobs];
410 for (int j = 0; j < njobs; j++)
414 jobs[j] = new MsaWSJob(wsinfo.addJobPane(), conmsa[j]);
418 jobs[j] = new MsaWSJob(0, conmsa[j]);
420 ((MsaWSJob) jobs[j]).preset = preset;
421 ((MsaWSJob) jobs[j]).arguments = paramset;
425 .setProgressName("region " + jobs[j].getJobnum(),
426 jobs[j].getJobnum());
428 wsinfo.setProgressText(jobs[j].getJobnum(), OutputHeader);
433 public boolean isCancellable()
438 public void cancelJob()
440 if (!jobComplete && jobs != null)
442 boolean cancelled = true;
443 for (int job = 0; job < jobs.length; job++)
445 if (jobs[job].isSubmitted() && !jobs[job].isSubjobComplete())
447 String cancelledMessage = "";
450 boolean cancelledJob = server
451 .cancelJob(jobs[job].getJobId());
455 cancelledMessage = "Job cancelled.";
456 ((MsaWSJob) jobs[job]).cancel(); // TODO: refactor to avoid this ugliness -
457 wsInfo.setStatus(jobs[job].getJobnum(),
458 WebserviceInfo.STATE_CANCELLED_OK);
462 // VALID UNSTOPPABLE JOB
463 cancelledMessage += "Server cannot cancel this job. just close the window.\n";
465 // wsInfo.setStatus(jobs[job].jobnum,
466 // WebserviceInfo.STATE_RUNNING);
468 } catch (Exception exc)
470 cancelledMessage += ("\nProblems cancelling the job : Exception received...\n"
473 "Exception whilst cancelling " + jobs[job].getJobId(), exc);
475 wsInfo.setProgressText(jobs[job].getJobnum(), OutputHeader
476 + cancelledMessage + "\n");
481 wsInfo.setStatus(WebserviceInfo.STATE_CANCELLED_OK);
484 this.interrupt(); // kick thread to update job states.
491 .setProgressText(OutputHeader
492 + "Server cannot cancel this job because it has not been submitted properly. just close the window.\n");
497 public void pollJob(AWsJob job) throws Exception
499 // TODO: investigate if we still need to cast here in J1.6
500 MsaWSJob j=((MsaWSJob) job);
501 // 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.
502 j.setjobStatus(server.getJobStatus(job.getJobId()));
503 updateJobProgress(j);
505 protected void updateJobProgress(MsaWSJob j) throws Exception {
506 StringBuffer response = j.jobProgress;
507 long lastchunk = j.getLastChunk();
509 j.setLastChunk(lastchunk);
510 ChunkHolder chunk = server.pullExecStatistics(j.getJobId(), lastchunk);
512 response.append(chunk.getChunk());
513 lastchunk = chunk.getNextPosition();
515 } while (lastchunk>=0 && j.getLastChunk()!=lastchunk);
518 public void StartJob(AWsJob job)
520 // boiler plate template
521 if (!(job instanceof MsaWSJob))
523 throw new Error("StartJob(MsaWSJob) called on a WSJobInstance "
526 MsaWSJob j = (MsaWSJob) job;
529 if (Cache.log.isDebugEnabled())
531 Cache.log.debug("Tried to submit an already submitted job "
538 if (j.seqs == null || j.seqs.size()==0)
540 // special case - selection consisted entirely of empty sequences...
541 j.setjobStatus(JobStatus.FINISHED);
542 j.setStatus("Empty Alignment Job");
546 // TODO: get the parameters (if any) for this job and submit the job
547 if (j.arguments!=null && j.arguments.size()>0)
549 j.setJobId(server.customAlign(j.seqs, j.arguments));
553 j.setJobId(server.presetAlign(j.seqs, j.preset));
555 j.setJobId(server.align(j.seqs));
558 if (j.getJobId()!= null)
560 j.setSubmitted(true);
561 j.setSubjobComplete(false);
562 // System.out.println(WsURL + " Job Id '" + jobId + "'");
569 + " returned null string for job id, it probably cannot be contacted. Try again later ?");
571 } catch (Exception e)
573 // Boilerplate code here
574 // TODO: JBPNote catch timeout or other fault types explicitly
575 // For unexpected errors
577 .println(WebServiceName
578 + "Client: Failed to submit the sequences for alignment (probably a server side problem)\n"
579 + "When contacting Server:" + WsUrl + "\n");
580 e.printStackTrace(System.err);
581 j.setAllowedServerExceptions(0);
582 wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_SERVERERROR);
583 wsInfo.setStatus(j.getJobnum(), WebserviceInfo.STATE_STOPPED_SERVERERROR);
587 "Failed to submit sequences for alignment.\n"
588 + "It is most likely that there is a problem with the server.\n"
589 + "Just close the window\n");
591 // e.printStackTrace(); // TODO: JBPNote DEBUG
596 public void parseResult()
598 int results = 0; // number of result sets received
599 JobStateSummary finalState = new JobStateSummary();
602 for (int j = 0; j < jobs.length; j++)
604 MsaWSJob msjob = ((MsaWSJob) jobs[j]);
605 if (jobs[j].isFinished() && msjob.alignment==null)
608 updateJobProgress(msjob);
609 } catch (Exception e)
611 Cache.log.warn("Exception when retrieving remaining Job progress data for job "+msjob.getJobId()+" on server "+WsUrl);
614 if (Cache.log.isDebugEnabled())
616 System.out.println("Job Execution file for job: "+msjob.getJobId()+" on server "+WsUrl);
617 System.out.println(msjob.getStatus());
618 System.out.println("*** End of status");
622 msjob.alignment = server.getResult(msjob.getJobId());
626 Cache.log.error("Couldn't get Alignment for job.",e);
629 finalState.updateJobPanelState(wsInfo, OutputHeader, jobs[j]);
630 if (jobs[j].isSubmitted() && jobs[j].isSubjobComplete()
631 && jobs[j].hasResults())
634 compbio.data.sequence.Alignment alignment = ((MsaWSJob) jobs[j]).alignment;
635 if (alignment != null)
637 wsInfo.appendProgressText(jobs[j].getJobnum(),
638 "\nAlignment Object Method Notes\n");
639 wsInfo.appendProgressText(jobs[j].getJobnum(), "Calculated with "+alignment.getMetadata().getProgram().toString());
640 // JBPNote The returned files from a webservice could be
641 // hidden behind icons in the monitor window that,
642 // when clicked, pop up their corresponding data
646 } catch (Exception ex)
649 Cache.log.error("Unexpected exception when processing results for "
651 wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_ERROR);
655 wsInfo.showResultsNewFrame
656 .addActionListener(new java.awt.event.ActionListener()
658 public void actionPerformed(java.awt.event.ActionEvent evt)
660 displayResults(true);
664 .addActionListener(new java.awt.event.ActionListener()
666 public void actionPerformed(java.awt.event.ActionEvent evt)
668 displayResults(false);
671 wsInfo.setResultsReady();
675 wsInfo.setFinishedNoResults();
679 void displayResults(boolean newFrame)
681 // view input or result data for each block
682 Vector alorders = new Vector();
683 SequenceI[][] results = new SequenceI[jobs.length][];
684 AlignmentOrder[] orders = new AlignmentOrder[jobs.length];
685 String lastProgram = null;
687 for (int j = 0; j < jobs.length; j++)
689 if (jobs[j].hasResults())
691 msjob = (MsaWSJob)jobs[j];
692 Object[] res = msjob.getAlignment();
693 lastProgram = msjob.alignment.getMetadata().getProgram().name();
694 alorders.add(res[1]);
695 results[j] = (SequenceI[]) res[0];
696 orders[j] = (AlignmentOrder) res[1];
698 // SequenceI[] alignment = input.getUpdated
705 Object[] newview = input.getUpdatedView(results, orders, getGapChar());
706 // trash references to original result data
707 for (int j = 0; j < jobs.length; j++)
712 SequenceI[] alignment = (SequenceI[]) newview[0];
713 ColumnSelection columnselection = (ColumnSelection) newview[1];
714 Alignment al = new Alignment(alignment);
715 // TODO: add 'provenance' property to alignment from the method notes
716 if (lastProgram!=null) {
717 al.setProperty("Alignment Program", lastProgram);
719 // accompanying each subjob
722 al.setDataset(dataset);
725 propagateDatasetMappings(al);
726 // JBNote- TODO: warn user if a block is input rather than aligned data ?
730 AlignFrame af = new AlignFrame(al, columnselection,
731 AlignFrame.DEFAULT_WIDTH, AlignFrame.DEFAULT_HEIGHT);
733 // initialise with same renderer settings as in parent alignframe.
734 af.getFeatureRenderer().transferSettings(this.featureSettings);
736 if (alorders.size() > 0)
738 if (alorders.size() == 1)
740 af.addSortByOrderMenuItem(WebServiceName + " Ordering",
741 (AlignmentOrder) alorders.get(0));
745 // construct a non-redundant ordering set
746 Vector names = new Vector();
747 for (int i = 0, l = alorders.size(); i < l; i++)
749 String orderName = new String(" Region " + i);
754 if (((AlignmentOrder) alorders.get(i))
755 .equals(((AlignmentOrder) alorders.get(j))))
759 orderName += "," + j;
767 if (i == 0 && j == 1)
769 names.add(new String(""));
773 names.add(orderName);
776 for (int i = 0, l = alorders.size(); i < l; i++)
778 af.addSortByOrderMenuItem(WebServiceName
779 + ((String) names.get(i)) + " Ordering",
780 (AlignmentOrder) alorders.get(i));
785 Desktop.addInternalFrame(af, alTitle, AlignFrame.DEFAULT_WIDTH,
786 AlignFrame.DEFAULT_HEIGHT);
791 System.out.println("MERGE WITH OLD FRAME");
792 // TODO: modify alignment in original frame, replacing old for new
793 // alignment using the commands.EditCommand model to ensure the update can
798 public boolean canMergeResults()