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