1 /*******************************************************************************
2 * Jalview - A Sequence Alignment Editor and Viewer (Version 2.7)
3 * Copyright (C) 2011 J Procter, AM Waterhouse, J Engelhardt, LM Lui, G Barton, M Clamp, S Searle
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 of the License, or (at your option) any later version.
11 * Jalview is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty
13 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR
14 * PURPOSE. See the GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License along with Jalview. If not, see <http://www.gnu.org/licenses/>.
17 *******************************************************************************/
18 package jalview.ws.rest;
20 import java.io.IOException;
21 import java.sql.ResultSet;
22 import java.util.ArrayList;
23 import java.util.Collection;
24 import java.util.Hashtable;
25 import java.util.List;
27 import java.util.Map.Entry;
29 import java.util.Vector;
31 import jalview.datamodel.AlignmentAnnotation;
32 import jalview.datamodel.AlignmentI;
33 import jalview.datamodel.AlignmentOrder;
34 import jalview.datamodel.AlignmentView;
35 import jalview.datamodel.SequenceGroup;
36 import jalview.datamodel.SequenceI;
37 import jalview.io.packed.DataProvider;
38 import jalview.io.packed.JalviewDataset;
39 import jalview.io.packed.ParsePackedSet;
40 import jalview.io.packed.SimpleDataProvider;
41 import jalview.io.packed.DataProvider.JvDataType;
42 import jalview.ws.AWsJob;
43 import jalview.ws.rest.params.Alignment;
44 import jalview.ws.rest.params.SeqGroupIndexVector;
46 public class RestJob extends AWsJob
49 // TODO: input alignmentview and other data for this job
50 RestServiceDescription rsd;
64 * dataset associated with this input data.
68 AlignmentOrder inputOrder;
71 * context of input data with respect to an AlignmentView's visible contigs.
75 private AlignmentI contextAl=null;
78 * create a rest job using data bounded by the given start/end column.
81 * @param restJobThread
84 * visible contigs of an alignment view from which _input was derived
86 public RestJob(int jobNum, RestJobThread restJobThread,
87 AlignmentI _input, int[] viscontigs)
89 rsd = restJobThread.restClient.service;
91 if (viscontigs != null)
93 origviscontig = new int[viscontigs.length];
94 System.arraycopy(viscontigs, 0, origviscontig, 0, viscontigs.length);
96 // get sequences for the alignmentI
97 // get groups trimmed to alignment columns
98 // get any annotation trimmed to start/end columns, too.
101 // form alignment+groups+annotation,preprocess and then record references
103 ArrayList<InputType> alinp = new ArrayList<InputType>();
104 int paramsWithData = 0;
105 // we cheat for moment - since we know a-priori what data is available and
106 // what inputs we have implemented so far
107 for (Map.Entry<String, InputType> prm : rsd.inputParams.entrySet())
109 if (!prm.getValue().isConstant())
111 if (prm.getValue() instanceof Alignment)
113 alinp.add(prm.getValue());
117 // TODO: move validation of input data to SeqGroupIndexVector
118 if ((prm.getValue() instanceof SeqGroupIndexVector)
119 && (_input.getGroups() != null && _input.getGroups()
120 .size() >= prm.getValue().min))
122 alinp.add(prm.getValue());
124 statMessage=("Not enough groups defined on the alignment - need at least "+prm.getValue().min);
133 if ((paramsWithData + alinp.size()) == rsd.inputParams.size())
135 inputOrder = new AlignmentOrder(_input);
136 if ((dsForIO = _input.getDataset()) == null)
138 _input.setDataset(null);
140 dsForIO = _input.getDataset();
145 setAlignmentForInputs(alinp, _input);
150 // not enough data, so we bail.
155 boolean validInput = false;
158 public boolean hasResults()
160 return gotresult && (parsedResults ? validJvresults : true);
164 public boolean hasValidInput()
170 public boolean isRunning()
172 return running; // TODO: can we check the response body for status messages
177 public boolean isQueued()
183 public boolean isFinished()
185 return resSet != null;
189 public boolean isFailed()
191 // TODO logic for error
196 public boolean isBroken()
198 // TODO logic for error
203 public boolean isServerError()
205 // TODO logic for error
210 public boolean hasStatus()
212 return statMessage != null;
215 protected String statMessage = null;
217 public HttpResultSet resSet;
220 public String getStatus()
226 public boolean hasResponse()
228 return statMessage != null || resSet != null;
232 public void clearResponse()
234 // only clear the transient server response
241 * @see jalview.ws.AWsJob#getState()
244 public String getState()
246 // TODO generate state string - prolly should have a default abstract method
248 return "Job is clueless";
251 public String getPostUrl()
254 // TODO Auto-generated method stub
258 public Set<Map.Entry<String, InputType>> getInputParams()
260 return rsd.inputParams.entrySet();
263 // return the URL that should be polled for this job
264 public String getPollUrl()
266 return rsd.getDecoratedResultUrl(jobId);
271 * @return the context for parsing results from service
273 public JalviewDataset newJalviewDataset()
277 context = new JalviewDataset(dsForIO, null, squniq, null);
280 // TODO devise way of merging new annotation onto (identical) existing annotation that was used as input
281 // delete all input annotation
282 if (contextAl.getAlignmentAnnotation()!=null) {
283 for (AlignmentAnnotation alan: contextAl.getAlignmentAnnotation()) {
284 contextAl.deleteAnnotation(alan);
287 // TODO devise way of merging new groups onto (identical) existing groups when they were used as input to service
288 // delete all existing groups
289 if (contextAl.getGroups()!=null)
291 contextAl.deleteAllGroups();
293 context.addAlignment(contextAl);
301 * Extract list of sequence IDs for input parameter 'token' with given
308 public SequenceI[] getSequencesForInput(String token,
309 InputType.molType type) throws NoValidInputDataException
311 Object sgdat = inputData.get(token);
312 // can we form an alignment from this data ?
315 throw new NoValidInputDataException(
316 "No Sequence vector data bound to input '" + token
317 + "' for service at " + rsd.postUrl);
319 if (sgdat instanceof AlignmentI)
321 return ((AlignmentI) sgdat).getSequencesArray();
323 if (sgdat instanceof SequenceGroup)
325 return ((SequenceGroup) sgdat).getSequencesAsArray(null);
327 if (sgdat instanceof Vector)
329 if (((Vector) sgdat).size() > 0
330 && ((Vector) sgdat).get(0) instanceof SequenceI)
332 SequenceI[] sq = new SequenceI[((Vector) sgdat).size()];
333 ((Vector) sgdat).copyInto(sq);
337 throw new NoValidInputDataException(
338 "No Sequence vector data bound to input '" + token
339 + "' for service at " + rsd.postUrl);
343 * binding between input data (AlignmentI, SequenceGroup, NJTree) and input
346 private Hashtable<String, Object> inputData = new Hashtable<String, Object>();
349 * is the job fully submitted to server and apparently in progress ?
351 public boolean running = false;
357 * - reference to object to be stored as input. Note - input data may
358 * be modifed by formatter
360 public void setAlignmentForInputs(Collection<InputType> itypes,
363 for (InputType itype : itypes)
365 if (!rsd.inputParams.values().contains(itype))
367 throw new IllegalArgumentException("InputType " + itype.getClass()
368 + " is not valid for service at " + rsd.postUrl);
370 if (itype instanceof AlignmentProcessor)
372 ((AlignmentProcessor) itype).prepareAlignment(al);
374 // stash a reference for recall when the alignment data is formatted
375 inputData.put(itype.token, al);
384 * @return alignment object bound to the given token
385 * @throws NoValidInputDataException
387 public AlignmentI getAlignmentForInput(String token,
388 InputType.molType type) throws NoValidInputDataException
390 Object al = inputData.get(token);
391 // can we form an alignment from this data ?
392 if (al == null || !(al instanceof AlignmentI))
394 throw new NoValidInputDataException(
395 "No alignment data bound to input '" + token
396 + "' for service at " + rsd.postUrl);
398 return (AlignmentI) al;
402 * test to see if the job has data of type cl that's needed for the job to run
405 * @return true or false
407 public boolean hasDataOfType(Class cl)
409 if (AlignmentI.class.isAssignableFrom(cl))
413 // TODO: add more source data types
419 * context used to parse results from service
421 JalviewDataset context = null;
422 protected boolean parsedResults = false;
423 protected boolean validJvresults=false;
424 Object[] jvresultobj = null;
426 * process the results obtained from the server into jalview datamodel objects
427 * ready to be merged/added to the users' view. Use hasResults to test if results were added to context.
429 public void parseResultSet() throws Exception, Error
431 if (!parsedResults) {
433 jvresultobj = resSet.parseResultSet();
434 validJvresults = true;
440 * @return true if job has an input alignment and it was annotated when results were parsed
442 public boolean isInputContextModified()
444 return contextAl!=null && validJvresults && context.getAl().get(0).isModified();
449 * @return true if the ID/metadata for the input sequences were saved and sequence IDs renamed.
451 public boolean isInputUniquified()
453 // TODO Auto-generated method stub
458 * Return map between ordering of alignment submitted as input, and ordering of alignment as provided by user
459 * @return int[sequence index in submitted data]==sequence index in input.
461 public int[] getOrderMap()
463 SequenceI[] contseq = contextAl.getSequencesArray();
464 int map[] = new int[contseq.length];
465 for (int i=0;i<contseq.length;i++)
467 // TODO: optimise for large N - build a lookup hash for IDs returning order, and then lookup each sequ's original order
468 map[i] = inputOrder.getOrder().indexOf(contseq[i]);