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 if (!server.handleSubmitError(throwable, j, wsInfo))
295 if (throwable instanceof Exception)
297 throw ((Exception) throwable);
299 if (throwable instanceof Error)
301 throw ((Error) throwable);
307 if (j.getJobId() != null)
309 j.setSubmitted(true);
310 j.setSubjobComplete(false);
311 // System.out.println(WsURL + " Job Id '" + jobId + "'");
316 throw new Exception(MessageManager.formatMessage(
317 "exception.web_service_returned_null_try_later",
327 // For unexpected errors
328 System.err.println(WebServiceName
329 + "Client: Failed to submit the sequences for alignment (probably a server side problem)\n"
330 + "When contacting Server:" + WsUrl + "\n");
331 e.printStackTrace(System.err);
332 wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_SERVERERROR);
333 wsInfo.setStatus(j.getJobnum(),
334 WebserviceInfo.STATE_STOPPED_SERVERERROR);
335 } catch (Exception e)
337 // For unexpected errors
338 System.err.println(WebServiceName
339 + "Client: Failed to submit the sequences for alignment (probably a server side problem)\n"
340 + "When contacting Server:" + WsUrl + "\n");
341 e.printStackTrace(System.err);
342 wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_SERVERERROR);
343 wsInfo.setStatus(j.getJobnum(),
344 WebserviceInfo.STATE_STOPPED_SERVERERROR);
347 if (!j.isSubmitted())
349 // Boilerplate code here
350 // TODO: JBPNote catch timeout or other fault types explicitly
352 j.setAllowedServerExceptions(0);
353 wsInfo.appendProgressText(j.getJobnum(), MessageManager.getString(
354 "info.failed_to_submit_sequences_for_alignment"));
360 public void parseResult()
362 long progbar = System.currentTimeMillis();
363 wsInfo.setProgressBar(
364 MessageManager.getString("status.collecting_job_results"),
366 int results = 0; // number of result sets received
367 JobStateSummary finalState = new JobStateSummary();
370 for (int j = 0; j < jobs.length; j++)
372 MsaWSJob msjob = ((MsaWSJob) jobs[j]);
373 if (jobs[j].isFinished() && msjob.alignment == null)
375 int nunchanged = 3, nexcept = 3;
376 boolean jpchanged = false, jpex = false;
381 jpchanged = server.updateJobProgress(msjob);
387 } catch (Exception e)
391 "Exception when retrieving remaining Job progress data for job "
392 + msjob.getJobId() + " on server " + WsUrl);
396 // set flag remember that we've had an exception.
404 Thread.sleep(jpex ? 2400 : 1200); // wait a bit longer if we
405 // experienced an exception.
406 } catch (Exception ex)
412 } while (nunchanged > 0 && nexcept > 0);
414 if (Console.isDebugEnabled())
416 System.out.println("Job Execution file for job: "
417 + msjob.getJobId() + " on server " + WsUrl);
418 System.out.println(msjob.getStatus());
419 System.out.println("*** End of status");
422 ///// jabaws specific(ish) Get Result from Server when available
425 msjob.alignment = server.getAlignmentFor(msjob.getJobHandle());
426 } catch (Exception e)
428 if (!server.handleCollectionException(e, msjob, wsInfo))
430 Console.error("Couldn't get Alignment for job.", e);
431 // TODO: Increment count and retry ?
432 msjob.setState(JobState.SERVERERROR);
436 finalState.updateJobPanelState(wsInfo, OutputHeader, jobs[j]);
437 if (jobs[j].isSubmitted() && jobs[j].isSubjobComplete()
438 && jobs[j].hasResults())
443 } catch (Exception ex)
447 "Unexpected exception when processing results for " + alTitle,
449 wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_ERROR);
453 wsInfo.showResultsNewFrame
454 .addActionListener(new java.awt.event.ActionListener()
457 public void actionPerformed(java.awt.event.ActionEvent evt)
459 displayResults(true);
463 .addActionListener(new java.awt.event.ActionListener()
466 public void actionPerformed(java.awt.event.ActionEvent evt)
468 displayResults(false);
471 wsInfo.setResultsReady();
475 wsInfo.setFinishedNoResults();
477 updateGlobalStatus(finalState);
478 wsInfo.setProgressBar(null, progbar);
482 * Display alignment results in a new frame (or - not currently supported -
483 * added to an existing alignment).
487 void displayResults(boolean newFrame)
489 // view input or result data for each block
490 List<AlignmentOrder> alorders = new ArrayList<>();
491 SequenceI[][] results = new SequenceI[jobs.length][];
492 AlignmentOrder[] orders = new AlignmentOrder[jobs.length];
493 String lastProgram = null;
495 for (int j = 0; j < jobs.length; j++)
497 if (jobs[j].hasResults())
499 msjob = (MsaWSJob) jobs[j];
500 Object[] res = msjob.getAlignment();
501 lastProgram = msjob.getAlignmentProgram();
502 alorders.add((AlignmentOrder) res[1]);
503 results[j] = (SequenceI[]) res[0];
504 orders[j] = (AlignmentOrder) res[1];
506 // SequenceI[] alignment = input.getUpdated
513 Object[] newview = input.getUpdatedView(results, orders, getGapChar());
514 // trash references to original result data
515 for (int j = 0; j < jobs.length; j++)
520 SequenceI[] alignment = (SequenceI[]) newview[0];
521 HiddenColumns hidden = (HiddenColumns) newview[1];
522 Alignment al = new Alignment(alignment);
523 // TODO: add 'provenance' property to alignment from the method notes
524 if (lastProgram != null)
526 al.setProperty("Alignment Program", lastProgram);
528 // accompanying each subjob
531 al.setDataset(dataset);
534 propagateDatasetMappings(al);
535 // JBNote- TODO: warn user if a block is input rather than aligned data ?
539 displayInNewFrame(al, alorders, hidden);
544 // TODO 2.9.x feature
545 System.out.println("MERGE WITH OLD FRAME");
546 // TODO: modify alignment in original frame, replacing old for new
547 // alignment using the commands.EditCommand model to ensure the update can
553 * Display the alignment result in a new frame.
557 * @param columnselection
559 protected void displayInNewFrame(AlignmentI al,
560 List<AlignmentOrder> alorders, HiddenColumns hidden)
562 AlignFrame af = new AlignFrame(al, hidden, AlignFrame.DEFAULT_WIDTH,
563 AlignFrame.DEFAULT_HEIGHT);
565 // initialise with same renderer settings as in parent alignframe.
566 af.getFeatureRenderer().transferSettings(this.featureSettings);
568 if (alorders.size() > 0)
570 addSortByMenuItems(af, alorders);
573 // TODO: refactor retrieve and show as new splitFrame as Desktop method
576 * If alignment was requested from one half of a SplitFrame, show in a
577 * SplitFrame with the other pane similarly aligned.
579 AlignFrame requestedBy = getRequestingAlignFrame();
580 if (requestedBy != null && requestedBy.getSplitViewContainer() != null
581 && requestedBy.getSplitViewContainer()
582 .getComplement(requestedBy) != null)
584 AlignmentI complement = requestedBy.getSplitViewContainer()
585 .getComplement(requestedBy);
586 String complementTitle = requestedBy.getSplitViewContainer()
587 .getComplementTitle(requestedBy);
588 // becomes null if the alignment window was closed before the alignment
590 AlignmentI copyComplement = new Alignment(complement);
591 // todo should this be done by copy constructor?
592 copyComplement.setGapCharacter(complement.getGapCharacter());
593 // share the same dataset (and the mappings it holds)
594 copyComplement.setDataset(complement.getDataset());
595 copyComplement.alignAs(al);
596 if (copyComplement.getHeight() > 0)
598 af.setTitle(alTitle);
599 AlignFrame af2 = new AlignFrame(copyComplement,
600 AlignFrame.DEFAULT_WIDTH, AlignFrame.DEFAULT_HEIGHT);
601 af2.setTitle(complementTitle);
602 String linkedTitle = MessageManager
603 .getString("label.linked_view_title");
604 JInternalFrame splitFrame = new SplitFrame(
605 al.isNucleotide() ? af : af2, al.isNucleotide() ? af2 : af);
606 Desktop.addInternalFrame(splitFrame, linkedTitle, -1, -1);
612 * Not from SplitFrame, or failed to created a complementary alignment
614 Desktop.addInternalFrame(af, alTitle, AlignFrame.DEFAULT_WIDTH,
615 AlignFrame.DEFAULT_HEIGHT);
619 * Add sort order options to the AlignFrame menus.
624 protected void addSortByMenuItems(AlignFrame af,
625 List<AlignmentOrder> alorders)
628 if (alorders.size() == 1)
630 af.addSortByOrderMenuItem(WebServiceName + " Ordering",
635 // construct a non-redundant ordering set
636 List<String> names = new ArrayList<>();
637 for (int i = 0, l = alorders.size(); i < l; i++)
639 String orderName = " Region " + i;
644 if (alorders.get(i).equals(alorders.get(j)))
648 orderName += "," + j;
656 if (i == 0 && j == 1)
662 names.add(orderName);
665 for (int i = 0, l = alorders.size(); i < l; i++)
667 af.addSortByOrderMenuItem(
668 WebServiceName + (names.get(i)) + " Ordering",
675 public boolean canMergeResults()