JAL-3807 - Move new JPred client to the jws2 package.
[jalview.git] / src / jalview / ws / jws2 / JPredThread.java
1 package jalview.ws.jws2;
2
3 import static java.lang.String.format;
4
5 import java.util.Hashtable;
6 import java.util.List;
7
8 import jalview.analysis.SeqsetUtils;
9 import jalview.bin.Cache;
10 import jalview.datamodel.Alignment;
11 import jalview.datamodel.AlignmentAnnotation;
12 import jalview.datamodel.AlignmentI;
13 import jalview.datamodel.AlignmentView;
14 import jalview.datamodel.HiddenColumns;
15 import jalview.datamodel.SequenceI;
16 import jalview.gui.AlignFrame;
17 import jalview.gui.Desktop;
18 import jalview.gui.WebserviceInfo;
19 import jalview.io.JPredFile;
20 import jalview.io.JnetAnnotationMaker;
21 import jalview.util.MessageManager;
22 import jalview.ws.AWSThread;
23 import jalview.ws.AWsJob;
24 import jalview.ws.JobStateSummary;
25 import jalview.ws.WSClientI;
26 import jalview.ws.api.CancellableI;
27 import jalview.ws.api.JPredMutlipleAlignmentServiceI;
28 import jalview.ws.gui.WsJob;
29 import jalview.ws.gui.WsJob.JobState;
30
31
32 class JPredJob extends WsJob
33 {
34   Hashtable<?, ?> sequenceInfo;
35   List<SequenceI> msf;
36   int[] delMap;
37   AlignmentI alignment = null;
38   HiddenColumns hiddenCols = null;
39
40   public JPredJob(Hashtable<?, ?> sequenceInfo, SequenceI[] msf, int[] delMap)
41   {
42     this.sequenceInfo = sequenceInfo;
43     this.msf = List.of(msf);
44     this.delMap = delMap;
45   }
46
47   @Override
48   public boolean hasValidInput()
49   {
50     return true;
51   }
52
53   @Override
54   public boolean hasResults()
55   {
56     return (isSubjobComplete() && alignment != null);
57   }
58 }
59
60
61 public class JPredThread extends AWSThread implements WSClientI
62 {
63
64   private JPredMutlipleAlignmentServiceI server;
65   private String title;
66   private Hashtable<?, ?> sequenceInfo;
67   private SequenceI[] msf;
68   private int[] delMap;
69
70   public JPredThread(WebserviceInfo wsInfo, String title,
71       JPredMutlipleAlignmentServiceI server, Hashtable<?, ?> sequenceInfo,
72       SequenceI[] msf, int[] delMap, AlignmentView view, AlignFrame frame,
73       String wsURL)
74   {
75     super(frame, wsInfo, view, wsURL);
76     this.server = server;
77     this.title = title;
78     this.sequenceInfo = sequenceInfo;
79     this.msf = msf;
80     this.delMap = delMap;
81     JPredJob job = new JPredJob(sequenceInfo, msf, delMap);
82     this.jobs = new JPredJob[] { job };
83   }
84
85   @Override
86   public boolean isCancellable()
87   {
88     return server instanceof CancellableI;
89   }
90
91   @Override
92   public boolean canMergeResults()
93   {
94     return false;
95   }
96
97   @Override
98   public void cancelJob()
99   {
100     // TODO Auto-generated method stub
101
102   }
103
104   @Override
105   public void pollJob(AWsJob job_) throws Exception
106   {
107     var job = (JPredJob) job_;
108     server.updateStatus(job);
109     server.updateJobProgress(job);
110   }
111
112   @Override
113   public void StartJob(AWsJob job_)
114   {
115     if (!(job_ instanceof JPredJob))
116       throw new RuntimeException("Invalid job type");
117     var job = (JPredJob) job_;
118     if (job.isSubmitted())
119     {
120       return;
121     }
122     try {
123       try
124       {
125         var jobHandle = server.align(job.msf);
126         if (jobHandle != null)
127           job.setJobHandle(jobHandle);
128       }
129       catch (Throwable th) {
130         if (!server.handleSubmitError(th, job, wsInfo)) {
131           throw th;
132         }
133       }
134       if (job.getJobId() != null) {
135         job.setSubmitted(true);
136         job.setSubjobComplete(false);
137         return;
138       }
139       else {
140         throw new Exception(MessageManager.formatMessage(
141                 "exception.web_service_returned_null_try_later",
142                 new String[]
143                 { WsUrl }));
144       }
145     }
146     catch (Throwable th)
147     {
148       // For unexpected errors
149       System.err.println(WebServiceName
150               + "Client: Failed to submit the sequences for alignment (probably a server side problem)\n"
151               + "When contacting Server:" + WsUrl + "\n");
152       th.printStackTrace(System.err);
153       wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_SERVERERROR);
154       wsInfo.setStatus(job.getJobnum(),
155               WebserviceInfo.STATE_STOPPED_SERVERERROR);
156
157     }
158     finally
159     {
160       if (!job.isSubmitted())
161       {
162         job.setAllowedServerExceptions(0);
163         wsInfo.appendProgressText(job.getJobnum(), MessageManager.getString(
164                 "info.failed_to_submit_sequences_for_alignment"));
165       }
166     }
167   }
168
169   @Override
170   public void parseResult()
171   {
172     long progbar = (long) (Math.random() * ~(1L << 63));
173     wsInfo.setProgressBar(
174         MessageManager.getString("status.collecting_job_results"), progbar);
175     int results = 0;
176     var finalState = new JobStateSummary();
177     try
178     {
179       for (int i = 0; i < jobs.length; i++) {
180         final var job = (JPredJob) jobs[i];
181         finalState.updateJobPanelState(wsInfo, OutputHeader, job);
182         if (job.isFinished()) {
183           try {
184             server.updateJobProgress(job);
185           }
186           catch (Exception e) {
187             Cache.log.warn(format(
188                 "Exception when retrieving remaining Job progress data " +
189                 "for job %s on server %s", job.getJobId(), WsUrl));
190             e.printStackTrace();
191           }
192           // removed the waiting loop
193           Cache.log.debug(format("Job Execution file for job: %s " +
194               "on server %s%n%s", job.getJobId(), WsUrl, job.getStatus()));
195           try {
196             prepareJobResult(job);
197           }
198           catch (Exception e) {
199             if (!server.handleCollectionException(e, job, wsInfo)) {
200               Cache.log.error("Could not get alignment for job.", e);
201               job.setState(JobState.SERVERERROR);
202             }
203           }
204         }
205         finalState.updateJobPanelState(wsInfo, OutputHeader, job);
206         if (job.isSubmitted() && job.isSubjobComplete() && job.hasResults()) {
207           results++;
208         }
209       }
210     }
211     catch (Exception e) {
212       Cache.log.error(
213           "Unexpected exception when processing results for " + title, e);
214       wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_ERROR);
215     }
216     if (results > 0) {
217       wsInfo.showResultsNewFrame.addActionListener(
218           (evt) -> displayResults(true));
219       wsInfo.mergeResults.addActionListener(
220           (evt) -> displayResults(false));
221       wsInfo.setResultsReady();
222     }
223     else {
224       wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_ERROR);
225       wsInfo.appendInfoText("No jobs ran.");
226       wsInfo.setFinishedNoResults();
227     }
228     updateGlobalStatus(finalState);
229     wsInfo.removeProgressBar(progbar);
230   }
231
232   private void prepareJobResult(JPredJob job) throws Exception
233   {
234     HiddenColumns hiddenCols = null;
235     int firstSeq = -1;
236     AlignmentI alignment;
237     var prediction = server.getPrediction(job.getJobHandle());
238     var preds = prediction.getSeqsAsArray();
239
240     if (job.delMap != null)
241     {
242       Object[] alandcolsel = input
243               .getAlignmentAndHiddenColumns(getGapChar());
244       alignment = new Alignment((SequenceI[]) alandcolsel[0]);
245       hiddenCols = (HiddenColumns) alandcolsel[1];
246     }
247     else
248     {
249       alignment = server.getAlignment(job.getJobHandle());
250       var seqs = new SequenceI[alignment.getHeight()];
251       for (int i = 0; i < alignment.getHeight(); i++)
252       {
253         seqs[i] = alignment.getSequenceAt(i);
254       }
255       if (!SeqsetUtils.deuniquify(sequenceInfo, seqs))
256       {
257         throw (new Exception(MessageManager.getString(
258                 "exception.couldnt_recover_sequence_properties_for_alignment")));
259       }
260     }
261     firstSeq = 0;
262     if (currentView.getDataset() != null)
263     {
264       alignment.setDataset(currentView.getDataset());
265     }
266     else
267     {
268       alignment.setDataset(null);
269     }
270     JnetAnnotationMaker.add_annotation(prediction, alignment, firstSeq, false,
271             job.delMap);
272
273     for (var annot : alignment.getAlignmentAnnotation())
274     {
275       if (annot.sequenceRef != null)
276       {
277         replaceAnnotationOnAlignmentWith(annot, annot.label,
278                 "jalview.ws.JPred", annot.sequenceRef);
279       }
280     }
281     job.alignment = alignment;
282     job.hiddenCols = hiddenCols;
283   }
284
285   private static void replaceAnnotationOnAlignmentWith(
286           AlignmentAnnotation newAnnot, String typeName, String calcId,
287           SequenceI aSeq)
288   {
289     SequenceI dsseq = aSeq.getDatasetSequence();
290     while (dsseq.getDatasetSequence() != null)
291     {
292       dsseq = dsseq.getDatasetSequence();
293     }
294     // look for same annotation on dataset and lift this one over
295     List<AlignmentAnnotation> dsan = dsseq.getAlignmentAnnotations(calcId,
296             typeName);
297     if (dsan != null && dsan.size() > 0)
298     {
299       for (AlignmentAnnotation dssan : dsan)
300       {
301         dsseq.removeAlignmentAnnotation(dssan);
302       }
303     }
304     AlignmentAnnotation dssan = new AlignmentAnnotation(newAnnot);
305     dsseq.addAlignmentAnnotation(dssan);
306     dssan.adjustForAlignment();
307   }
308
309   private void displayResults(boolean newWindow)
310   {
311     if (jobs == null || jobs.length == 0)
312     {
313       return;
314     }
315     var job = (JPredJob) jobs[0];
316     if (job.hasResults() && newWindow)
317     {
318       AlignFrame frame;
319       job.alignment.setSeqrep(job.alignment.getSequenceAt(0));
320       frame = new AlignFrame(job.alignment, job.hiddenCols,
321               AlignFrame.DEFAULT_WIDTH, AlignFrame.DEFAULT_HEIGHT);
322       Desktop.addInternalFrame(frame, title, AlignFrame.DEFAULT_WIDTH,
323               AlignFrame.DEFAULT_HEIGHT);
324     }
325   }
326
327 }