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 Cache.log.error("failed to send the job to the alignment server", throwable);
292 if (!server.handleSubmitError(throwable, j, wsInfo))
294 if (throwable instanceof Exception)
296 throw ((Exception) throwable);
298 if (throwable instanceof Error)
300 throw ((Error) throwable);
305 if (j.getJobId() != null)
307 j.setSubmitted(true);
308 j.setSubjobComplete(false);
309 // System.out.println(WsURL + " Job Id '" + jobId + "'");
314 throw new Exception(MessageManager.formatMessage(
315 "exception.web_service_returned_null_try_later",
325 // For unexpected errors
326 System.err.println(WebServiceName
327 + "Client: Failed to submit the sequences for alignment (probably a server side problem)\n"
328 + "When contacting Server:" + WsUrl + "\n");
329 e.printStackTrace(System.err);
330 wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_SERVERERROR);
331 wsInfo.setStatus(j.getJobnum(),
332 WebserviceInfo.STATE_STOPPED_SERVERERROR);
333 } catch (Exception e)
335 // For unexpected errors
336 System.err.println(WebServiceName
337 + "Client: Failed to submit the sequences for alignment (probably a server side problem)\n"
338 + "When contacting Server:" + WsUrl + "\n");
339 e.printStackTrace(System.err);
340 wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_SERVERERROR);
341 wsInfo.setStatus(j.getJobnum(),
342 WebserviceInfo.STATE_STOPPED_SERVERERROR);
345 if (!j.isSubmitted())
347 // Boilerplate code here
348 // TODO: JBPNote catch timeout or other fault types explicitly
350 j.setAllowedServerExceptions(0);
351 wsInfo.appendProgressText(j.getJobnum(), MessageManager.getString(
352 "info.failed_to_submit_sequences_for_alignment"));
358 public void parseResult()
360 long progbar = System.currentTimeMillis();
361 wsInfo.setProgressBar(
362 MessageManager.getString("status.collecting_job_results"),
364 int results = 0; // number of result sets received
365 JobStateSummary finalState = new JobStateSummary();
368 for (int j = 0; j < jobs.length; j++)
370 MsaWSJob msjob = ((MsaWSJob) jobs[j]);
371 if (jobs[j].isFinished() && msjob.alignment == null)
373 int nunchanged = 3, nexcept = 3;
374 boolean jpchanged = false, jpex = false;
379 jpchanged = server.updateJobProgress(msjob);
385 } catch (Exception e)
389 "Exception when retrieving remaining Job progress data for job "
390 + msjob.getJobId() + " on server " + WsUrl);
394 // set flag remember that we've had an exception.
402 Thread.sleep(jpex ? 2400 : 1200); // wait a bit longer if we
403 // experienced an exception.
404 } catch (Exception ex)
410 } while (nunchanged > 0 && nexcept > 0);
412 if (Cache.log.isDebugEnabled())
414 System.out.println("Job Execution file for job: "
415 + msjob.getJobId() + " on server " + WsUrl);
416 System.out.println(msjob.getStatus());
417 System.out.println("*** End of status");
420 ///// jabaws specific(ish) Get Result from Server when available
423 msjob.alignment = server.getAlignmentFor(msjob.getJobHandle());
424 } catch (Exception e)
426 if (!server.handleCollectionException(e, msjob, wsInfo))
428 Cache.log.error("Couldn't get Alignment for job.", e);
429 // TODO: Increment count and retry ?
430 msjob.setState(JobState.SERVERERROR);
434 finalState.updateJobPanelState(wsInfo, OutputHeader, jobs[j]);
435 if (jobs[j].isSubmitted() && jobs[j].isSubjobComplete()
436 && jobs[j].hasResults())
441 } catch (Exception ex)
445 "Unexpected exception when processing results for " + alTitle,
447 wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_ERROR);
451 wsInfo.showResultsNewFrame
452 .addActionListener(new java.awt.event.ActionListener()
455 public void actionPerformed(java.awt.event.ActionEvent evt)
457 displayResults(true);
461 .addActionListener(new java.awt.event.ActionListener()
464 public void actionPerformed(java.awt.event.ActionEvent evt)
466 displayResults(false);
469 wsInfo.setResultsReady();
473 wsInfo.setFinishedNoResults();
475 updateGlobalStatus(finalState);
476 wsInfo.setProgressBar(null, progbar);
480 * Display alignment results in a new frame (or - not currently supported -
481 * added to an existing alignment).
485 void displayResults(boolean newFrame)
487 // view input or result data for each block
488 List<AlignmentOrder> alorders = new ArrayList<>();
489 SequenceI[][] results = new SequenceI[jobs.length][];
490 AlignmentOrder[] orders = new AlignmentOrder[jobs.length];
491 String lastProgram = null;
493 for (int j = 0; j < jobs.length; j++)
495 if (jobs[j].hasResults())
497 msjob = (MsaWSJob) jobs[j];
498 Object[] res = msjob.getAlignment();
499 lastProgram = msjob.getAlignmentProgram();
500 alorders.add((AlignmentOrder) res[1]);
501 results[j] = (SequenceI[]) res[0];
502 orders[j] = (AlignmentOrder) res[1];
504 // SequenceI[] alignment = input.getUpdated
511 Object[] newview = input.getUpdatedView(results, orders, getGapChar());
512 // trash references to original result data
513 for (int j = 0; j < jobs.length; j++)
518 SequenceI[] alignment = (SequenceI[]) newview[0];
519 HiddenColumns hidden = (HiddenColumns) newview[1];
520 Alignment al = new Alignment(alignment);
521 // TODO: add 'provenance' property to alignment from the method notes
522 if (lastProgram != null)
524 al.setProperty("Alignment Program", lastProgram);
526 // accompanying each subjob
529 al.setDataset(dataset);
532 propagateDatasetMappings(al);
533 // JBNote- TODO: warn user if a block is input rather than aligned data ?
537 displayInNewFrame(al, alorders, hidden);
541 // TODO 2.9.x feature
542 System.out.println("MERGE WITH OLD FRAME");
543 // TODO: modify alignment in original frame, replacing old for new
544 // alignment using the commands.EditCommand model to ensure the update can
550 * Display the alignment result in a new frame.
554 * @param columnselection
556 protected void displayInNewFrame(AlignmentI al,
557 List<AlignmentOrder> alorders, HiddenColumns hidden)
559 AlignFrame af = new AlignFrame(al, hidden, AlignFrame.DEFAULT_WIDTH,
560 AlignFrame.DEFAULT_HEIGHT);
562 // initialise with same renderer settings as in parent alignframe.
563 af.getFeatureRenderer().transferSettings(this.featureSettings);
565 if (alorders.size() > 0)
567 addSortByMenuItems(af, alorders);
570 // TODO: refactor retrieve and show as new splitFrame as Desktop method
573 * If alignment was requested from one half of a SplitFrame, show in a
574 * SplitFrame with the other pane similarly aligned.
576 AlignFrame requestedBy = getRequestingAlignFrame();
577 if (requestedBy != null && requestedBy.getSplitViewContainer() != null
578 && requestedBy.getSplitViewContainer()
579 .getComplement(requestedBy) != null)
581 AlignmentI complement = requestedBy.getSplitViewContainer()
582 .getComplement(requestedBy);
583 String complementTitle = requestedBy.getSplitViewContainer()
584 .getComplementTitle(requestedBy);
585 // becomes null if the alignment window was closed before the alignment
587 AlignmentI copyComplement = new Alignment(complement);
588 // todo should this be done by copy constructor?
589 copyComplement.setGapCharacter(complement.getGapCharacter());
590 // share the same dataset (and the mappings it holds)
591 copyComplement.setDataset(complement.getDataset());
592 copyComplement.alignAs(al);
593 if (copyComplement.getHeight() > 0)
595 af.setTitle(alTitle);
596 AlignFrame af2 = new AlignFrame(copyComplement,
597 AlignFrame.DEFAULT_WIDTH, AlignFrame.DEFAULT_HEIGHT);
598 af2.setTitle(complementTitle);
599 String linkedTitle = MessageManager
600 .getString("label.linked_view_title");
601 JInternalFrame splitFrame = new SplitFrame(
602 al.isNucleotide() ? af : af2, al.isNucleotide() ? af2 : af);
603 Desktop.addInternalFrame(splitFrame, linkedTitle, -1, -1);
609 * Not from SplitFrame, or failed to created a complementary alignment
611 Desktop.addInternalFrame(af, alTitle, AlignFrame.DEFAULT_WIDTH,
612 AlignFrame.DEFAULT_HEIGHT);
616 * Add sort order options to the AlignFrame menus.
621 protected void addSortByMenuItems(AlignFrame af,
622 List<AlignmentOrder> alorders)
625 if (alorders.size() == 1)
627 af.addSortByOrderMenuItem(WebServiceName + " Ordering",
632 // construct a non-redundant ordering set
633 List<String> names = new ArrayList<>();
634 for (int i = 0, l = alorders.size(); i < l; i++)
636 String orderName = " Region " + i;
641 if (alorders.get(i).equals(alorders.get(j)))
645 orderName += "," + j;
653 if (i == 0 && j == 1)
659 names.add(orderName);
662 for (int i = 0, l = alorders.size(); i < l; i++)
664 af.addSortByOrderMenuItem(
665 WebServiceName + (names.get(i)) + " Ordering",
672 public boolean canMergeResults()