Class |
Line # |
Actions |
|||
---|---|---|---|---|---|
AWSThread | 37 | 88 | 42 |
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; | |
22 | ||
23 | import jalview.bin.Console; | |
24 | import jalview.datamodel.AlignedCodonFrame; | |
25 | import jalview.datamodel.Alignment; | |
26 | import jalview.datamodel.AlignmentI; | |
27 | import jalview.datamodel.AlignmentView; | |
28 | import jalview.datamodel.SequenceI; | |
29 | import jalview.gui.AlignFrame; | |
30 | import jalview.gui.WebserviceInfo; | |
31 | import jalview.util.MessageManager; | |
32 | import jalview.viewmodel.seqfeatures.FeatureRendererSettings; | |
33 | ||
34 | import java.util.ArrayList; | |
35 | import java.util.List; | |
36 | ||
37 | public abstract class AWSThread extends Thread | |
38 | { | |
39 | ||
40 | /** | |
41 | * view that this job was associated with | |
42 | */ | |
43 | protected AlignmentI currentView = null; | |
44 | ||
45 | /** | |
46 | * feature settings from view that job was associated with | |
47 | */ | |
48 | protected FeatureRendererSettings featureSettings = null; | |
49 | ||
50 | /** | |
51 | * metadata about this web service | |
52 | */ | |
53 | protected WebserviceInfo wsInfo = null; | |
54 | ||
55 | /** | |
56 | * original input data for this job | |
57 | */ | |
58 | protected AlignmentView input = null; | |
59 | ||
60 | /** | |
61 | * dataset sequence relationships to be propagated onto new results | |
62 | */ | |
63 | protected List<AlignedCodonFrame> codonframe = null; | |
64 | ||
65 | /** | |
66 | * are there jobs still running in this thread. | |
67 | */ | |
68 | protected boolean jobComplete = false; | |
69 | ||
70 | /** | |
71 | * one or more jobs being managed by this thread. | |
72 | */ | |
73 | protected AWsJob jobs[] = null; | |
74 | ||
75 | /** | |
76 | * full name of service | |
77 | */ | |
78 | protected String WebServiceName = null; | |
79 | ||
80 | protected char defGapChar = '-'; | |
81 | ||
82 | /** | |
83 | * header prepended to all output from job | |
84 | */ | |
85 | protected String OutputHeader; | |
86 | ||
87 | /** | |
88 | * only used when reporting a web service out of memory error - the job ID | |
89 | * will be concatenated to the URL | |
90 | */ | |
91 | protected String WsUrl = null; | |
92 | ||
93 | /* | |
94 | * The AlignFrame from which the service was requested. | |
95 | */ | |
96 | private AlignFrame alignFrame; | |
97 | ||
98 | /** | |
99 | * generic web service job/subjob poll loop | |
100 | */ | |
101 | 1 | @Override |
102 | public void run() | |
103 | { | |
104 | 1 | JobStateSummary jstate = null; |
105 | 1 | if (jobs == null) |
106 | { | |
107 | 0 | jobComplete = true; |
108 | } | |
109 | 1 | while (!jobComplete) |
110 | { | |
111 | 1 | jstate = new JobStateSummary(); |
112 | 1 | for (int j = 0; j < jobs.length; j++) |
113 | { | |
114 | ||
115 | 1 | if (!jobs[j].submitted && jobs[j].hasValidInput()) |
116 | { | |
117 | 1 | StartJob(jobs[j]); |
118 | } | |
119 | ||
120 | 1 | if (jobs[j].submitted && !jobs[j].subjobComplete) |
121 | { | |
122 | 0 | try |
123 | { | |
124 | 0 | pollJob(jobs[j]); |
125 | 0 | if (!jobs[j].hasResponse()) |
126 | { | |
127 | 0 | throw (new Exception( |
128 | "Timed out when communicating with server\nTry again later.\n")); | |
129 | } | |
130 | 0 | Console.debug("Job " + j + " Result state " + jobs[j].getState() |
131 | + "(ServerError=" + jobs[j].isServerError() + ")"); | |
132 | } catch (Exception ex) | |
133 | { | |
134 | // Deal with Transaction exceptions | |
135 | 0 | wsInfo.appendProgressText(jobs[j].jobnum, MessageManager |
136 | .formatMessage("info.server_exception", new Object[] | |
137 | { WebServiceName, ex.getMessage() })); | |
138 | // always output the exception's stack trace to the log | |
139 | 0 | Console.warn(WebServiceName + " job(" + jobs[j].jobnum |
140 | + ") Server exception."); | |
141 | // todo: could limit trace to cause if this is a SOAPFaultException. | |
142 | 0 | ex.printStackTrace(); |
143 | ||
144 | 0 | if (jobs[j].allowedServerExceptions > 0) |
145 | { | |
146 | 0 | jobs[j].allowedServerExceptions--; |
147 | 0 | Console.debug("Sleeping after a server exception."); |
148 | 0 | try |
149 | { | |
150 | 0 | Thread.sleep(5000); |
151 | } catch (InterruptedException ex1) | |
152 | { | |
153 | } | |
154 | } | |
155 | else | |
156 | { | |
157 | 0 | Console.warn("Dropping job " + j + " " + jobs[j].jobId); |
158 | 0 | jobs[j].subjobComplete = true; |
159 | 0 | wsInfo.setStatus(jobs[j].jobnum, |
160 | WebserviceInfo.STATE_STOPPED_SERVERERROR); | |
161 | } | |
162 | } catch (OutOfMemoryError er) | |
163 | { | |
164 | 0 | jobComplete = true; |
165 | 0 | jobs[j].subjobComplete = true; |
166 | 0 | jobs[j].clearResponse(); // may contain out of date result data |
167 | 0 | wsInfo.setStatus(jobs[j].jobnum, |
168 | WebserviceInfo.STATE_STOPPED_ERROR); | |
169 | 0 | Console.error("Out of memory when retrieving Job " + j + " id:" |
170 | + WsUrl + "/" + jobs[j].jobId, er); | |
171 | 0 | new jalview.gui.OOMWarning( |
172 | "retrieving result for " + WebServiceName, er); | |
173 | 0 | System.gc(); |
174 | } | |
175 | } | |
176 | 1 | jstate.updateJobPanelState(wsInfo, OutputHeader, jobs[j]); |
177 | } | |
178 | // Decide on overall state based on collected jobs[] states | |
179 | 0 | updateGlobalStatus(jstate); |
180 | 0 | if (!jobComplete) |
181 | { | |
182 | 0 | try |
183 | { | |
184 | 0 | Thread.sleep(5000); |
185 | } catch (InterruptedException e) | |
186 | { | |
187 | 0 | Console.debug("Interrupted sleep waiting for next job poll.", e); |
188 | } | |
189 | // jalview.bin.Console.outPrintln("I'm alive "+alTitle); | |
190 | } | |
191 | } | |
192 | 0 | if (jobComplete && jobs != null) |
193 | { | |
194 | 0 | parseResult(); // tidy up and make results available to user |
195 | } | |
196 | else | |
197 | { | |
198 | 0 | Console.debug( |
199 | "WebServiceJob poll loop finished with no jobs created."); | |
200 | 0 | wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_ERROR); |
201 | 0 | wsInfo.appendProgressText( |
202 | MessageManager.getString("info.no_jobs_ran")); | |
203 | 0 | wsInfo.setFinishedNoResults(); |
204 | } | |
205 | } | |
206 | ||
207 | 0 | protected void updateGlobalStatus(JobStateSummary jstate) |
208 | { | |
209 | 0 | if (jstate.running > 0) |
210 | { | |
211 | 0 | wsInfo.setStatus(WebserviceInfo.STATE_RUNNING); |
212 | } | |
213 | 0 | else if (jstate.queuing > 0) |
214 | { | |
215 | 0 | wsInfo.setStatus(WebserviceInfo.STATE_QUEUING); |
216 | } | |
217 | else | |
218 | { | |
219 | 0 | jobComplete = true; |
220 | 0 | if (jstate.finished > 0) |
221 | { | |
222 | 0 | wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_OK); |
223 | } | |
224 | 0 | else if (jstate.error > 0) |
225 | { | |
226 | 0 | wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_ERROR); |
227 | } | |
228 | 0 | else if (jstate.serror > 0) |
229 | { | |
230 | 0 | wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_SERVERERROR); |
231 | } | |
232 | } | |
233 | } | |
234 | ||
235 | 0 | public AWSThread() |
236 | { | |
237 | 0 | super(); |
238 | } | |
239 | ||
240 | 0 | public AWSThread(Runnable target) |
241 | { | |
242 | 0 | super(target); |
243 | } | |
244 | ||
245 | 0 | public AWSThread(String name) |
246 | { | |
247 | 0 | super(name); |
248 | } | |
249 | ||
250 | 0 | public AWSThread(ThreadGroup group, Runnable target) |
251 | { | |
252 | 0 | super(group, target); |
253 | } | |
254 | ||
255 | 0 | public AWSThread(ThreadGroup group, String name) |
256 | { | |
257 | 0 | super(group, name); |
258 | } | |
259 | ||
260 | 0 | public AWSThread(Runnable target, String name) |
261 | { | |
262 | 0 | super(target, name); |
263 | } | |
264 | ||
265 | 0 | public AWSThread(ThreadGroup group, Runnable target, String name) |
266 | { | |
267 | 0 | super(group, target, name); |
268 | } | |
269 | ||
270 | /** | |
271 | * query web service for status of job. on return, job.result must not be null | |
272 | * - if it is then it will be assumed that the job status query timed out and | |
273 | * a server exception will be logged. | |
274 | * | |
275 | * @param job | |
276 | * @throws Exception | |
277 | * will be logged as a server exception for this job | |
278 | */ | |
279 | public abstract void pollJob(AWsJob job) throws Exception; | |
280 | ||
281 | /** | |
282 | * submit job to web service | |
283 | * | |
284 | * @param job | |
285 | */ | |
286 | public abstract void StartJob(AWsJob job); | |
287 | ||
288 | /** | |
289 | * process the set of AWsJob objects into a set of results, and tidy up. | |
290 | */ | |
291 | public abstract void parseResult(); | |
292 | ||
293 | /** | |
294 | * helper function to conserve dataset references to sequence objects returned | |
295 | * from web services 1. Propagates AlCodonFrame data from | |
296 | * <code>codonframe</code> to <code>al</code> TODO: refactor to datamodel | |
297 | * | |
298 | * @param al | |
299 | */ | |
300 | 0 | public void propagateDatasetMappings(Alignment al) |
301 | { | |
302 | 0 | if (codonframe != null) |
303 | { | |
304 | 0 | SequenceI[] alignment = al.getSequencesArray(); |
305 | 0 | for (int sq = 0; sq < alignment.length; sq++) |
306 | { | |
307 | 0 | for (AlignedCodonFrame acf : codonframe) |
308 | { | |
309 | 0 | final SequenceI seq = alignment[sq]; |
310 | 0 | if (acf != null && acf.involvesSequence(seq)) |
311 | { | |
312 | 0 | al.addCodonFrame(acf); |
313 | 0 | break; |
314 | } | |
315 | } | |
316 | } | |
317 | } | |
318 | } | |
319 | ||
320 | 0 | public AWSThread(ThreadGroup group, Runnable target, String name, |
321 | long stackSize) | |
322 | { | |
323 | 0 | super(group, target, name, stackSize); |
324 | } | |
325 | ||
326 | /** | |
327 | * | |
328 | * @return gap character to use for any alignment generation | |
329 | */ | |
330 | 0 | public char getGapChar() |
331 | { | |
332 | 0 | return defGapChar; |
333 | } | |
334 | ||
335 | /** | |
336 | * | |
337 | * @param alignFrame | |
338 | * reference for copying mappings across | |
339 | * @param wsInfo | |
340 | * gui attachment point | |
341 | * @param input | |
342 | * input data for the calculation | |
343 | * @param webServiceName | |
344 | * name of service | |
345 | * @param wsUrl | |
346 | * url of the service being invoked | |
347 | */ | |
348 | 0 | public AWSThread(AlignFrame alignFrame, WebserviceInfo wsinfo, |
349 | AlignmentView input, String webServiceName, String wsUrl) | |
350 | { | |
351 | 0 | this(alignFrame, wsinfo, input, wsUrl); |
352 | 0 | WebServiceName = webServiceName; |
353 | } | |
354 | ||
355 | /** | |
356 | * Extracts additional info from alignment view's context. | |
357 | * | |
358 | * @param alframe | |
359 | * - reference for copying mappings and display styles across | |
360 | * @param wsinfo2 | |
361 | * - gui attachment point - may be null | |
362 | * @param alview | |
363 | * - input data for the calculation | |
364 | * @param wsurl2 | |
365 | * - url of the service being invoked | |
366 | */ | |
367 | 2 | public AWSThread(AlignFrame alframe, WebserviceInfo wsinfo2, |
368 | AlignmentView alview, String wsurl2) | |
369 | { | |
370 | 2 | super(); |
371 | 2 | this.alignFrame = alframe; |
372 | 2 | currentView = alframe.getCurrentView().getAlignment(); |
373 | 2 | featureSettings = alframe.getFeatureRenderer().getSettings(); |
374 | 2 | defGapChar = alframe.getViewport().getGapCharacter(); |
375 | 2 | this.wsInfo = wsinfo2; |
376 | 2 | this.input = alview; |
377 | 2 | WsUrl = wsurl2; |
378 | 2 | if (alframe != null) |
379 | { | |
380 | 2 | List<AlignedCodonFrame> cf = alframe.getViewport().getAlignment() |
381 | .getCodonFrames(); | |
382 | 2 | if (cf != null) |
383 | { | |
384 | 2 | codonframe = new ArrayList<AlignedCodonFrame>(); |
385 | 2 | codonframe.addAll(cf); |
386 | } | |
387 | } | |
388 | } | |
389 | ||
390 | 0 | protected AlignFrame getRequestingAlignFrame() |
391 | { | |
392 | 0 | return this.alignFrame; |
393 | } | |
394 | } |