more fixes and extension to allow return data types to be specified JAL-715
[jalview.git] / src / jalview / ws / rest / RestJob.java
1 package jalview.ws.rest;
2
3 import java.io.IOException;
4 import java.util.ArrayList;
5 import java.util.Collection;
6 import java.util.Hashtable;
7 import java.util.List;
8 import java.util.Map;
9 import java.util.Map.Entry;
10 import java.util.Set;
11 import java.util.Vector;
12
13 import jalview.datamodel.AlignmentI;
14 import jalview.datamodel.AlignmentOrder;
15 import jalview.datamodel.AlignmentView;
16 import jalview.datamodel.SequenceGroup;
17 import jalview.datamodel.SequenceI;
18 import jalview.io.packed.DataProvider;
19 import jalview.io.packed.JalviewDataset;
20 import jalview.io.packed.ParsePackedSet;
21 import jalview.io.packed.SimpleDataProvider;
22 import jalview.io.packed.DataProvider.JvDataType;
23 import jalview.ws.AWsJob;
24 import jalview.ws.rest.params.Alignment;
25 import jalview.ws.rest.params.SeqGroupIndexVector;
26
27 public class RestJob extends AWsJob
28 {
29
30   // TODO: input alignmentview and other data for this job
31   RestServiceDescription rsd;
32
33   // boolean submitted;
34   boolean gotresponse;
35
36   boolean error;
37
38   boolean waiting;
39
40   boolean gotresult;
41
42   Hashtable squniq;
43
44   /**
45    * dataset associated with this input data.
46    */
47   AlignmentI dsForIO;
48
49   AlignmentOrder inputOrder;
50
51   /**
52    * context of input data with respect to an AlignmentView's visible contigs.
53    */
54   int[] origviscontig;
55
56   private AlignmentI contextAl=null;
57
58   /**
59    * create a rest job using data bounded by the given start/end column.
60    * 
61    * @param addJobPane
62    * @param restJobThread
63    * @param _input
64    * @param viscontigs
65    *          visible contigs of an alignment view from which _input was derived
66    */
67   public RestJob(int jobNum, RestJobThread restJobThread,
68           AlignmentI _input, int[] viscontigs)
69   {
70     rsd = restJobThread.restClient.service;
71     jobnum = jobNum;
72     if (viscontigs != null)
73     {
74       origviscontig = new int[viscontigs.length];
75       System.arraycopy(viscontigs, 0, origviscontig, 0, viscontigs.length);
76     }
77     // get sequences for the alignmentI
78     // get groups trimmed to alignment columns
79     // get any annotation trimmed to start/end columns, too.
80
81     // prepare input
82     // form alignment+groups+annotation,preprocess and then record references
83     // for formatters
84     ArrayList<InputType> alinp = new ArrayList<InputType>();
85     int paramsWithData = 0;
86     // we cheat for moment - since we know a-priori what data is available and
87     // what inputs we have implemented so far
88     for (Map.Entry<String, InputType> prm : rsd.inputParams.entrySet())
89     {
90       if (!prm.getValue().isConstant())
91       {
92         if (prm.getValue() instanceof Alignment)
93         {
94           alinp.add(prm.getValue());
95         }
96         else
97         {
98           if ((prm.getValue() instanceof SeqGroupIndexVector)
99                   && (_input.getGroups() != null && _input.getGroups()
100                           .size() > 0))
101           {
102             alinp.add(prm.getValue());
103           }
104         }
105       }
106       else
107       {
108         paramsWithData++;
109       }
110     }
111     if ((paramsWithData + alinp.size()) == rsd.inputParams.size())
112     {
113       inputOrder = new AlignmentOrder(_input);
114       if ((dsForIO = _input.getDataset()) == null)
115       {
116         _input.setDataset(null);
117       }
118       dsForIO = _input.getDataset();
119       if (contextAl==null)
120       {
121         contextAl = _input;
122       }
123       setAlignmentForInputs(alinp, _input);
124       validInput = true;
125     }
126     else
127     {
128       // not enough data, so we bail.
129       validInput = false;
130     }
131   }
132
133   boolean validInput = false;
134
135   @Override
136   public boolean hasResults()
137   {
138     return gotresult;
139   }
140
141   @Override
142   public boolean hasValidInput()
143   {
144     return validInput;
145   }
146
147   @Override
148   public boolean isRunning()
149   {
150     return running; // TODO: can we check the response body for status messages
151                     // ?
152   }
153
154   @Override
155   public boolean isQueued()
156   {
157     return waiting;
158   }
159
160   @Override
161   public boolean isFinished()
162   {
163     return resSet != null;
164   }
165
166   @Override
167   public boolean isFailed()
168   {
169     // TODO logic for error
170     return error;
171   }
172
173   @Override
174   public boolean isBroken()
175   {
176     // TODO logic for error
177     return error;
178   }
179
180   @Override
181   public boolean isServerError()
182   {
183     // TODO logic for error
184     return error;
185   }
186
187   @Override
188   public boolean hasStatus()
189   {
190     return statMessage != null;
191   }
192
193   protected String statMessage = null;
194
195   public HttpResultSet resSet;
196
197   @Override
198   public String getStatus()
199   {
200     return statMessage;
201   }
202
203   @Override
204   public boolean hasResponse()
205   {
206     return statMessage != null || resSet != null;
207   }
208
209   @Override
210   public void clearResponse()
211   {
212     // only clear the transient server response
213     // statMessage=null;
214   }
215
216   /*
217    * (non-Javadoc)
218    * 
219    * @see jalview.ws.AWsJob#getState()
220    */
221   @Override
222   public String getState()
223   {
224     // TODO generate state string - prolly should have a default abstract method
225     // for this
226     return "Job is clueless";
227   }
228
229   public String getPostUrl()
230   {
231
232     // TODO Auto-generated method stub
233     return rsd.postUrl;
234   }
235
236   public Set<Map.Entry<String, InputType>> getInputParams()
237   {
238     return rsd.inputParams.entrySet();
239   }
240
241   // return the URL that should be polled for this job
242   public String getPollUrl()
243   {
244     return rsd.getDecoratedResultUrl(jobId);
245   }
246
247   /**
248    * 
249    * @return the context for parsing results from service
250    */
251   public JalviewDataset newJalviewDataset()
252   {
253     if (context == null)
254     {
255       context = new JalviewDataset(dsForIO, null, squniq, null);
256       if (contextAl!=null)
257       {
258         context.addAlignment(contextAl);
259       }
260       
261     }
262     return context;
263   }
264
265   /**
266    * Extract list of sequence IDs for input parameter 'token' with given
267    * molecule type
268    * 
269    * @param token
270    * @param type
271    * @return
272    */
273   public SequenceI[] getSequencesForInput(String token,
274           InputType.molType type) throws NoValidInputDataException
275   {
276     Object sgdat = inputData.get(token);
277     // can we form an alignment from this data ?
278     if (sgdat == null)
279     {
280       throw new NoValidInputDataException(
281               "No Sequence vector data bound to input '" + token
282                       + "' for service at " + rsd.postUrl);
283     }
284     if (sgdat instanceof AlignmentI)
285     {
286       return ((AlignmentI) sgdat).getSequencesArray();
287     }
288     if (sgdat instanceof SequenceGroup)
289     {
290       return ((SequenceGroup) sgdat).getSequencesAsArray(null);
291     }
292     if (sgdat instanceof Vector)
293     {
294       if (((Vector) sgdat).size() > 0
295               && ((Vector) sgdat).get(0) instanceof SequenceI)
296       {
297         SequenceI[] sq = new SequenceI[((Vector) sgdat).size()];
298         ((Vector) sgdat).copyInto(sq);
299         return sq;
300       }
301     }
302     throw new NoValidInputDataException(
303             "No Sequence vector data bound to input '" + token
304                     + "' for service at " + rsd.postUrl);
305   }
306
307   /**
308    * binding between input data (AlignmentI, SequenceGroup, NJTree) and input
309    * param names.
310    */
311   private Hashtable<String, Object> inputData = new Hashtable<String, Object>();
312
313   /**
314    * is the job fully submitted to server and apparently in progress ?
315    */
316   public boolean running = false;
317
318   /**
319    * 
320    * @param itypes
321    * @param al
322    *          - reference to object to be stored as input. Note - input data may
323    *          be modifed by formatter
324    */
325   public void setAlignmentForInputs(Collection<InputType> itypes,
326           AlignmentI al)
327   {
328     for (InputType itype : itypes)
329     {
330       if (!rsd.inputParams.values().contains(itype))
331       {
332         throw new IllegalArgumentException("InputType " + itype.getClass()
333                 + " is not valid for service at " + rsd.postUrl);
334       }
335       if (itype instanceof AlignmentProcessor)
336       {
337         ((AlignmentProcessor) itype).prepareAlignment(al);
338       }
339       // stash a reference for recall when the alignment data is formatted
340       inputData.put(itype.token, al);
341     }
342
343   }
344
345   /**
346    * 
347    * @param token
348    * @param type
349    * @return alignment object bound to the given token
350    * @throws NoValidInputDataException
351    */
352   public AlignmentI getAlignmentForInput(String token,
353           InputType.molType type) throws NoValidInputDataException
354   {
355     Object al = inputData.get(token);
356     // can we form an alignment from this data ?
357     if (al == null || !(al instanceof AlignmentI))
358     {
359       throw new NoValidInputDataException(
360               "No alignment data bound to input '" + token
361                       + "' for service at " + rsd.postUrl);
362     }
363     return (AlignmentI) al;
364   }
365
366   /**
367    * test to see if the job has data of type cl that's needed for the job to run
368    * 
369    * @param cl
370    * @return true or false
371    */
372   public boolean hasDataOfType(Class cl)
373   {
374     if (AlignmentI.class.isAssignableFrom(cl))
375     {
376       return true;
377     }
378     // TODO: add more source data types
379
380     return false;
381   }
382
383   /**
384    * context used to parse results from service
385    */
386   JalviewDataset context = null;
387
388   Object[] jvresultobj = null;
389
390   /**
391    * process the results obtained from the server into jalview datamodel objects
392    * ready to be merged/added to the users' view.
393    */
394   public void parseResultSet() throws Exception, Error
395   {
396     jvresultobj = resSet.parseResultSet();
397   }
398
399 }