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.datamodel.Alignment;
25 import jalview.datamodel.AlignmentI;
26 import jalview.datamodel.AlignmentOrder;
27 import jalview.datamodel.AlignmentView;
28 import jalview.datamodel.HiddenColumns;
29 import jalview.datamodel.SequenceI;
30 import jalview.gui.AlignFrame;
31 import jalview.gui.Desktop;
32 import jalview.gui.SplitFrame;
33 import jalview.gui.WebserviceInfo;
34 import jalview.util.MessageManager;
35 import jalview.ws.AWSThread;
36 import jalview.ws.AWsJob;
37 import jalview.ws.JobStateSummary;
38 import jalview.ws.WSClientI;
39 import jalview.ws.api.CancellableI;
40 import jalview.ws.api.JobId;
41 import jalview.ws.api.MultipleSequenceAlignmentI;
42 import jalview.ws.gui.WsJob.JobState;
43 import jalview.ws.params.ArgumentI;
44 import jalview.ws.params.WsParamSetI;
46 import java.util.ArrayList;
47 import java.util.List;
49 import javax.swing.JInternalFrame;
51 public class MsaWSThread extends AWSThread implements WSClientI
53 boolean submitGaps = false; // pass sequences including gaps to alignment
57 boolean preserveOrder = true; // and always store and recover sequence
61 String alTitle; // name which will be used to form new alignment window.
63 AlignmentI dataset; // dataset to which the new alignment will be
67 MultipleSequenceAlignmentI server = null;
70 * set basic options for this (group) of Msa jobs
77 private MsaWSThread(MultipleSequenceAlignmentI server, String wsUrl,
78 WebserviceInfo wsinfo,
79 jalview.gui.AlignFrame alFrame, AlignmentView alview,
80 String wsname, boolean subgaps, boolean presorder)
82 super(alFrame, wsinfo, alview, wsname, wsUrl);
84 this.submitGaps = subgaps;
85 this.preserveOrder = presorder;
89 * create one or more Msa jobs to align visible sequences in _msa
102 public MsaWSThread(MultipleSequenceAlignmentI server2, WsParamSetI preset,
103 List<ArgumentI> paramset,
104 String wsUrl, WebserviceInfo wsinfo,
105 jalview.gui.AlignFrame alFrame, String wsname, String title,
106 AlignmentView _msa, boolean subgaps, boolean presorder,
109 this(server2, wsUrl, wsinfo, alFrame, _msa, wsname, subgaps, presorder);
110 OutputHeader = wsInfo.getProgressText();
114 SequenceI[][] conmsa = _msa.getVisibleContigs('-');
117 int nvalid = 0, njobs = conmsa.length;
118 jobs = new AWsJob[njobs];
119 for (int j = 0; j < njobs; j++)
123 jobs[j] = new MsaWSJob(this, wsinfo.addJobPane(), conmsa[j]);
127 jobs[j] = new MsaWSJob(this, 0, conmsa[j]);
129 if (jobs[j].hasValidInput())
133 jobs[j].setPreset(preset);
134 jobs[j].setArguments(paramset);
135 ((MsaWSJob) jobs[j]).alignmentProgram = wsname;
138 wsinfo.setProgressName("region " + jobs[j].getJobnum(),
139 jobs[j].getJobnum());
141 wsinfo.setProgressText(jobs[j].getJobnum(), OutputHeader);
143 validInput = nvalid > 0;
147 boolean validInput = false;
151 * @return true if the thread will perform a calculation
153 public boolean hasValidInput()
159 public boolean isCancellable()
161 return server instanceof CancellableI;
165 public void cancelJob()
167 // TODO decide if when some jobs are not cancellable to shut down the thread
169 if (!jobComplete && jobs != null)
171 boolean cancelled = true;
172 for (int job = 0; job < jobs.length; job++)
174 if (jobs[job].isSubmitted() && !jobs[job].isSubjobComplete())
176 String cancelledMessage = "";
179 CancellableI service = (CancellableI) server;
180 boolean cancelledJob = service.cancel((WsJob) jobs[job]);
184 cancelledMessage = "Job cancelled.";
185 ((MsaWSJob) jobs[job]).cancel(); // TODO: refactor to avoid this
187 wsInfo.setStatus(jobs[job].getJobnum(),
188 WebserviceInfo.STATE_CANCELLED_OK);
192 // VALID UNSTOPPABLE JOB
193 cancelledMessage += "Server cannot cancel this job. just close the window.\n";
195 // wsInfo.setStatus(jobs[job].jobnum,
196 // WebserviceInfo.STATE_RUNNING);
198 } catch (Exception exc)
200 cancelledMessage += ("\nProblems cancelling the job : Exception received...\n"
203 "Exception whilst cancelling " + jobs[job].getJobId(),
206 wsInfo.setProgressText(jobs[job].getJobnum(),
207 OutputHeader + cancelledMessage + "\n");
211 // if we hadn't submitted then just mark the job as cancelled.
212 jobs[job].setSubjobComplete(true);
213 wsInfo.setStatus(jobs[job].getJobnum(),
214 WebserviceInfo.STATE_CANCELLED_OK);
220 wsInfo.setStatus(WebserviceInfo.STATE_CANCELLED_OK);
223 this.interrupt(); // kick thread to update job states.
229 wsInfo.setProgressText(OutputHeader
230 + "Server cannot cancel this job because it has not been submitted properly. just close the window.\n");
236 public void pollJob(AWsJob job) throws Exception
238 // TODO: investigate if we still need to cast here in J1.6
239 MsaWSJob j = ((MsaWSJob) job);
240 // this is standard code, but since the interface doesn't comprise of a
241 // basic one that implements (getJobStatus, pullExecStatistics) we have to
242 // repeat the code for all jw2s services.
243 server.updateStatus(j);
244 server.updateJobProgress(j);
248 public void StartJob(AWsJob job)
250 Exception lex = null;
251 // boiler plate template
252 if (!(job instanceof MsaWSJob))
254 throw new Error(MessageManager.formatMessage(
255 "error.implementation_error_msawbjob_called", new String[]
256 { job.getClass().toString() }));
258 MsaWSJob j = (MsaWSJob) job;
261 if (Cache.log.isDebugEnabled())
264 "Tried to submit an already submitted job " + j.getJobId());
270 if (j.seqs == null || j.seqs.size() == 0)
272 // special case - selection consisted entirely of empty sequences...
273 j.setState(JobState.FINISHED);
274 j.setStatus(MessageManager.getString("label.empty_alignment_job"));
278 j.addInitialStatus(); // list the presets/parameters used for the job in
282 JobId jobHandle = server.align(j.seqs, j.getPreset(),
284 if (jobHandle != null)
286 j.setJobHandle(jobHandle);
289 } catch (Throwable throwable)
291 if (!server.handleSubmitError(throwable, j, wsInfo))
293 if (throwable instanceof Exception)
295 throw ((Exception) throwable);
297 if (throwable instanceof Error)
299 throw ((Error) throwable);
304 if (j.getJobId() != null)
306 j.setSubmitted(true);
307 j.setSubjobComplete(false);
308 // System.out.println(WsURL + " Job Id '" + jobId + "'");
313 throw new Exception(MessageManager.formatMessage(
314 "exception.web_service_returned_null_try_later",
324 // For unexpected errors
325 System.err.println(WebServiceName
326 + "Client: Failed to submit the sequences for alignment (probably a server side problem)\n"
327 + "When contacting Server:" + WsUrl + "\n");
328 e.printStackTrace(System.err);
329 wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_SERVERERROR);
330 wsInfo.setStatus(j.getJobnum(),
331 WebserviceInfo.STATE_STOPPED_SERVERERROR);
332 } catch (Exception e)
334 // For unexpected errors
335 System.err.println(WebServiceName
336 + "Client: Failed to submit the sequences for alignment (probably a server side problem)\n"
337 + "When contacting Server:" + WsUrl + "\n");
338 e.printStackTrace(System.err);
339 wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_SERVERERROR);
340 wsInfo.setStatus(j.getJobnum(),
341 WebserviceInfo.STATE_STOPPED_SERVERERROR);
344 if (!j.isSubmitted())
346 // Boilerplate code here
347 // TODO: JBPNote catch timeout or other fault types explicitly
349 j.setAllowedServerExceptions(0);
350 wsInfo.appendProgressText(j.getJobnum(), MessageManager.getString(
351 "info.failed_to_submit_sequences_for_alignment"));
357 public void parseResult()
359 long progbar = System.currentTimeMillis();
360 wsInfo.setProgressBar(
361 MessageManager.getString("status.collecting_job_results"),
363 int results = 0; // number of result sets received
364 JobStateSummary finalState = new JobStateSummary();
367 for (int j = 0; j < jobs.length; j++)
369 MsaWSJob msjob = ((MsaWSJob) jobs[j]);
370 if (jobs[j].isFinished() && msjob.alignment == null)
372 int nunchanged = 3, nexcept = 3;
373 boolean jpchanged = false, jpex = false;
378 jpchanged = server.updateJobProgress(msjob);
384 } catch (Exception e)
388 "Exception when retrieving remaining Job progress data for job "
389 + msjob.getJobId() + " on server " + WsUrl);
393 // set flag remember that we've had an exception.
401 Thread.sleep(jpex ? 2400 : 1200); // wait a bit longer if we
402 // experienced an exception.
403 } catch (Exception ex)
409 } while (nunchanged > 0 && nexcept > 0);
411 if (Cache.log.isDebugEnabled())
413 System.out.println("Job Execution file for job: "
414 + msjob.getJobId() + " on server " + WsUrl);
415 System.out.println(msjob.getStatus());
416 System.out.println("*** End of status");
419 ///// jabaws specific(ish) Get Result from Server when available
422 msjob.alignment = server.getAlignmentFor(msjob.getJobHandle());
423 } catch (Exception e)
425 if (!server.handleCollectionException(e, msjob, wsInfo))
427 Cache.log.error("Couldn't get Alignment for job.", e);
428 // TODO: Increment count and retry ?
429 msjob.setState(JobState.SERVERERROR);
433 finalState.updateJobPanelState(wsInfo, OutputHeader, jobs[j]);
434 if (jobs[j].isSubmitted() && jobs[j].isSubjobComplete()
435 && jobs[j].hasResults())
440 } catch (Exception ex)
444 "Unexpected exception when processing results for " + alTitle,
446 wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_ERROR);
450 wsInfo.showResultsNewFrame
451 .addActionListener(new java.awt.event.ActionListener()
454 public void actionPerformed(java.awt.event.ActionEvent evt)
456 displayResults(true);
460 .addActionListener(new java.awt.event.ActionListener()
463 public void actionPerformed(java.awt.event.ActionEvent evt)
465 displayResults(false);
468 wsInfo.setResultsReady();
472 wsInfo.setFinishedNoResults();
474 updateGlobalStatus(finalState);
475 wsInfo.setProgressBar(null, progbar);
479 * Display alignment results in a new frame (or - not currently supported -
480 * added to an existing alignment).
484 void displayResults(boolean newFrame)
486 // view input or result data for each block
487 List<AlignmentOrder> alorders = new ArrayList<>();
488 SequenceI[][] results = new SequenceI[jobs.length][];
489 AlignmentOrder[] orders = new AlignmentOrder[jobs.length];
490 String lastProgram = null;
492 for (int j = 0; j < jobs.length; j++)
494 if (jobs[j].hasResults())
496 msjob = (MsaWSJob) jobs[j];
497 Object[] res = msjob.getAlignment();
498 lastProgram = msjob.getAlignmentProgram();
499 alorders.add((AlignmentOrder) res[1]);
500 results[j] = (SequenceI[]) res[0];
501 orders[j] = (AlignmentOrder) res[1];
503 // SequenceI[] alignment = input.getUpdated
510 Object[] newview = input.getUpdatedView(results, orders, getGapChar());
511 // trash references to original result data
512 for (int j = 0; j < jobs.length; j++)
517 SequenceI[] alignment = (SequenceI[]) newview[0];
518 HiddenColumns hidden = (HiddenColumns) newview[1];
519 Alignment al = new Alignment(alignment);
520 // TODO: add 'provenance' property to alignment from the method notes
521 if (lastProgram != null)
523 al.setProperty("Alignment Program", lastProgram);
525 // accompanying each subjob
528 al.setDataset(dataset);
531 propagateDatasetMappings(al);
532 // JBNote- TODO: warn user if a block is input rather than aligned data ?
536 displayInNewFrame(al, alorders, hidden);
540 // TODO 2.9.x feature
541 System.out.println("MERGE WITH OLD FRAME");
542 // TODO: modify alignment in original frame, replacing old for new
543 // alignment using the commands.EditCommand model to ensure the update can
549 * Display the alignment result in a new frame.
553 * @param columnselection
555 protected void displayInNewFrame(AlignmentI al,
556 List<AlignmentOrder> alorders, HiddenColumns hidden)
558 AlignFrame af = new AlignFrame(al, hidden, AlignFrame.DEFAULT_WIDTH,
559 AlignFrame.DEFAULT_HEIGHT);
561 // initialise with same renderer settings as in parent alignframe.
562 af.getFeatureRenderer().transferSettings(this.featureSettings);
564 if (alorders.size() > 0)
566 addSortByMenuItems(af, alorders);
569 // TODO: refactor retrieve and show as new splitFrame as Desktop method
572 * If alignment was requested from one half of a SplitFrame, show in a
573 * SplitFrame with the other pane similarly aligned.
575 AlignFrame requestedBy = getRequestingAlignFrame();
576 if (requestedBy != null && requestedBy.getSplitViewContainer() != null
577 && requestedBy.getSplitViewContainer()
578 .getComplement(requestedBy) != null)
580 AlignmentI complement = requestedBy.getSplitViewContainer()
581 .getComplement(requestedBy);
582 String complementTitle = requestedBy.getSplitViewContainer()
583 .getComplementTitle(requestedBy);
584 // becomes null if the alignment window was closed before the alignment
586 AlignmentI copyComplement = new Alignment(complement);
587 // todo should this be done by copy constructor?
588 copyComplement.setGapCharacter(complement.getGapCharacter());
589 // share the same dataset (and the mappings it holds)
590 copyComplement.setDataset(complement.getDataset());
591 copyComplement.alignAs(al);
592 if (copyComplement.getHeight() > 0)
594 af.setTitle(alTitle);
595 AlignFrame af2 = new AlignFrame(copyComplement,
596 AlignFrame.DEFAULT_WIDTH, AlignFrame.DEFAULT_HEIGHT);
597 af2.setTitle(complementTitle);
598 String linkedTitle = MessageManager
599 .getString("label.linked_view_title");
600 JInternalFrame splitFrame = new SplitFrame(
601 al.isNucleotide() ? af : af2, al.isNucleotide() ? af2 : af);
602 Desktop.addInternalFrame(splitFrame, linkedTitle, -1, -1);
608 * Not from SplitFrame, or failed to created a complementary alignment
610 Desktop.addInternalFrame(af, alTitle, AlignFrame.DEFAULT_WIDTH,
611 AlignFrame.DEFAULT_HEIGHT);
615 * Add sort order options to the AlignFrame menus.
620 protected void addSortByMenuItems(AlignFrame af,
621 List<AlignmentOrder> alorders)
624 if (alorders.size() == 1)
626 af.addSortByOrderMenuItem(WebServiceName + " Ordering",
631 // construct a non-redundant ordering set
632 List<String> names = new ArrayList<>();
633 for (int i = 0, l = alorders.size(); i < l; i++)
635 String orderName = " Region " + i;
640 if (alorders.get(i).equals(alorders.get(j)))
644 orderName += "," + j;
652 if (i == 0 && j == 1)
658 names.add(orderName);
661 for (int i = 0, l = alorders.size(); i < l; i++)
663 af.addSortByOrderMenuItem(
664 WebServiceName + (names.get(i)) + " Ordering",
671 public boolean canMergeResults()