1 package jalview.ws2.slivka;
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;
15 import jalview.api.AlignViewportI;
16 import jalview.bin.Cache;
17 import jalview.datamodel.Alignment;
18 import jalview.datamodel.AlignmentAnnotation;
19 import jalview.datamodel.AlignmentI;
20 import jalview.datamodel.SequenceI;
21 import jalview.io.AnnotationFile;
22 import jalview.io.DataSourceType;
23 import jalview.io.FeaturesFile;
24 import jalview.io.FileFormat;
25 import jalview.io.FileFormatI;
26 import jalview.io.FormatAdapter;
27 import jalview.ws.gui.WsJob;
28 import jalview.ws.params.ArgumentI;
29 import jalview.ws.params.ParamDatastoreI;
30 import jalview.ws.params.WsParamSetI;
31 import jalview.ws.slivkaws.SlivkaDatastore;
32 import jalview.ws2.WebServiceI;
33 import jalview.ws2.operations.Operation;
34 import jalview.ws2.ResultSupplier;
35 import jalview.ws2.WSJob;
36 import jalview.ws2.WSJobStatus;
37 import javajs.http.ClientProtocolException;
38 import uk.ac.dundee.compbio.slivkaclient.Job;
39 import uk.ac.dundee.compbio.slivkaclient.Parameter;
40 import uk.ac.dundee.compbio.slivkaclient.RemoteFile;
41 import uk.ac.dundee.compbio.slivkaclient.SlivkaClient;
42 import uk.ac.dundee.compbio.slivkaclient.SlivkaService;
44 public class SlivkaWebService implements WebServiceI
46 protected final SlivkaClient client;
48 protected final SlivkaService service;
50 protected ParamDatastoreI store;
52 protected static final EnumMap<Job.Status, WSJobStatus> statusMap = new EnumMap<>(
55 statusMap.put(Job.Status.PENDING, WSJobStatus.SUBMITTED);
56 statusMap.put(Job.Status.REJECTED, WSJobStatus.INVALID);
57 statusMap.put(Job.Status.ACCEPTED, WSJobStatus.QUEUED);
58 statusMap.put(Job.Status.QUEUED, WSJobStatus.QUEUED);
59 statusMap.put(Job.Status.RUNNING, WSJobStatus.RUNNING);
60 statusMap.put(Job.Status.COMPLETED, WSJobStatus.FINISHED);
61 statusMap.put(Job.Status.INTERRUPTED, WSJobStatus.CANCELLED);
62 statusMap.put(Job.Status.DELETED, WSJobStatus.CANCELLED);
63 statusMap.put(Job.Status.FAILED, WSJobStatus.FAILED);
64 statusMap.put(Job.Status.ERROR, WSJobStatus.SERVER_ERROR);
65 statusMap.put(Job.Status.UNKNOWN, WSJobStatus.UNKNOWN);
68 public SlivkaWebService(SlivkaClient client, SlivkaService service)
71 this.service = service;
75 public String getHostName()
77 return client.getUrl().toString();
81 public String getProviderName()
87 public String getName()
89 return service.getName();
93 public String getDescription()
95 return service.getDescription();
99 public boolean hasParameters()
101 return getParamStore().getServiceParameters().size() > 0;
105 public ParamDatastoreI getParamStore()
109 store = new SlivkaDatastore(service);
115 public String submit(List<SequenceI> sequences, List<ArgumentI> args)
118 var request = new uk.ac.dundee.compbio.slivkaclient.JobRequest();
119 for (Parameter param : service.getParameters())
121 if (param instanceof Parameter.FileParameter)
123 // if finds a file input, gives it sequences stream
124 Parameter.FileParameter fileParam = (Parameter.FileParameter) param;
126 switch (fileParam.getMediaType())
128 case "application/pfam":
129 format = FileFormat.Pfam;
131 case "application/stockholm":
132 format = FileFormat.Stockholm;
134 case "application/clustal":
135 format = FileFormat.Clustal;
137 case "application/fasta":
139 format = FileFormat.Fasta;
142 InputStream stream = new ByteArrayInputStream(format.getWriter(null)
143 .print(sequences.toArray(new SequenceI[0]), false)
145 request.addFile(param.getId(), stream);
150 for (ArgumentI arg : args)
152 // multiple choice field names are name$number to avoid duplications
153 // the number is stripped here
154 String paramId = arg.getName().split("\\$", 2)[0];
155 Parameter param = service.getParameter(paramId);
156 if (param instanceof Parameter.FlagParameter)
158 if (arg.getValue() != null && !arg.getValue().isBlank())
159 request.addData(paramId, true);
161 request.addData(paramId, false);
165 request.addData(paramId, arg.getValue());
169 var job = service.submitJob(request);
174 public void updateProgress(WSJob job) throws IOException
176 var slivkaJob = client.getJob(job.getJobId());
177 job.setStatus(statusMap.get(slivkaJob.getStatus()));
178 Collection<RemoteFile> files = slivkaJob.getResults();
179 for (RemoteFile f : files)
181 if (f.getLabel().equals("log"))
183 ByteArrayOutputStream stream = new ByteArrayOutputStream();
185 job.setLog(stream.toString("UTF-8"));
187 else if (f.getLabel().equals("error-log"))
189 ByteArrayOutputStream stream = new ByteArrayOutputStream();
191 job.setErrorLog(stream.toString("UTF-8"));
197 public void cancel(WSJob job) throws IOException
199 job.setStatus(WSJobStatus.CANCELLED);
200 Cache.log.warn("Slivka does not support job cancellation yet.");
204 public boolean handleSubmissionError(WSJob job, Exception ex)
206 if (ex instanceof ClientProtocolException)
208 Cache.log.error("Job submission failed due to exception.", ex);
215 public boolean handleCollectionError(WSJob job, Exception ex)
217 // TODO Auto-generated method stub
221 public AlignmentI getAlignment(WSJob job, List<SequenceI> dataset,
222 AlignViewportI viewport) throws IOException
224 Collection<RemoteFile> files;
225 var slivkaJob = client.getJob(job.getJobId());
226 files = slivkaJob.getResults();
227 for (RemoteFile f : files)
229 if (f.getMediaType().equals("application/clustal"))
231 return new FormatAdapter().readFile(f.getContentUrl().toString(),
232 DataSourceType.URL, FileFormat.Clustal);
234 else if (f.getMediaType().equals("application/fasta"))
236 return new FormatAdapter().readFile(f.getContentUrl().toString(),
237 DataSourceType.URL, FileFormat.Fasta);
243 public FeaturesFile getFeaturesFile(WSJob job,
244 List<SequenceI> dataset, AlignViewportI viewport) throws IOException
246 var slivkaJob = client.getJob(job.getJobId());
247 Collection<RemoteFile> files = slivkaJob.getResults();
248 for (RemoteFile f : files)
250 if (f.getMediaType().equals("application/jalview-features"))
252 return new FeaturesFile(f.getContentUrl().toString(), DataSourceType.URL);
258 public List<AlignmentAnnotation> getAnnotations(WSJob job,
259 List<SequenceI> dataset, AlignViewportI viewport) throws IOException
261 var slivkaJob = client.getJob(job.getJobId());
262 Collection<RemoteFile> files = slivkaJob.getResults();
263 for (RemoteFile f : files)
265 if (f.getMediaType().equals("application/jalview-annotations"))
267 Alignment aln = new Alignment(dataset.toArray(new SequenceI[0]));
268 AnnotationFile af = new AnnotationFile();
269 boolean valid = af.readAnnotationFileWithCalcId(aln, service.getId(),
270 f.getContentUrl().toString(), DataSourceType.URL);
273 return Arrays.asList(aln.getAlignmentAnnotation());
277 throw new IOException("Unable to read annotations from file " +
278 f.getContentUrl().toString());
286 public String toString()
288 return String.format("SlivkaWebService[%s]", getName());