75e5722ac253cb9f50ed1870d4ea1c52d6a1ea80
[jalview.git] / src / jalview / ws / slivkaws / SlivkaWSInstance.java
1 package jalview.ws.slivkaws;
2
3 import jalview.datamodel.SequenceI;
4 import jalview.gui.WebserviceInfo;
5 import jalview.io.FileFormat;
6 import jalview.io.FormatAdapter;
7 import jalview.ws.api.JalviewServiceEndpointProviderI;
8 import jalview.ws.api.JalviewWebServiceI;
9 import jalview.ws.api.JobId;
10 import jalview.ws.api.ServiceWithParameters;
11 import jalview.ws.gui.WsJob;
12 import jalview.ws.params.ArgumentI;
13 import jalview.ws.params.ParamDatastoreI;
14 import jalview.ws.params.ParamManager;
15 import jalview.ws.params.WsParamSetI;
16
17 import java.io.ByteArrayInputStream;
18 import java.io.ByteArrayOutputStream;
19 import java.io.IOError;
20 import java.io.IOException;
21 import java.io.InputStream;
22 import java.util.Arrays;
23 import java.util.EnumMap;
24 import java.util.HashSet;
25 import java.util.List;
26 import java.util.Optional;
27 import java.util.Set;
28
29 import uk.ac.dundee.compbio.slivkaclient.FieldType;
30 import uk.ac.dundee.compbio.slivkaclient.FileField;
31 import uk.ac.dundee.compbio.slivkaclient.FormField;
32 import uk.ac.dundee.compbio.slivkaclient.FormValidationException;
33 import uk.ac.dundee.compbio.slivkaclient.JobState;
34 import uk.ac.dundee.compbio.slivkaclient.RemoteFile;
35 import uk.ac.dundee.compbio.slivkaclient.SlivkaClient;
36 import uk.ac.dundee.compbio.slivkaclient.SlivkaForm;
37 import uk.ac.dundee.compbio.slivkaclient.SlivkaService;
38 import uk.ac.dundee.compbio.slivkaclient.ValidationException;
39
40 public abstract class SlivkaWSInstance extends ServiceWithParameters
41     implements JalviewServiceEndpointProviderI, JalviewWebServiceI
42 {
43   protected final SlivkaClient client;
44
45   protected final SlivkaService service;
46
47   protected SlivkaDatastore store = null;
48
49   protected static final EnumMap<JobState, WsJob.JobState> stateMap = new EnumMap<>(JobState.class);
50   {
51     stateMap.put(JobState.PENDING, WsJob.JobState.QUEUED);
52     stateMap.put(JobState.REJECTED, WsJob.JobState.INVALID);
53     stateMap.put(JobState.ACCEPTED, WsJob.JobState.QUEUED);
54     stateMap.put(JobState.QUEUED, WsJob.JobState.QUEUED);
55     stateMap.put(JobState.RUNNING, WsJob.JobState.RUNNING);
56     stateMap.put(JobState.COMPLETED, WsJob.JobState.FINISHED);
57     stateMap.put(JobState.INTERRUPTED, WsJob.JobState.CANCELLED);
58     stateMap.put(JobState.DELETED, WsJob.JobState.CANCELLED);
59     stateMap.put(JobState.FAILED, WsJob.JobState.FAILED);
60     stateMap.put(JobState.ERROR, WsJob.JobState.SERVERERROR);
61     stateMap.put(JobState.UNKNOWN, WsJob.JobState.UNKNOWN);
62   }
63   protected final Set<WsJob.JobState> failedStates = new HashSet<>(Arrays.asList(
64       WsJob.JobState.INVALID, WsJob.JobState.BROKEN, WsJob.JobState.FAILED,
65       WsJob.JobState.SERVERERROR, WsJob.JobState.CANCELLED
66   ));
67
68   public SlivkaWSInstance(SlivkaClient client, SlivkaService service, String action)
69   {
70     super(action, action, service.getLabel(), "Slivka", client.getUrl().toString());
71     this.client = client;
72     this.service = service;
73   }
74
75   protected final JobId submit(List<SequenceI> sequences,
76           WsParamSetI preset, List<ArgumentI> args) throws Throwable
77   {
78     SlivkaForm form = service.getForm();
79     for (FormField field : form.getFields())
80     {
81       if (field.getType() == FieldType.FILE)
82       {
83         FormatAdapter fa = new FormatAdapter();
84         fa.setNewlineString("\r\n");
85         FileField fileField = (FileField) field;
86         FileFormat format;
87         switch (fileField.getMediaType())
88         {
89         case "application/pfam":
90           format = FileFormat.Pfam;
91           break;
92         case "application/stockholm":
93           format = FileFormat.Stockholm;
94           break;
95         default:
96         case "application/fasta":
97           format = FileFormat.Fasta;
98           break;
99         }
100         InputStream stream = new ByteArrayInputStream(
101             fa.formatSequences(format, sequences.toArray(new SequenceI[0]))
102                 .getBytes());
103         RemoteFile rf = client.uploadFile(stream, "input",
104             fileField.getMediaType());
105         form.insert(field.getName(), rf);
106       }
107     }
108     if (args != null)
109     {
110       for (ArgumentI arg : args)
111       {
112         // multiple choice field names are name$number to avoid duplications
113         // the number is stripped here
114         String fieldName = arg.getName().split("\\$", 2)[0];
115         FormField field = form.getField(fieldName);
116         if (field.getType() == FieldType.BOOLEAN)
117         {
118           form.insert(fieldName,
119                   (arg.getValue() != null && !arg.getValue().isBlank())
120                           ? true
121                           : false);
122         }
123         else
124         {
125           form.insert(fieldName, arg.getValue());
126         }
127       }
128     }
129     return new JobId(service.getName(), service.getName(), form.submit());
130   }
131
132   @Override
133   public final void updateStatus(WsJob job)
134   {
135     try
136     {
137       job.setState(stateMap.get(client.getJobState(job.getJobId())));
138     } catch (IOException e)
139     {
140       throw new IOError(e);
141     }
142   }
143
144   @Override
145   public final boolean updateJobProgress(WsJob job) throws IOException
146   {
147     List<RemoteFile> files = client.getJobResults(job.getJobId());
148     RemoteFile logFile=null;
149     for (RemoteFile f : files)
150     {
151       if (f.getLabel().equals("log"))
152       {
153         logFile = f; break;
154       }
155     }
156
157     boolean newContent = false;
158     if (logFile!=null)
159     {
160       ByteArrayOutputStream output = new ByteArrayOutputStream();
161       logFile.writeTo(output);
162       if (output.size() > job.getNextChunk())
163       {
164         newContent = true;
165         job.setStatus(output.toString("UTF-8"));
166         job.setnextChunk(output.size());
167       }
168     }
169     if (failedStates.contains(job.getJobState()))
170     {
171       
172       RemoteFile errLogFile = null;
173       for (RemoteFile f : files)
174       {
175         if (f.getLabel().equals("error-log"))
176         {
177           errLogFile = f;
178           break;
179         }
180       }
181
182       if (errLogFile!=null)
183       {
184         ByteArrayOutputStream output = new ByteArrayOutputStream();
185         errLogFile.writeTo(output);
186         if (output.size() > 0)
187         {
188           newContent = true;
189           job.setStatus(job.getStatus() + "\n" + output.toString("UTF-8"));
190         }
191       }
192     }
193     return newContent;
194   }
195
196   @Override
197   public final boolean handleSubmitError(Throwable _lex, WsJob j, WebserviceInfo wsInfo)
198   {
199     if (_lex instanceof FormValidationException)
200     {
201       FormValidationException formError = (FormValidationException) _lex;
202       String[] messages = new String[formError.getErrors().size()];
203       int i = 0;
204       for (ValidationException e : formError.getErrors())
205       {
206         messages[i++] = String.format("%s: %s,", e.getField().getName(), e.getMessage());
207       }
208       j.setState(WsJob.JobState.INVALID);
209       j.setStatus(String.join(", ", messages));
210       return true;
211     }
212     return false;
213   }
214
215   @Override
216   public final boolean handleCollectionException(Exception e, WsJob msjob, WebserviceInfo wsInfo)
217   {
218     // TODO
219     return false;
220   }
221
222   final SlivkaService getService()
223   {
224     return service;
225   }
226
227   @Override
228   public final Object getEndpoint()
229   {
230     return this;
231   }
232
233   @Override
234   public final void initParamStore(ParamManager userParameterStore)
235   {
236     if (store == null)
237     {
238       try
239       {
240         store = new SlivkaDatastore(service);
241       } catch (IOException e)
242       {
243         throw new IOError(e);
244       }
245     }
246   }
247
248   @Override
249   public boolean hasParameters()
250   {
251     return true;
252   }
253
254   @Override
255   public final ParamDatastoreI getParamStore()
256   {
257     if (store == null)
258     {
259       initParamStore(null);
260     }
261     return store;
262   }
263
264 }