bd4cb61aa55a2fe85c8251434633a24303bb935c
[jalview.git] / src / jalview / ws2 / slivka / SlivkaWebService.java
1 package jalview.ws2.slivka;
2
3 import java.io.ByteArrayInputStream;
4 import java.io.ByteArrayOutputStream;
5 import java.io.IOException;
6 import java.io.InputStream;
7 import java.util.ArrayList;
8 import java.util.Arrays;
9 import java.util.Collection;
10 import java.util.EnumMap;
11 import java.util.HashSet;
12 import java.util.List;
13 import java.util.Set;
14
15 import jalview.bin.Cache;
16 import jalview.datamodel.AlignmentI;
17 import jalview.datamodel.SequenceI;
18 import jalview.io.DataSourceType;
19 import jalview.io.FileFormat;
20 import jalview.io.FileFormatI;
21 import jalview.io.FormatAdapter;
22 import jalview.ws.gui.WsJob;
23 import jalview.ws.params.ArgumentI;
24 import jalview.ws.params.ParamDatastoreI;
25 import jalview.ws.params.WsParamSetI;
26 import jalview.ws.slivkaws.SlivkaDatastore;
27 import jalview.ws2.WebServiceI;
28 import jalview.ws2.operations.Operation;
29 import jalview.ws2.ResultSupplier;
30 import jalview.ws2.WSJob;
31 import jalview.ws2.WSJobStatus;
32 import javajs.http.ClientProtocolException;
33 import uk.ac.dundee.compbio.slivkaclient.Job;
34 import uk.ac.dundee.compbio.slivkaclient.Parameter;
35 import uk.ac.dundee.compbio.slivkaclient.RemoteFile;
36 import uk.ac.dundee.compbio.slivkaclient.SlivkaClient;
37 import uk.ac.dundee.compbio.slivkaclient.SlivkaService;
38
39 public class SlivkaWebService implements WebServiceI
40 {
41   protected final SlivkaClient client;
42
43   protected final SlivkaService service;
44
45   protected SlivkaDatastore store = null;
46
47   protected final ArrayList<Operation> operations = new ArrayList<>();
48
49   protected int typeFlags = 0;
50
51   protected static final EnumMap<Job.Status, WSJobStatus> statusMap = new EnumMap<>(
52           Job.Status.class);
53   {
54     statusMap.put(Job.Status.PENDING, WSJobStatus.SUBMITTED);
55     statusMap.put(Job.Status.REJECTED, WSJobStatus.INVALID);
56     statusMap.put(Job.Status.ACCEPTED, WSJobStatus.QUEUED);
57     statusMap.put(Job.Status.QUEUED, WSJobStatus.QUEUED);
58     statusMap.put(Job.Status.RUNNING, WSJobStatus.RUNNING);
59     statusMap.put(Job.Status.COMPLETED, WSJobStatus.FINISHED);
60     statusMap.put(Job.Status.INTERRUPTED, WSJobStatus.CANCELLED);
61     statusMap.put(Job.Status.DELETED, WSJobStatus.CANCELLED);
62     statusMap.put(Job.Status.FAILED, WSJobStatus.FAILED);
63     statusMap.put(Job.Status.ERROR, WSJobStatus.SERVER_ERROR);
64     statusMap.put(Job.Status.UNKNOWN, WSJobStatus.UNKNOWN);
65   }
66
67   public SlivkaWebService(SlivkaClient client, SlivkaService service)
68   {
69     this.client = client;
70     this.service = service;
71   }
72
73   @Override
74   public String getHostName()
75   {
76     return client.getUrl().toString();
77   }
78
79   @Override
80   public String getProviderName()
81   {
82     return "slivka";
83   }
84
85   @Override
86   public String getName()
87   {
88     return service.getName();
89   }
90
91   @Override
92   public String getDescription()
93   {
94     return service.getDescription();
95   }
96
97   @Override
98   public List<Operation> getOperations()
99   {
100     return operations;
101   }
102
103   void addOperation(Operation operation)
104   {
105     operations.add(operation);
106   }
107
108   void removeOperation(Operation operation)
109   {
110     operations.remove(operation);
111   }
112
113   @Override
114   public boolean hasParameters()
115   {
116     return getParamStore().getServiceParameters().size() > 0;
117   }
118
119   @Override
120   public ParamDatastoreI getParamStore()
121   {
122     if (store == null)
123     {
124       store = new SlivkaDatastore(service);
125     }
126     return store;
127   }
128
129   @Override
130   public String submit(List<SequenceI> sequences, List<ArgumentI> args)
131           throws IOException
132   {
133     var request = new uk.ac.dundee.compbio.slivkaclient.JobRequest();
134     for (Parameter param : service.getParameters())
135     {
136       if (param instanceof Parameter.FileParameter)
137       {
138         // if finds a file input, gives it sequences stream
139         Parameter.FileParameter fileParam = (Parameter.FileParameter) param;
140         FileFormat format;
141         switch (fileParam.getMediaType())
142         {
143         case "application/pfam":
144           format = FileFormat.Pfam;
145           break;
146         case "application/stockholm":
147           format = FileFormat.Stockholm;
148           break;
149         case "application/clustal":
150           format = FileFormat.Clustal;
151           break;
152         case "application/fasta":
153         default:
154           format = FileFormat.Fasta;
155           break;
156         }
157         InputStream stream = new ByteArrayInputStream(format.getWriter(null)
158                 .print(sequences.toArray(new SequenceI[0]), false)
159                 .getBytes());
160         request.addFile(param.getId(), stream);
161       }
162     }
163     if (args != null)
164     {
165       for (ArgumentI arg : args)
166       {
167         // multiple choice field names are name$number to avoid duplications
168         // the number is stripped here
169         String paramId = arg.getName().split("\\$", 2)[0];
170         Parameter param = service.getParameter(paramId);
171         if (param instanceof Parameter.FlagParameter)
172         {
173           if (arg.getValue() != null && !arg.getValue().isBlank())
174             request.addData(paramId, true);
175           else
176             request.addData(paramId, false);
177         }
178         else
179         {
180           request.addData(paramId, arg.getValue());
181         }
182       }
183     }
184     var job = service.submitJob(request);
185     return job.getId();
186   }
187
188   @Override
189   public void updateProgress(WSJob job) throws IOException
190   {
191     var slivkaJob = client.getJob(job.getJobId());
192     job.setStatus(statusMap.get(slivkaJob.getStatus()));
193     Collection<RemoteFile> files = slivkaJob.getResults();
194     for (RemoteFile f : files)
195     {
196       if (f.getLabel().equals("log"))
197       {
198         ByteArrayOutputStream stream = new ByteArrayOutputStream();
199         f.writeTo(stream);
200         job.setLog(stream.toString("UTF-8"));
201       }
202       else if (f.getLabel().equals("error-log"))
203       {
204         ByteArrayOutputStream stream = new ByteArrayOutputStream();
205         f.writeTo(stream);
206         job.setErrorLog(stream.toString("UTF-8"));
207       }
208     }
209   }
210
211   @Override
212   public void cancel(WSJob job) throws IOException
213   {
214     Cache.log.warn("Slivka does not support job cancellation yet.");
215   }
216
217   @Override
218   public boolean handleSubmissionError(WSJob job, Exception ex)
219   {
220     if (ex instanceof ClientProtocolException)
221     {
222       Cache.log.error("Job submission failed due to exception.", ex);
223       return true;
224     }
225     return false;
226   }
227
228   @Override
229   public boolean handleCollectionError(WSJob job, Exception ex)
230   {
231     // TODO Auto-generated method stub
232     return false;
233   }
234
235   public AlignmentI getAlignment(WSJob job) throws IOException
236   {
237     Collection<RemoteFile> files;
238     var slivkaJob = client.getJob(job.getJobId());
239     files = slivkaJob.getResults();
240     for (RemoteFile f : files)
241     {
242       if (f.getMediaType().equals("application/clustal"))
243       {
244         return new FormatAdapter().readFile(f.getContentUrl().toString(),
245                 DataSourceType.URL, FileFormat.Clustal);
246       }
247       else if (f.getMediaType().equals("application/fasta"))
248       {
249         return new FormatAdapter().readFile(f.getContentUrl().toString(),
250                 DataSourceType.URL, FileFormat.Fasta);
251       }
252     }
253     return null;
254   }
255
256   @Override
257   public String toString()
258   {
259     return String.format("SlivkaWebService[%s]", getName());
260   }
261 }