initial implementation of Rest client framework (JAL-715)
[jalview.git] / src / jalview / ws / rest / RestJob.java
1 package jalview.ws.rest;
2
3 import java.util.ArrayList;
4 import java.util.Collection;
5 import java.util.Hashtable;
6 import java.util.Map;
7 import java.util.Map.Entry;
8 import java.util.Set;
9 import java.util.Vector;
10
11 import jalview.datamodel.AlignmentI;
12 import jalview.datamodel.AlignmentView;
13 import jalview.datamodel.SequenceGroup;
14 import jalview.datamodel.SequenceI;
15 import jalview.io.packed.JalviewDataset;
16 import jalview.ws.AWsJob;
17 import jalview.ws.rest.params.Alignment;
18 import jalview.ws.rest.params.SeqGroupIndexVector;
19
20 public class RestJob extends AWsJob
21 {
22   
23   // TODO: input alignmentview and other data for this job
24   RestServiceDescription rsd;
25   // boolean submitted;
26   boolean gotresponse;
27   boolean error;
28   boolean waiting;
29   boolean gotresult;
30   Hashtable squniq;
31   
32   /**
33    * create a rest job using data bounded by the given start/end column. 
34    * @param addJobPane
35    * @param restJobThread
36    * @param _input
37    */
38   public RestJob(int jobNum, RestJobThread restJobThread,
39           AlignmentI _input)
40   {
41     rsd = restJobThread.restClient.service;
42     jobnum = jobNum;
43     // get sequences for the alignmentI
44     // get groups trimmed to alignment columns
45     // get any annotation trimmed to start/end columns, too.
46     
47     // prepare input
48     // form alignment+groups+annotation,preprocess and then record references for formatters
49     ArrayList<InputType> alinp = new ArrayList<InputType>();
50     int paramsWithData=0;
51     // we cheat for moment - since we know a-priori what data is available and what inputs we have implemented so far
52     for (Map.Entry<String,InputType>prm: rsd.inputParams.entrySet())
53     {
54       if (!prm.getValue().isConstant())
55       {
56         if (prm.getValue() instanceof Alignment)
57       {
58         alinp.add(prm.getValue());
59       } else
60       {
61         if ((prm.getValue() instanceof SeqGroupIndexVector)
62               &&(_input.getGroups()!=null && _input.getGroups().size()>0))
63         {
64           alinp.add(prm.getValue());
65         }
66       } 
67       } else {
68         paramsWithData++;
69       }
70     }
71     if ((paramsWithData+alinp.size())==rsd.inputParams.size())
72     {
73       setAlignmentForInputs(alinp, _input);
74       validInput=true;
75     } else {
76       // not enough data, so we bail.
77       validInput=false;
78     }
79   }
80   boolean validInput=false;
81   @Override
82   public boolean hasResults()
83   {
84     return gotresult;
85   }
86
87   @Override
88   public boolean hasValidInput()
89   {
90     return validInput;
91   }
92
93   @Override
94   public boolean isRunning()
95   {
96     return running; // TODO: can we check the response body for status messages ?
97   }
98
99   @Override
100   public boolean isQueued()
101   {
102     return waiting;
103   }
104
105   @Override
106   public boolean isFinished()
107   {
108     return resSet!=null;
109   }
110
111   @Override
112   public boolean isFailed()
113   {
114     // TODO logic for error
115     return error;
116   }
117
118   @Override
119   public boolean isBroken()
120   {
121     // TODO logic for error
122     return error;
123   }
124
125   @Override
126   public boolean isServerError()
127   {
128     // TODO logic for error
129     return error;
130   }
131
132   @Override
133   public boolean hasStatus()
134   {
135     return statMessage != null;
136   }
137
138   protected String statMessage = null;
139   public HttpResultSet resSet;
140
141   @Override
142   public String getStatus()
143   {
144     return statMessage;
145   }
146
147   @Override
148   public boolean hasResponse()
149   {
150     return statMessage!=null || resSet!=null;
151   }
152
153   @Override
154   public void clearResponse()
155   {
156     // only clear the transient server response 
157     // statMessage=null;
158   }
159
160   /* (non-Javadoc)
161    * @see jalview.ws.AWsJob#getState()
162    */
163   @Override
164   public String getState()
165   {
166     // TODO generate state string - prolly should have a default abstract method for this
167     return "Job is clueless";
168   }
169
170   public String getPostUrl()
171   {
172     
173     // TODO Auto-generated method stub
174     return rsd.postUrl;
175   }
176
177   public Set<Map.Entry<String,InputType>> getInputParams()
178   {
179     return rsd.inputParams.entrySet();
180   }
181
182   // return the URL that should be polled for this job
183   public String getPollUrl()
184   {
185     return rsd.getDecoratedResultUrl(jobId);
186   }
187
188   /**
189    * 
190    * @return new context for parsing results from service
191    */
192   public JalviewDataset newJalviewDataset()
193   {
194     /*
195      * TODO: initialise dataset with correct input context 
196      */
197     JalviewDataset njd = new JalviewDataset();
198     return njd;
199   }
200
201   /**
202    * Extract list of sequence IDs for input parameter 'token' with given molecule type
203    * @param token
204    * @param type
205    * @return
206    */
207   public SequenceI[] getSequencesForInput(String token, InputType.molType type) throws NoValidInputDataException
208   {
209     Object sgdat = inputData.get(token);
210     // can we form an alignment from this data ?
211     if (sgdat==null)
212     {
213       throw new NoValidInputDataException("No Sequence vector data bound to input '"+token+"' for service at "+rsd.postUrl);
214     }
215     if (sgdat instanceof AlignmentI)
216     {
217       return ((AlignmentI) sgdat).getSequencesArray();
218     }
219     if (sgdat instanceof SequenceGroup)
220     {
221       return ((SequenceGroup)sgdat).getSequencesAsArray(null);
222     }
223     if (sgdat instanceof Vector)
224     {
225       if (((Vector)sgdat).size()>0 && ((Vector)sgdat).get(0) instanceof SequenceI)
226       {
227         SequenceI[] sq = new SequenceI[((Vector)sgdat).size()];
228         ((Vector)sgdat).copyInto(sq);
229         return sq;
230       }
231     }
232     throw new NoValidInputDataException("No Sequence vector data bound to input '"+token+"' for service at "+rsd.postUrl);
233   }
234   /**
235    * binding between input data (AlignmentI, SequenceGroup, NJTree) and input param names.
236    */
237   private Hashtable<String,Object> inputData=new Hashtable<String,Object>();
238   /**
239    * is the job fully submitted to server and apparently in progress ?
240    */
241   public boolean running=false;
242   /**
243    * 
244    * @param itypes
245    * @param al - reference to object to be stored as input. Note - input data may be modifed by formatter
246    */
247   public void setAlignmentForInputs(Collection<InputType> itypes, AlignmentI al)
248   {
249     for (InputType itype: itypes) {
250       if (!rsd.inputParams.values().contains(itype))
251       {
252         throw new IllegalArgumentException("InputType "+itype.getClass()+
253                 " is not valid for service at "+rsd.postUrl);
254       }
255       if (itype instanceof AlignmentProcessor)
256       {
257         ((AlignmentProcessor)itype).prepareAlignment(al);
258       }
259       // stash a reference for recall when the alignment data is formatted
260       inputData.put(itype.token, al);
261     }
262     
263   }
264   /**
265    * 
266    * @param token
267    * @param type
268    * @return alignment object bound to the given token
269    * @throws NoValidInputDataException
270    */
271   public AlignmentI getAlignmentForInput(String token, InputType.molType type) throws NoValidInputDataException
272   {
273     Object al = inputData.get(token);
274     // can we form an alignment from this data ?
275     if (al==null || !(al instanceof AlignmentI))
276     {
277       throw new NoValidInputDataException("No alignment data bound to input '"+token+"' for service at "+rsd.postUrl);
278     }
279     return (AlignmentI) al;
280   }
281
282   /**
283    * test to see if the job has data of type cl that's needed for the job to run
284    * @param cl
285    * @return true or false
286    */
287   public boolean hasDataOfType(Class cl)
288   {
289     if (AlignmentI.class.isAssignableFrom(cl)) {
290       return true;
291     }
292     // TODO: add more source data types 
293  
294     return false;
295   }
296
297 }