Class |
Line # |
Actions |
|||
---|---|---|---|---|---|
RestJob | 40 | 88 | 53 |
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.rest; | |
22 | ||
23 | import jalview.datamodel.AlignmentAnnotation; | |
24 | import jalview.datamodel.AlignmentI; | |
25 | import jalview.datamodel.AlignmentOrder; | |
26 | import jalview.datamodel.SequenceGroup; | |
27 | import jalview.datamodel.SequenceI; | |
28 | import jalview.io.packed.JalviewDataset; | |
29 | import jalview.ws.AWsJob; | |
30 | import jalview.ws.rest.params.Alignment; | |
31 | import jalview.ws.rest.params.SeqGroupIndexVector; | |
32 | ||
33 | import java.util.ArrayList; | |
34 | import java.util.Collection; | |
35 | import java.util.Hashtable; | |
36 | import java.util.Map; | |
37 | import java.util.Set; | |
38 | import java.util.Vector; | |
39 | ||
40 | public class RestJob extends AWsJob | |
41 | { | |
42 | ||
43 | // TODO: input alignmentview and other data for this job | |
44 | RestServiceDescription rsd; | |
45 | ||
46 | // boolean submitted; | |
47 | boolean gotresponse; | |
48 | ||
49 | boolean error; | |
50 | ||
51 | boolean waiting; | |
52 | ||
53 | boolean gotresult; | |
54 | ||
55 | Hashtable squniq; | |
56 | ||
57 | /** | |
58 | * dataset associated with this input data. | |
59 | */ | |
60 | AlignmentI dsForIO; | |
61 | ||
62 | AlignmentOrder inputOrder; | |
63 | ||
64 | /** | |
65 | * context of input data with respect to an AlignmentView's visible contigs. | |
66 | */ | |
67 | int[] origviscontig; | |
68 | ||
69 | private AlignmentI contextAl = null; | |
70 | ||
71 | /** | |
72 | * create a rest job using data bounded by the given start/end column. | |
73 | * | |
74 | * @param addJobPane | |
75 | * @param restJobThread | |
76 | * @param _input | |
77 | * @param viscontigs | |
78 | * visible contigs of an alignment view from which _input was derived | |
79 | */ | |
80 | 3 | public RestJob(int jobNum, RestJobThread restJobThread, AlignmentI _input, |
81 | int[] viscontigs) | |
82 | { | |
83 | 3 | rsd = restJobThread.restClient.service; |
84 | 3 | jobnum = jobNum; |
85 | 3 | if (viscontigs != null) |
86 | { | |
87 | 2 | origviscontig = new int[viscontigs.length]; |
88 | 2 | System.arraycopy(viscontigs, 0, origviscontig, 0, viscontigs.length); |
89 | } | |
90 | // get sequences for the alignmentI | |
91 | // get groups trimmed to alignment columns | |
92 | // get any annotation trimmed to start/end columns, too. | |
93 | 3 | squniq = jalview.analysis.SeqsetUtils |
94 | .uniquify(_input.getSequencesArray(), true); | |
95 | // prepare input | |
96 | // form alignment+groups+annotation,preprocess and then record references | |
97 | // for formatters | |
98 | 3 | ArrayList<InputType> alinp = new ArrayList<InputType>(); |
99 | 3 | int paramsWithData = 0; |
100 | // TODO: JAL-715 - generalise the following validation logic for all | |
101 | // parameter types | |
102 | // we cheat for moment - since we know a-priori what data is available and | |
103 | // what inputs we have implemented so far | |
104 | 3 | for (Map.Entry<String, InputType> prm : rsd.inputParams.entrySet()) |
105 | { | |
106 | 6 | if (!prm.getValue().isConstant()) |
107 | { | |
108 | 6 | if (prm.getValue() instanceof Alignment) |
109 | { | |
110 | 3 | alinp.add(prm.getValue()); |
111 | } | |
112 | else | |
113 | { | |
114 | 3 | if (prm.getValue() instanceof SeqGroupIndexVector |
115 | && _input.getGroups() != null | |
116 | && _input.getGroups().size() >= -1 + prm.getValue().min) | |
117 | { | |
118 | // the test above is not rigorous but fixes JAL-1298, since | |
119 | // submission will fail if the partition set doesn't contain at | |
120 | // least one partition | |
121 | 3 | alinp.add(prm.getValue()); |
122 | } | |
123 | else | |
124 | { | |
125 | 0 | statMessage = ("Not enough groups defined on the alignment - need at least " |
126 | + prm.getValue().min); | |
127 | } | |
128 | } | |
129 | } | |
130 | else | |
131 | { | |
132 | 0 | paramsWithData++; |
133 | } | |
134 | } | |
135 | 3 | if ((paramsWithData + alinp.size()) == rsd.inputParams.size()) |
136 | { | |
137 | 3 | inputOrder = new AlignmentOrder(_input); |
138 | ? | if ((dsForIO = _input.getDataset()) == null) |
139 | { | |
140 | 2 | _input.setDataset(null); |
141 | } | |
142 | 3 | dsForIO = _input.getDataset(); |
143 | 3 | if (contextAl == null) |
144 | { | |
145 | 3 | contextAl = _input; |
146 | } | |
147 | 3 | setAlignmentForInputs(alinp, _input); |
148 | 3 | validInput = true; |
149 | } | |
150 | else | |
151 | { | |
152 | // not enough data, so we bail. | |
153 | 0 | validInput = false; |
154 | } | |
155 | } | |
156 | ||
157 | boolean validInput = false; | |
158 | ||
159 | 0 | @Override |
160 | public boolean hasResults() | |
161 | { | |
162 | 0 | return gotresult && (parsedResults ? validJvresults : true); |
163 | } | |
164 | ||
165 | 2 | @Override |
166 | public boolean hasValidInput() | |
167 | { | |
168 | 2 | return validInput; |
169 | } | |
170 | ||
171 | 1 | @Override |
172 | public boolean isRunning() | |
173 | { | |
174 | 1 | return running; // TODO: can we check the response body for status messages |
175 | // ? | |
176 | } | |
177 | ||
178 | 1 | @Override |
179 | public boolean isQueued() | |
180 | { | |
181 | 1 | return waiting; |
182 | } | |
183 | ||
184 | 1 | @Override |
185 | public boolean isFinished() | |
186 | { | |
187 | 1 | return resSet != null; |
188 | } | |
189 | ||
190 | 1 | @Override |
191 | public boolean isFailed() | |
192 | { | |
193 | // TODO logic for error | |
194 | 1 | return error; |
195 | } | |
196 | ||
197 | 1 | @Override |
198 | public boolean isBroken() | |
199 | { | |
200 | // TODO logic for error | |
201 | 1 | return error; |
202 | } | |
203 | ||
204 | 1 | @Override |
205 | public boolean isServerError() | |
206 | { | |
207 | // TODO logic for error | |
208 | 1 | return error; |
209 | } | |
210 | ||
211 | 1 | @Override |
212 | public boolean hasStatus() | |
213 | { | |
214 | 1 | return statMessage != null; |
215 | } | |
216 | ||
217 | protected String statMessage = null; | |
218 | ||
219 | public HttpResultSet resSet; | |
220 | ||
221 | 1 | @Override |
222 | public String getStatus() | |
223 | { | |
224 | 1 | return statMessage; |
225 | } | |
226 | ||
227 | 0 | @Override |
228 | public boolean hasResponse() | |
229 | { | |
230 | 0 | return statMessage != null || resSet != null; |
231 | } | |
232 | ||
233 | 0 | @Override |
234 | public void clearResponse() | |
235 | { | |
236 | // only clear the transient server response | |
237 | // statMessage=null; | |
238 | } | |
239 | ||
240 | /* | |
241 | * (non-Javadoc) | |
242 | * | |
243 | * @see jalview.ws.AWsJob#getState() | |
244 | */ | |
245 | 0 | @Override |
246 | public String getState() | |
247 | { | |
248 | // TODO generate state string - prolly should have a default abstract method | |
249 | // for this | |
250 | 0 | return "Job is clueless"; |
251 | } | |
252 | ||
253 | 1 | public String getPostUrl() |
254 | { | |
255 | ||
256 | // TODO Auto-generated method stub | |
257 | 1 | return rsd.postUrl; |
258 | } | |
259 | ||
260 | 1 | public Set<Map.Entry<String, InputType>> getInputParams() |
261 | { | |
262 | 1 | return rsd.inputParams.entrySet(); |
263 | } | |
264 | ||
265 | // return the URL that should be polled for this job | |
266 | 0 | public String getPollUrl() |
267 | { | |
268 | 0 | return rsd.getDecoratedResultUrl(jobId); |
269 | } | |
270 | ||
271 | /** | |
272 | * | |
273 | * @return the context for parsing results from service | |
274 | */ | |
275 | 0 | public JalviewDataset newJalviewDataset() |
276 | { | |
277 | 0 | if (context == null) |
278 | { | |
279 | 0 | context = new JalviewDataset(dsForIO, null, squniq, null); |
280 | 0 | if (contextAl != null) |
281 | { | |
282 | // TODO devise way of merging new annotation onto (identical) existing | |
283 | // annotation that was used as input | |
284 | // delete all input annotation | |
285 | 0 | if (contextAl.getAlignmentAnnotation() != null) |
286 | { | |
287 | 0 | for (AlignmentAnnotation alan : contextAl |
288 | .getAlignmentAnnotation()) | |
289 | { | |
290 | 0 | contextAl.deleteAnnotation(alan); |
291 | } | |
292 | } | |
293 | // TODO devise way of merging new groups onto (identical) existing | |
294 | // groups when they were used as input to service | |
295 | // delete all existing groups | |
296 | 0 | if (contextAl.getGroups() != null) |
297 | { | |
298 | 0 | contextAl.deleteAllGroups(); |
299 | } | |
300 | 0 | context.addAlignment(contextAl); |
301 | } | |
302 | ||
303 | } | |
304 | 0 | return context; |
305 | } | |
306 | ||
307 | /** | |
308 | * Extract list of sequence IDs for input parameter 'token' with given | |
309 | * molecule type | |
310 | * | |
311 | * @param token | |
312 | * @param type | |
313 | * @return | |
314 | */ | |
315 | 0 | public SequenceI[] getSequencesForInput(String token, |
316 | InputType.molType type) throws NoValidInputDataException | |
317 | { | |
318 | 0 | Object sgdat = inputData.get(token); |
319 | // can we form an alignment from this data ? | |
320 | 0 | if (sgdat == null) |
321 | { | |
322 | 0 | throw new NoValidInputDataException( |
323 | "No Sequence vector data bound to input '" + token | |
324 | + "' for service at " + rsd.postUrl); | |
325 | } | |
326 | 0 | if (sgdat instanceof AlignmentI) |
327 | { | |
328 | 0 | return ((AlignmentI) sgdat).getSequencesArray(); |
329 | } | |
330 | 0 | if (sgdat instanceof SequenceGroup) |
331 | { | |
332 | 0 | return ((SequenceGroup) sgdat).getSequencesAsArray(null); |
333 | } | |
334 | 0 | if (sgdat instanceof Vector) |
335 | { | |
336 | 0 | if (((Vector) sgdat).size() > 0 |
337 | && ((Vector) sgdat).get(0) instanceof SequenceI) | |
338 | { | |
339 | 0 | SequenceI[] sq = new SequenceI[((Vector) sgdat).size()]; |
340 | 0 | ((Vector) sgdat).copyInto(sq); |
341 | 0 | return sq; |
342 | } | |
343 | } | |
344 | 0 | throw new NoValidInputDataException( |
345 | "No Sequence vector data bound to input '" + token | |
346 | + "' for service at " + rsd.postUrl); | |
347 | } | |
348 | ||
349 | /** | |
350 | * binding between input data (AlignmentI, SequenceGroup, NJTree) and input | |
351 | * param names. | |
352 | */ | |
353 | private Hashtable<String, Object> inputData = new Hashtable<String, Object>(); | |
354 | ||
355 | /** | |
356 | * is the job fully submitted to server and apparently in progress ? | |
357 | */ | |
358 | public boolean running = false; | |
359 | ||
360 | /** | |
361 | * | |
362 | * @param itypes | |
363 | * @param al | |
364 | * - reference to object to be stored as input. Note - input data may | |
365 | * be modifed by formatter | |
366 | */ | |
367 | 4 | public void setAlignmentForInputs(Collection<InputType> itypes, |
368 | AlignmentI al) | |
369 | { | |
370 | 4 | for (InputType itype : itypes) |
371 | { | |
372 | 8 | if (!rsd.inputParams.values().contains(itype)) |
373 | { | |
374 | 0 | throw new IllegalArgumentException("InputType " + itype.getClass() |
375 | + " is not valid for service at " + rsd.postUrl); | |
376 | } | |
377 | 8 | if (itype instanceof AlignmentProcessor) |
378 | { | |
379 | 4 | ((AlignmentProcessor) itype).prepareAlignment(al); |
380 | } | |
381 | // stash a reference for recall when the alignment data is formatted | |
382 | 8 | inputData.put(itype.token, al); |
383 | } | |
384 | ||
385 | } | |
386 | ||
387 | /** | |
388 | * | |
389 | * @param token | |
390 | * @param type | |
391 | * @return alignment object bound to the given token | |
392 | * @throws NoValidInputDataException | |
393 | */ | |
394 | 4 | public AlignmentI getAlignmentForInput(String token, |
395 | InputType.molType type) throws NoValidInputDataException | |
396 | { | |
397 | 4 | Object al = inputData.get(token); |
398 | // can we form an alignment from this data ? | |
399 | 4 | if (al == null || !(al instanceof AlignmentI)) |
400 | { | |
401 | 0 | throw new NoValidInputDataException( |
402 | "No alignment data bound to input '" + token | |
403 | + "' for service at " + rsd.postUrl); | |
404 | } | |
405 | 4 | return (AlignmentI) al; |
406 | } | |
407 | ||
408 | /** | |
409 | * test to see if the job has data of type cl that's needed for the job to run | |
410 | * | |
411 | * @param cl | |
412 | * @return true or false | |
413 | */ | |
414 | 2 | public boolean hasDataOfType(Class cl) |
415 | { | |
416 | 2 | if (AlignmentI.class.isAssignableFrom(cl)) |
417 | { | |
418 | 2 | return true; |
419 | } | |
420 | // TODO: add more source data types | |
421 | ||
422 | 0 | return false; |
423 | } | |
424 | ||
425 | /** | |
426 | * context used to parse results from service | |
427 | */ | |
428 | JalviewDataset context = null; | |
429 | ||
430 | protected boolean parsedResults = false; | |
431 | ||
432 | protected boolean validJvresults = false; | |
433 | ||
434 | Object[] jvresultobj = null; | |
435 | ||
436 | /** | |
437 | * process the results obtained from the server into jalview datamodel objects | |
438 | * ready to be merged/added to the users' view. Use hasResults to test if | |
439 | * results were added to context. | |
440 | */ | |
441 | 0 | public void parseResultSet() throws Exception, Error |
442 | { | |
443 | 0 | if (!parsedResults) |
444 | { | |
445 | 0 | parsedResults = true; |
446 | 0 | jvresultobj = resSet.parseResultSet(); |
447 | 0 | validJvresults = true; |
448 | } | |
449 | } | |
450 | ||
451 | /** | |
452 | * | |
453 | * @return true if job has an input alignment and it was annotated when | |
454 | * results were parsed | |
455 | */ | |
456 | 0 | public boolean isInputContextModified() |
457 | { | |
458 | 0 | return contextAl != null && validJvresults |
459 | && context.getAl().get(0).isModified(); | |
460 | } | |
461 | ||
462 | /** | |
463 | * | |
464 | * @return true if the ID/metadata for the input sequences were saved and | |
465 | * sequence IDs renamed. | |
466 | */ | |
467 | 0 | public boolean isInputUniquified() |
468 | { | |
469 | // TODO Auto-generated method stub | |
470 | 0 | return false; |
471 | } | |
472 | ||
473 | /** | |
474 | * Return map between ordering of alignment submitted as input, and ordering | |
475 | * of alignment as provided by user | |
476 | * | |
477 | * @return int[sequence index in submitted data]==sequence index in input. | |
478 | */ | |
479 | 0 | public int[] getOrderMap() |
480 | { | |
481 | 0 | SequenceI[] contseq = contextAl.getSequencesArray(); |
482 | 0 | int map[] = new int[contseq.length]; |
483 | 0 | for (int i = 0; i < contseq.length; i++) |
484 | { | |
485 | // TODO: optimise for large N - build a lookup hash for IDs returning | |
486 | // order, and then lookup each sequ's original order | |
487 | 0 | map[i] = inputOrder.getOrder().indexOf(contseq[i]); |
488 | } | |
489 | 0 | return map; |
490 | } | |
491 | ||
492 | } |