Clover icon

Coverage Report

  1. Project Clover database Thu Dec 4 2025 14:43:25 GMT
  2. Package jalview.ws.gui

File MsaWSThread.java

 

Coverage histogram

../../../img/srcFileCovDistChart0.png
60% of files have more coverage

Code metrics

96
212
14
1
680
512
80
0.38
15.14
14
5.71

Classes

Class Line # Actions
MsaWSThread 52 212 80
0.00%
 

Contributing tests

No tests hitting this source file were found.

Source view

1    /*
2    * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3    * Copyright (C) $$Year-Rel$$ The Jalview Authors
4    *
5    * This file is part of Jalview.
6    *
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.
11    *
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.
16    *
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.
20    */
21    package jalview.ws.gui;
22   
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;
46   
47    import java.util.ArrayList;
48    import java.util.List;
49   
50    import javax.swing.JInternalFrame;
51   
 
52    public class MsaWSThread extends AWSThread implements WSClientI
53    {
54    boolean submitGaps = false; // pass sequences including gaps to alignment
55   
56    // service
57   
58    boolean preserveOrder = true; // and always store and recover sequence
59   
60    // order
61   
62   
63    String alTitle; // name which will be used to form new alignment window.
64   
65    AlignmentI dataset; // dataset to which the new alignment will be
66   
67    // associated.
68   
69    MultipleSequenceAlignmentI server = null;
70   
71    /**
72    * set basic options for this (group) of Msa jobs
73    *
74    * @param subgaps
75    * boolean
76    * @param presorder
77    * boolean
78    */
 
79  0 toggle private MsaWSThread(MultipleSequenceAlignmentI server, String wsUrl,
80    WebserviceInfo wsinfo,
81    jalview.gui.AlignFrame alFrame, AlignmentView alview,
82    String wsname, boolean subgaps, boolean presorder)
83    {
84  0 super(alFrame, wsinfo, alview, wsname, wsUrl);
85  0 this.server = server;
86  0 this.submitGaps = subgaps;
87  0 this.preserveOrder = presorder;
88    }
89   
90    /**
91    * create one or more Msa jobs to align visible sequences in _msa
92    *
93    * @param title
94    * String
95    * @param _msa
96    * AlignmentView
97    * @param subgaps
98    * boolean
99    * @param presorder
100    * boolean
101    * @param seqset
102    * Alignment
103    */
 
104  0 toggle 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,
109    AlignmentI seqset)
110    {
111  0 this(server2, wsUrl, wsinfo, alFrame, _msa, wsname, subgaps, presorder);
112  0 OutputHeader = wsInfo.getProgressText();
113  0 alTitle = title;
114  0 dataset = seqset;
115   
116  0 SequenceI[][] conmsa = _msa.getVisibleContigs('-');
117  0 if (conmsa != null)
118    {
119  0 int nvalid = 0, njobs = conmsa.length;
120  0 jobs = new AWsJob[njobs];
121  0 for (int j = 0; j < njobs; j++)
122    {
123  0 if (j != 0)
124    {
125  0 jobs[j] = new MsaWSJob(this, wsinfo.addJobPane(), conmsa[j]);
126    }
127    else
128    {
129  0 jobs[j] = new MsaWSJob(this, 0, conmsa[j]);
130    }
131  0 if (jobs[j].hasValidInput())
132    {
133  0 nvalid++;
134    }
135  0 jobs[j].setPreset(preset);
136  0 jobs[j].setArguments(paramset);
137  0 ((MsaWSJob) jobs[j]).alignmentProgram = wsname;
138  0 if (njobs > 0)
139    {
140  0 wsinfo.setProgressName("region " + jobs[j].getJobnum(),
141    jobs[j].getJobnum());
142    }
143  0 wsinfo.setProgressText(jobs[j].getJobnum(), OutputHeader);
144    }
145  0 validInput = nvalid > 0;
146    }
147    }
148   
149    boolean validInput = false;
150   
151    /**
152    *
153    * @return true if the thread will perform a calculation
154    */
 
155  0 toggle public boolean hasValidInput()
156    {
157  0 return validInput;
158    }
159   
 
160  0 toggle @Override
161    public boolean isCancellable()
162    {
163  0 return server instanceof CancellableI;
164    }
165   
 
166  0 toggle @Override
167    public void cancelJob()
168    {
169    // TODO decide if when some jobs are not cancellable to shut down the thread
170    // anyhow ?
171  0 if (!jobComplete && jobs != null)
172    {
173  0 boolean cancelled = true;
174  0 for (int job = 0; job < jobs.length; job++)
175    {
176  0 if (jobs[job].isSubmitted() && !jobs[job].isSubjobComplete())
177    {
178  0 String cancelledMessage = "";
179  0 try
180    {
181  0 CancellableI service = (CancellableI) server;
182  0 boolean cancelledJob = service.cancel((WsJob) jobs[job]);
183  0 if (cancelledJob)
184    {
185    // CANCELLED_JOB
186  0 cancelledMessage = "Job cancelled.";
187  0 ((MsaWSJob) jobs[job]).cancel(); // TODO: refactor to avoid this
188    // ugliness -
189  0 wsInfo.setStatus(jobs[job].getJobnum(),
190    WebserviceInfo.STATE_CANCELLED_OK);
191    }
192    else
193    {
194    // VALID UNSTOPPABLE JOB
195  0 cancelledMessage += "Server cannot cancel this job. just close the window.\n";
196  0 cancelled = false;
197    // wsInfo.setStatus(jobs[job].jobnum,
198    // WebserviceInfo.STATE_RUNNING);
199    }
200    } catch (Exception exc)
201    {
202  0 cancelledMessage += ("\nProblems cancelling the job : Exception received...\n"
203    + exc + "\n");
204  0 Console.warn(
205    "Exception whilst cancelling " + jobs[job].getJobId(),
206    exc);
207    }
208  0 wsInfo.setProgressText(jobs[job].getJobnum(),
209    OutputHeader + cancelledMessage + "\n");
210    }
211    else
212    {
213    // if we hadn't submitted then just mark the job as cancelled.
214  0 jobs[job].setSubjobComplete(true);
215  0 wsInfo.setStatus(jobs[job].getJobnum(),
216    WebserviceInfo.STATE_CANCELLED_OK);
217   
218    }
219    }
220  0 if (cancelled)
221    {
222  0 wsInfo.setStatus(WebserviceInfo.STATE_CANCELLED_OK);
223  0 jobComplete = true;
224    }
225  0 this.interrupt(); // kick thread to update job states.
226    }
227    else
228    {
229  0 if (!jobComplete)
230    {
231  0 wsInfo.setProgressText(OutputHeader
232    + "Server cannot cancel this job because it has not been submitted properly. just close the window.\n");
233    }
234    }
235    }
236   
 
237  0 toggle @Override
238    public void pollJob(AWsJob job) throws Exception
239    {
240    // TODO: investigate if we still need to cast here in J1.6
241  0 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  0 server.updateStatus(j);
246  0 server.updateJobProgress(j);
247    }
248   
 
249  0 toggle @Override
250    public void StartJob(AWsJob job)
251    {
252  0 Exception lex = null;
253    // boiler plate template
254  0 if (!(job instanceof MsaWSJob))
255    {
256  0 throw new Error(MessageManager.formatMessage(
257    "error.implementation_error_msawbjob_called", new String[]
258    { job.getClass().toString() }));
259    }
260  0 MsaWSJob j = (MsaWSJob) job;
261  0 if (j.isSubmitted())
262    {
263  0 if (Console.isDebugEnabled())
264    {
265  0 Console.debug(
266    "Tried to submit an already submitted job " + j.getJobId());
267    }
268  0 return;
269    }
270    // end boilerplate
271   
272  0 if (j.seqs == null || j.seqs.size() == 0)
273    {
274    // special case - selection consisted entirely of empty sequences...
275  0 j.setState(JobState.FINISHED);
276  0 j.setStatus(MessageManager.getString("label.empty_alignment_job"));
277    }
278  0 try
279    {
280  0 j.addInitialStatus(); // list the presets/parameters used for the job in
281    // status
282  0 try
283    {
284  0 JobId jobHandle = server.align(j.seqs, j.getPreset(),
285    j.getArguments());
286  0 if (jobHandle != null)
287    {
288  0 j.setJobHandle(jobHandle);
289    }
290   
291    } catch (Throwable throwable)
292    {
293  0 Console.error("failed to send the job to the alignment server", throwable);
294  0 if (!server.handleSubmitError(throwable, j, wsInfo))
295    {
296  0 if (throwable instanceof Exception)
297    {
298  0 throw ((Exception) throwable);
299    }
300  0 if (throwable instanceof Error)
301    {
302  0 throw ((Error) throwable);
303    }
304    }
305    }
306    ///// generic
307   
308  0 if (j.getJobId() != null)
309    {
310  0 j.setSubmitted(true);
311  0 j.setSubjobComplete(false);
312    // System.out.println(WsURL + " Job Id '" + jobId + "'");
313  0 return;
314    }
315    else
316    {
317  0 throw new Exception(MessageManager.formatMessage(
318    "exception.web_service_returned_null_try_later",
319    new String[]
320    { WsUrl }));
321    }
322    }
323    //// jabaws specific
324   
325    //// generic
326    catch (Error e)
327    {
328    // For unexpected errors
329  0 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  0 e.printStackTrace(System.err);
333  0 wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_SERVERERROR);
334  0 wsInfo.setStatus(j.getJobnum(),
335    WebserviceInfo.STATE_STOPPED_SERVERERROR);
336    } catch (Exception e)
337    {
338    // For unexpected errors
339  0 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  0 e.printStackTrace(System.err);
343  0 wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_SERVERERROR);
344  0 wsInfo.setStatus(j.getJobnum(),
345    WebserviceInfo.STATE_STOPPED_SERVERERROR);
346    } finally
347    {
348  0 if (!j.isSubmitted())
349    {
350    // Boilerplate code here
351    // TODO: JBPNote catch timeout or other fault types explicitly
352   
353  0 j.setAllowedServerExceptions(0);
354  0 wsInfo.appendProgressText(j.getJobnum(), MessageManager.getString(
355    "info.failed_to_submit_sequences_for_alignment"));
356    }
357    }
358    }
359   
 
360  0 toggle @Override
361    public void parseResult()
362    {
363  0 long progbar = System.currentTimeMillis();
364  0 wsInfo.setProgressBar(
365    MessageManager.getString("status.collecting_job_results"),
366    progbar);
367  0 int results = 0; // number of result sets received
368  0 JobStateSummary finalState = new JobStateSummary();
369  0 try
370    {
371  0 for (int j = 0; j < jobs.length; j++)
372    {
373  0 MsaWSJob msjob = ((MsaWSJob) jobs[j]);
374  0 if (jobs[j].isFinished() && msjob.alignment == null)
375    {
376  0 int nunchanged = 3, nexcept = 3;
377  0 boolean jpchanged = false, jpex = false;
378  0 do
379    {
380  0 try
381    {
382  0 jpchanged = server.updateJobProgress(msjob);
383  0 jpex = false;
384  0 if (jpchanged)
385    {
386  0 nexcept = 3;
387    }
388    } catch (Exception e)
389    {
390   
391  0 Console.warn(
392    "Exception when retrieving remaining Job progress data for job "
393    + msjob.getJobId() + " on server " + WsUrl);
394  0 e.printStackTrace();
395  0 nexcept--;
396  0 nunchanged = 3;
397    // set flag remember that we've had an exception.
398  0 jpex = true;
399  0 jpchanged = false;
400    }
401  0 if (!jpchanged)
402    {
403  0 try
404    {
405  0 Thread.sleep(jpex ? 2400 : 1200); // wait a bit longer if we
406    // experienced an exception.
407    } catch (Exception ex)
408    {
409    }
410  0 ;
411  0 nunchanged--;
412    }
413  0 } while (nunchanged > 0 && nexcept > 0);
414   
415  0 if (Console.isDebugEnabled())
416    {
417  0 System.out.println("Job Execution file for job: "
418    + msjob.getJobId() + " on server " + WsUrl);
419  0 System.out.println(msjob.getStatus());
420  0 System.out.println("*** End of status");
421   
422    }
423    ///// jabaws specific(ish) Get Result from Server when available
424  0 try
425    {
426  0 msjob.alignment = server.getAlignmentFor(msjob.getJobHandle());
427    } catch (Exception e)
428    {
429  0 if (!server.handleCollectionException(e, msjob, wsInfo))
430    {
431  0 Console.error("Couldn't get Alignment for job.", e);
432    // TODO: Increment count and retry ?
433  0 msjob.setState(JobState.SERVERERROR);
434    }
435    }
436    }
437  0 finalState.updateJobPanelState(wsInfo, OutputHeader, jobs[j]);
438  0 if (jobs[j].isSubmitted() && jobs[j].isSubjobComplete()
439    && jobs[j].hasResults())
440    {
441  0 results++;
442    }
443    }
444    } catch (Exception ex)
445    {
446   
447  0 Console.error(
448    "Unexpected exception when processing results for " + alTitle,
449    ex);
450  0 wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_ERROR);
451    }
452  0 if (results > 0)
453    {
454  0 wsInfo.showResultsNewFrame
455    .addActionListener(new java.awt.event.ActionListener()
456    {
 
457  0 toggle @Override
458    public void actionPerformed(java.awt.event.ActionEvent evt)
459    {
460  0 displayResults(true);
461    }
462    });
463  0 wsInfo.mergeResults
464    .addActionListener(new java.awt.event.ActionListener()
465    {
 
466  0 toggle @Override
467    public void actionPerformed(java.awt.event.ActionEvent evt)
468    {
469  0 displayResults(false);
470    }
471    });
472  0 wsInfo.setResultsReady();
473    }
474    else
475    {
476  0 wsInfo.setFinishedNoResults();
477    }
478  0 updateGlobalStatus(finalState);
479  0 wsInfo.setProgressBar(null, progbar);
480    }
481   
482    /**
483    * Display alignment results in a new frame (or - not currently supported -
484    * added to an existing alignment).
485    *
486    * @param newFrame
487    */
 
488  0 toggle void displayResults(boolean newFrame)
489    {
490    // view input or result data for each block
491  0 List<AlignmentOrder> alorders = new ArrayList<>();
492  0 SequenceI[][] results = new SequenceI[jobs.length][];
493  0 AlignmentOrder[] orders = new AlignmentOrder[jobs.length];
494  0 String lastProgram = null;
495  0 MsaWSJob msjob;
496  0 for (int j = 0; j < jobs.length; j++)
497    {
498  0 if (jobs[j].hasResults())
499    {
500  0 msjob = (MsaWSJob) jobs[j];
501  0 Object[] res = msjob.getAlignment();
502  0 lastProgram = msjob.getAlignmentProgram();
503  0 alorders.add((AlignmentOrder) res[1]);
504  0 results[j] = (SequenceI[]) res[0];
505  0 orders[j] = (AlignmentOrder) res[1];
506   
507    // SequenceI[] alignment = input.getUpdated
508    }
509    else
510    {
511  0 results[j] = null;
512    }
513    }
514  0 Object[] newview = input.getUpdatedView(results, orders, getGapChar());
515    // trash references to original result data
516  0 for (int j = 0; j < jobs.length; j++)
517    {
518  0 results[j] = null;
519  0 orders[j] = null;
520    }
521  0 SequenceI[] alignment = (SequenceI[]) newview[0];
522  0 HiddenColumns hidden = (HiddenColumns) newview[1];
523  0 Alignment al = new Alignment(alignment);
524    // TODO: add 'provenance' property to alignment from the method notes
525  0 if (lastProgram != null)
526    {
527  0 al.setProperty("Alignment Program", lastProgram);
528    }
529    // accompanying each subjob
530  0 if (dataset != null)
531    {
532  0 al.setDataset(dataset);
533    }
534   
535  0 propagateDatasetMappings(al);
536    // JBNote- TODO: warn user if a block is input rather than aligned data ?
537   
538  0 if (newFrame)
539    {
540  0 displayInNewFrame(al, alorders, hidden);
541   
542    }
543    else
544    {
545    // TODO 2.9.x feature
546  0 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
549    // be undone
550    }
551    }
552   
553    /**
554    * Display the alignment result in a new frame.
555    *
556    * @param al
557    * @param alorders
558    * @param columnselection
559    */
 
560  0 toggle protected void displayInNewFrame(AlignmentI al,
561    List<AlignmentOrder> alorders, HiddenColumns hidden)
562    {
563  0 AlignFrame af = new AlignFrame(al, hidden, AlignFrame.DEFAULT_WIDTH,
564    AlignFrame.DEFAULT_HEIGHT);
565   
566    // initialise with same renderer settings as in parent alignframe.
567  0 af.getFeatureRenderer().transferSettings(this.featureSettings);
568   
569  0 if (alorders.size() > 0)
570    {
571  0 addSortByMenuItems(af, alorders);
572    }
573   
574    // TODO: refactor retrieve and show as new splitFrame as Desktop method
575   
576    /*
577    * If alignment was requested from one half of a SplitFrame, show in a
578    * SplitFrame with the other pane similarly aligned.
579    */
580  0 AlignFrame requestedBy = getRequestingAlignFrame();
581  0 if (requestedBy != null && requestedBy.getSplitViewContainer() != null
582    && requestedBy.getSplitViewContainer()
583    .getComplement(requestedBy) != null)
584    {
585  0 AlignmentI complement = requestedBy.getSplitViewContainer()
586    .getComplement(requestedBy);
587  0 String complementTitle = requestedBy.getSplitViewContainer()
588    .getComplementTitle(requestedBy);
589    // becomes null if the alignment window was closed before the alignment
590    // job finished.
591  0 AlignmentI copyComplement = new Alignment(complement);
592    // todo should this be done by copy constructor?
593  0 copyComplement.setGapCharacter(complement.getGapCharacter());
594    // share the same dataset (and the mappings it holds)
595  0 copyComplement.setDataset(complement.getDataset());
596  0 copyComplement.alignAs(al);
597  0 if (copyComplement.getHeight() > 0)
598    {
599  0 af.setTitle(alTitle);
600  0 AlignFrame af2 = new AlignFrame(copyComplement,
601    AlignFrame.DEFAULT_WIDTH, AlignFrame.DEFAULT_HEIGHT);
602  0 af2.setTitle(complementTitle);
603  0 String linkedTitle = MessageManager
604    .getString("label.linked_view_title");
605  0 JInternalFrame splitFrame = new SplitFrame(
606  0 al.isNucleotide() ? af : af2, al.isNucleotide() ? af2 : af);
607  0 Desktop.addInternalFrame(splitFrame, linkedTitle, -1, -1);
608  0 return;
609    }
610    }
611   
612    /*
613    * Not from SplitFrame, or failed to created a complementary alignment
614    */
615  0 Desktop.addInternalFrame(af, alTitle, AlignFrame.DEFAULT_WIDTH,
616    AlignFrame.DEFAULT_HEIGHT);
617    }
618   
619    /**
620    * Add sort order options to the AlignFrame menus.
621    *
622    * @param af
623    * @param alorders
624    */
 
625  0 toggle protected void addSortByMenuItems(AlignFrame af,
626    List<AlignmentOrder> alorders)
627    {
628    // update orders
629  0 if (alorders.size() == 1)
630    {
631  0 af.addSortByOrderMenuItem(WebServiceName + " Ordering",
632    alorders.get(0));
633    }
634    else
635    {
636    // construct a non-redundant ordering set
637  0 List<String> names = new ArrayList<>();
638  0 for (int i = 0, l = alorders.size(); i < l; i++)
639    {
640  0 String orderName = " Region " + i;
641  0 int j = i + 1;
642   
643  0 while (j < l)
644    {
645  0 if (alorders.get(i).equals(alorders.get(j)))
646    {
647  0 alorders.remove(j);
648  0 l--;
649  0 orderName += "," + j;
650    }
651    else
652    {
653  0 j++;
654    }
655    }
656   
657  0 if (i == 0 && j == 1)
658    {
659  0 names.add("");
660    }
661    else
662    {
663  0 names.add(orderName);
664    }
665    }
666  0 for (int i = 0, l = alorders.size(); i < l; i++)
667    {
668  0 af.addSortByOrderMenuItem(
669    WebServiceName + (names.get(i)) + " Ordering",
670    alorders.get(i));
671    }
672    }
673    }
674   
 
675  0 toggle @Override
676    public boolean canMergeResults()
677    {
678  0 return false;
679    }
680    }