2 * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3 * Copyright (C) $$Year-Rel$$ The Jalview Authors
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
10 * of the License, or (at your option) any later version.
12 * Jalview is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty
14 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15 * PURPOSE. See the GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with Jalview. If not, see <http://www.gnu.org/licenses/>.
19 * The Jalview Authors are detailed in the 'AUTHORS' file.
21 package jalview.ws.gui;
23 import jalview.bin.Cache;
24 import jalview.bin.Console;
25 import jalview.datamodel.Alignment;
26 import jalview.datamodel.AlignmentI;
27 import jalview.datamodel.AlignmentOrder;
28 import jalview.datamodel.AlignmentView;
29 import jalview.datamodel.HiddenColumns;
30 import jalview.datamodel.SequenceI;
31 import jalview.gui.AlignFrame;
32 import jalview.gui.Desktop;
33 import jalview.gui.SplitFrame;
34 import jalview.gui.WebserviceInfo;
35 import jalview.util.MessageManager;
36 import jalview.ws.AWSThread;
37 import jalview.ws.AWsJob;
38 import jalview.ws.JobStateSummary;
39 import jalview.ws.WSClientI;
40 import jalview.ws.api.CancellableI;
41 import jalview.ws.api.JobId;
42 import jalview.ws.api.MultipleSequenceAlignmentI;
43 import jalview.ws.gui.WsJob.JobState;
44 import jalview.ws.params.ArgumentI;
45 import jalview.ws.params.WsParamSetI;
47 import java.util.ArrayList;
48 import java.util.List;
50 import javax.swing.JInternalFrame;
52 public class MsaWSThread extends AWSThread implements WSClientI
54 boolean submitGaps = false; // pass sequences including gaps to alignment
58 boolean preserveOrder = true; // and always store and recover sequence
63 String alTitle; // name which will be used to form new alignment window.
65 AlignmentI dataset; // dataset to which the new alignment will be
69 MultipleSequenceAlignmentI server = null;
72 * set basic options for this (group) of Msa jobs
79 private MsaWSThread(MultipleSequenceAlignmentI server, String wsUrl,
80 WebserviceInfo wsinfo,
81 jalview.gui.AlignFrame alFrame, AlignmentView alview,
82 String wsname, boolean subgaps, boolean presorder)
84 super(alFrame, wsinfo, alview, wsname, wsUrl);
86 this.submitGaps = subgaps;
87 this.preserveOrder = presorder;
91 * create one or more Msa jobs to align visible sequences in _msa
104 public MsaWSThread(MultipleSequenceAlignmentI server2, WsParamSetI preset,
105 List<ArgumentI> paramset,
106 String wsUrl, WebserviceInfo wsinfo,
107 jalview.gui.AlignFrame alFrame, String wsname, String title,
108 AlignmentView _msa, boolean subgaps, boolean presorder,
111 this(server2, wsUrl, wsinfo, alFrame, _msa, wsname, subgaps, presorder);
112 OutputHeader = wsInfo.getProgressText();
116 SequenceI[][] conmsa = _msa.getVisibleContigs('-');
119 int nvalid = 0, njobs = conmsa.length;
120 jobs = new AWsJob[njobs];
121 for (int j = 0; j < njobs; j++)
125 jobs[j] = new MsaWSJob(this, wsinfo.addJobPane(), conmsa[j]);
129 jobs[j] = new MsaWSJob(this, 0, conmsa[j]);
131 if (jobs[j].hasValidInput())
135 jobs[j].setPreset(preset);
136 jobs[j].setArguments(paramset);
137 ((MsaWSJob) jobs[j]).alignmentProgram = wsname;
140 wsinfo.setProgressName("region " + jobs[j].getJobnum(),
141 jobs[j].getJobnum());
143 wsinfo.setProgressText(jobs[j].getJobnum(), OutputHeader);
145 validInput = nvalid > 0;
149 boolean validInput = false;
153 * @return true if the thread will perform a calculation
155 public boolean hasValidInput()
161 public boolean isCancellable()
163 return server instanceof CancellableI;
167 public void cancelJob()
169 // TODO decide if when some jobs are not cancellable to shut down the thread
171 if (!jobComplete && jobs != null)
173 boolean cancelled = true;
174 for (int job = 0; job < jobs.length; job++)
176 if (jobs[job].isSubmitted() && !jobs[job].isSubjobComplete())
178 String cancelledMessage = "";
181 CancellableI service = (CancellableI) server;
182 boolean cancelledJob = service.cancel((WsJob) jobs[job]);
186 cancelledMessage = "Job cancelled.";
187 ((MsaWSJob) jobs[job]).cancel(); // TODO: refactor to avoid this
189 wsInfo.setStatus(jobs[job].getJobnum(),
190 WebserviceInfo.STATE_CANCELLED_OK);
194 // VALID UNSTOPPABLE JOB
195 cancelledMessage += "Server cannot cancel this job. just close the window.\n";
197 // wsInfo.setStatus(jobs[job].jobnum,
198 // WebserviceInfo.STATE_RUNNING);
200 } catch (Exception exc)
202 cancelledMessage += ("\nProblems cancelling the job : Exception received...\n"
205 "Exception whilst cancelling " + jobs[job].getJobId(),
208 wsInfo.setProgressText(jobs[job].getJobnum(),
209 OutputHeader + cancelledMessage + "\n");
213 // if we hadn't submitted then just mark the job as cancelled.
214 jobs[job].setSubjobComplete(true);
215 wsInfo.setStatus(jobs[job].getJobnum(),
216 WebserviceInfo.STATE_CANCELLED_OK);
222 wsInfo.setStatus(WebserviceInfo.STATE_CANCELLED_OK);
225 this.interrupt(); // kick thread to update job states.
231 wsInfo.setProgressText(OutputHeader
232 + "Server cannot cancel this job because it has not been submitted properly. just close the window.\n");
238 public void pollJob(AWsJob job) throws Exception
240 // TODO: investigate if we still need to cast here in J1.6
241 MsaWSJob j = ((MsaWSJob) job);
242 // this is standard code, but since the interface doesn't comprise of a
243 // basic one that implements (getJobStatus, pullExecStatistics) we have to
244 // repeat the code for all jw2s services.
245 server.updateStatus(j);
246 server.updateJobProgress(j);
250 public void StartJob(AWsJob job)
252 Exception lex = null;
253 // boiler plate template
254 if (!(job instanceof MsaWSJob))
256 throw new Error(MessageManager.formatMessage(
257 "error.implementation_error_msawbjob_called", new String[]
258 { job.getClass().toString() }));
260 MsaWSJob j = (MsaWSJob) job;
263 if (Console.isDebugEnabled())
266 "Tried to submit an already submitted job " + j.getJobId());
272 if (j.seqs == null || j.seqs.size() == 0)
274 // special case - selection consisted entirely of empty sequences...
275 j.setState(JobState.FINISHED);
276 j.setStatus(MessageManager.getString("label.empty_alignment_job"));
280 j.addInitialStatus(); // list the presets/parameters used for the job in
284 JobId jobHandle = server.align(j.seqs, j.getPreset(),
286 if (jobHandle != null)
288 j.setJobHandle(jobHandle);
291 } catch (Throwable throwable)
293 Console.error("failed to send the job to the alignment server", throwable);
294 if (!server.handleSubmitError(throwable, j, wsInfo))
296 if (throwable instanceof Exception)
298 throw ((Exception) throwable);
300 if (throwable instanceof Error)
302 throw ((Error) throwable);
308 if (j.getJobId() != null)
310 j.setSubmitted(true);
311 j.setSubjobComplete(false);
312 // System.out.println(WsURL + " Job Id '" + jobId + "'");
317 throw new Exception(MessageManager.formatMessage(
318 "exception.web_service_returned_null_try_later",
328 // For unexpected errors
329 System.err.println(WebServiceName
330 + "Client: Failed to submit the sequences for alignment (probably a server side problem)\n"
331 + "When contacting Server:" + WsUrl + "\n");
332 e.printStackTrace(System.err);
333 wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_SERVERERROR);
334 wsInfo.setStatus(j.getJobnum(),
335 WebserviceInfo.STATE_STOPPED_SERVERERROR);
336 } catch (Exception e)
338 // For unexpected errors
339 System.err.println(WebServiceName
340 + "Client: Failed to submit the sequences for alignment (probably a server side problem)\n"
341 + "When contacting Server:" + WsUrl + "\n");
342 e.printStackTrace(System.err);
343 wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_SERVERERROR);
344 wsInfo.setStatus(j.getJobnum(),
345 WebserviceInfo.STATE_STOPPED_SERVERERROR);
348 if (!j.isSubmitted())
350 // Boilerplate code here
351 // TODO: JBPNote catch timeout or other fault types explicitly
353 j.setAllowedServerExceptions(0);
354 wsInfo.appendProgressText(j.getJobnum(), MessageManager.getString(
355 "info.failed_to_submit_sequences_for_alignment"));
361 public void parseResult()
363 long progbar = System.currentTimeMillis();
364 wsInfo.setProgressBar(
365 MessageManager.getString("status.collecting_job_results"),
367 int results = 0; // number of result sets received
368 JobStateSummary finalState = new JobStateSummary();
371 for (int j = 0; j < jobs.length; j++)
373 MsaWSJob msjob = ((MsaWSJob) jobs[j]);
374 if (jobs[j].isFinished() && msjob.alignment == null)
376 int nunchanged = 3, nexcept = 3;
377 boolean jpchanged = false, jpex = false;
382 jpchanged = server.updateJobProgress(msjob);
388 } catch (Exception e)
392 "Exception when retrieving remaining Job progress data for job "
393 + msjob.getJobId() + " on server " + WsUrl);
397 // set flag remember that we've had an exception.
405 Thread.sleep(jpex ? 2400 : 1200); // wait a bit longer if we
406 // experienced an exception.
407 } catch (Exception ex)
413 } while (nunchanged > 0 && nexcept > 0);
415 if (Console.isDebugEnabled())
417 System.out.println("Job Execution file for job: "
418 + msjob.getJobId() + " on server " + WsUrl);
419 System.out.println(msjob.getStatus());
420 System.out.println("*** End of status");
423 ///// jabaws specific(ish) Get Result from Server when available
426 msjob.alignment = server.getAlignmentFor(msjob.getJobHandle());
427 } catch (Exception e)
429 if (!server.handleCollectionException(e, msjob, wsInfo))
431 Console.error("Couldn't get Alignment for job.", e);
432 // TODO: Increment count and retry ?
433 msjob.setState(JobState.SERVERERROR);
437 finalState.updateJobPanelState(wsInfo, OutputHeader, jobs[j]);
438 if (jobs[j].isSubmitted() && jobs[j].isSubjobComplete()
439 && jobs[j].hasResults())
444 } catch (Exception ex)
448 "Unexpected exception when processing results for " + alTitle,
450 wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_ERROR);
454 wsInfo.showResultsNewFrame
455 .addActionListener(new java.awt.event.ActionListener()
458 public void actionPerformed(java.awt.event.ActionEvent evt)
460 displayResults(true);
464 .addActionListener(new java.awt.event.ActionListener()
467 public void actionPerformed(java.awt.event.ActionEvent evt)
469 displayResults(false);
472 wsInfo.setResultsReady();
476 wsInfo.setFinishedNoResults();
478 updateGlobalStatus(finalState);
479 wsInfo.setProgressBar(null, progbar);
483 * Display alignment results in a new frame (or - not currently supported -
484 * added to an existing alignment).
488 void displayResults(boolean newFrame)
490 // view input or result data for each block
491 List<AlignmentOrder> alorders = new ArrayList<>();
492 SequenceI[][] results = new SequenceI[jobs.length][];
493 AlignmentOrder[] orders = new AlignmentOrder[jobs.length];
494 String lastProgram = null;
496 for (int j = 0; j < jobs.length; j++)
498 if (jobs[j].hasResults())
500 msjob = (MsaWSJob) jobs[j];
501 Object[] res = msjob.getAlignment();
502 lastProgram = msjob.getAlignmentProgram();
503 alorders.add((AlignmentOrder) res[1]);
504 results[j] = (SequenceI[]) res[0];
505 orders[j] = (AlignmentOrder) res[1];
507 // SequenceI[] alignment = input.getUpdated
514 Object[] newview = input.getUpdatedView(results, orders, getGapChar());
515 // trash references to original result data
516 for (int j = 0; j < jobs.length; j++)
521 SequenceI[] alignment = (SequenceI[]) newview[0];
522 HiddenColumns hidden = (HiddenColumns) newview[1];
523 Alignment al = new Alignment(alignment);
524 // TODO: add 'provenance' property to alignment from the method notes
525 if (lastProgram != null)
527 al.setProperty("Alignment Program", lastProgram);
529 // accompanying each subjob
532 al.setDataset(dataset);
535 propagateDatasetMappings(al);
536 // JBNote- TODO: warn user if a block is input rather than aligned data ?
540 displayInNewFrame(al, alorders, hidden);
545 // TODO 2.9.x feature
546 System.out.println("MERGE WITH OLD FRAME");
547 // TODO: modify alignment in original frame, replacing old for new
548 // alignment using the commands.EditCommand model to ensure the update can
554 * Display the alignment result in a new frame.
558 * @param columnselection
560 protected void displayInNewFrame(AlignmentI al,
561 List<AlignmentOrder> alorders, HiddenColumns hidden)
563 AlignFrame af = new AlignFrame(al, hidden, AlignFrame.DEFAULT_WIDTH,
564 AlignFrame.DEFAULT_HEIGHT);
566 // initialise with same renderer settings as in parent alignframe.
567 af.getFeatureRenderer().transferSettings(this.featureSettings);
569 if (alorders.size() > 0)
571 addSortByMenuItems(af, alorders);
574 // TODO: refactor retrieve and show as new splitFrame as Desktop method
577 * If alignment was requested from one half of a SplitFrame, show in a
578 * SplitFrame with the other pane similarly aligned.
580 AlignFrame requestedBy = getRequestingAlignFrame();
581 if (requestedBy != null && requestedBy.getSplitViewContainer() != null
582 && requestedBy.getSplitViewContainer()
583 .getComplement(requestedBy) != null)
585 AlignmentI complement = requestedBy.getSplitViewContainer()
586 .getComplement(requestedBy);
587 String complementTitle = requestedBy.getSplitViewContainer()
588 .getComplementTitle(requestedBy);
589 // becomes null if the alignment window was closed before the alignment
591 AlignmentI copyComplement = new Alignment(complement);
592 // todo should this be done by copy constructor?
593 copyComplement.setGapCharacter(complement.getGapCharacter());
594 // share the same dataset (and the mappings it holds)
595 copyComplement.setDataset(complement.getDataset());
596 copyComplement.alignAs(al);
597 if (copyComplement.getHeight() > 0)
599 af.setTitle(alTitle);
600 AlignFrame af2 = new AlignFrame(copyComplement,
601 AlignFrame.DEFAULT_WIDTH, AlignFrame.DEFAULT_HEIGHT);
602 af2.setTitle(complementTitle);
603 String linkedTitle = MessageManager
604 .getString("label.linked_view_title");
605 JInternalFrame splitFrame = new SplitFrame(
606 al.isNucleotide() ? af : af2, al.isNucleotide() ? af2 : af);
607 Desktop.addInternalFrame(splitFrame, linkedTitle, -1, -1);
613 * Not from SplitFrame, or failed to created a complementary alignment
615 Desktop.addInternalFrame(af, alTitle, AlignFrame.DEFAULT_WIDTH,
616 AlignFrame.DEFAULT_HEIGHT);
620 * Add sort order options to the AlignFrame menus.
625 protected void addSortByMenuItems(AlignFrame af,
626 List<AlignmentOrder> alorders)
629 if (alorders.size() == 1)
631 af.addSortByOrderMenuItem(WebServiceName + " Ordering",
636 // construct a non-redundant ordering set
637 List<String> names = new ArrayList<>();
638 for (int i = 0, l = alorders.size(); i < l; i++)
640 String orderName = " Region " + i;
645 if (alorders.get(i).equals(alorders.get(j)))
649 orderName += "," + j;
657 if (i == 0 && j == 1)
663 names.add(orderName);
666 for (int i = 0, l = alorders.size(); i < l; i++)
668 af.addSortByOrderMenuItem(
669 WebServiceName + (names.get(i)) + " Ordering",
676 public boolean canMergeResults()