Merge branch 'feature/JAL-3686_slivka_client_js_update' into alpha/JAL-3066_Jalview_2...
[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.INTERRUPED, 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(service.getName(), 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     Optional<RemoteFile> logFile = files.stream()
149             .filter(f -> f.getLabel().equals("log")).findFirst();
150     boolean newContent = false;
151     if (logFile.isPresent())
152     {
153       ByteArrayOutputStream output = new ByteArrayOutputStream();
154       logFile.get().writeTo(output);
155       if (output.size() > job.getNextChunk())
156       {
157         newContent = true;
158         job.setStatus(output.toString("UTF-8"));
159         job.setnextChunk(output.size());
160       }
161     }
162     if (failedStates.contains(job.getJobState()))
163     {
164       Optional<RemoteFile> errLogFile = files.stream()
165               .filter(f -> f.getLabel().equals("error-log")).findFirst();
166       if (errLogFile.isPresent())
167       {
168         ByteArrayOutputStream output = new ByteArrayOutputStream();
169         errLogFile.get().writeTo(output);
170         if (output.size() > 0)
171         {
172           newContent = true;
173           job.setStatus(job.getStatus() + "\n" + output.toString("UTF-8"));
174         }
175       }
176     }
177     return newContent;
178   }
179
180   @Override
181   public final boolean handleSubmitError(Throwable _lex, WsJob j, WebserviceInfo wsInfo)
182   {
183     if (_lex instanceof FormValidationException)
184     {
185       FormValidationException formError = (FormValidationException) _lex;
186       String[] messages = new String[formError.getErrors().size()];
187       int i = 0;
188       for (ValidationException e : formError.getErrors())
189       {
190         messages[i++] = String.format("%s: %s,", e.getField().getName(), e.getMessage());
191       }
192       j.setState(WsJob.JobState.INVALID);
193       j.setStatus(String.join(", ", messages));
194       return true;
195     }
196     return false;
197   }
198
199   @Override
200   public final boolean handleCollectionException(Exception e, WsJob msjob, WebserviceInfo wsInfo)
201   {
202     // TODO
203     return false;
204   }
205
206   final SlivkaService getService()
207   {
208     return service;
209   }
210
211   @Override
212   public final Object getEndpoint()
213   {
214     return this;
215   }
216
217   @Override
218   public final void initParamStore(ParamManager userParameterStore)
219   {
220     if (store == null)
221     {
222       try
223       {
224         store = new SlivkaDatastore(service);
225       } catch (IOException e)
226       {
227         throw new IOError(e);
228       }
229     }
230   }
231
232   @Override
233   public boolean hasParameters()
234   {
235     return true;
236   }
237
238   @Override
239   public final ParamDatastoreI getParamStore()
240   {
241     if (store == null)
242     {
243       initParamStore(null);
244     }
245     return store;
246   }
247
248 }