From 017005bd6e7ec9f844f1265c191549453dd7217a Mon Sep 17 00:00:00 2001 From: Mateusz Warowny Date: Fri, 18 Mar 2022 17:08:53 +0100 Subject: [PATCH] JAL-3878 Create ws2 client for slivka services --- src/jalview/ws2/client/slivka/SlivkaWSClient.java | 229 +++++++++++++++++++++ 1 file changed, 229 insertions(+) create mode 100644 src/jalview/ws2/client/slivka/SlivkaWSClient.java diff --git a/src/jalview/ws2/client/slivka/SlivkaWSClient.java b/src/jalview/ws2/client/slivka/SlivkaWSClient.java new file mode 100644 index 0000000..16c9d3a --- /dev/null +++ b/src/jalview/ws2/client/slivka/SlivkaWSClient.java @@ -0,0 +1,229 @@ +package jalview.ws2.client.slivka; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.util.EnumMap; +import java.util.List; +import java.util.regex.Pattern; + +import jalview.bin.Cache; +import jalview.datamodel.AlignmentI; +import jalview.datamodel.SequenceI; +import jalview.io.DataSourceType; +import jalview.io.FileFormat; +import jalview.io.FormatAdapter; +import jalview.ws.params.ArgumentI; +import jalview.ws2.api.Credentials; +import jalview.ws2.api.JobStatus; +import jalview.ws2.api.WebServiceJobHandle; +import jalview.ws2.client.api.AlignmentWebServiceClientI; +import jalview.ws2.client.api.WebServiceClientI; +import uk.ac.dundee.compbio.slivkaclient.Job; +import uk.ac.dundee.compbio.slivkaclient.Parameter; +import uk.ac.dundee.compbio.slivkaclient.SlivkaClient; +import uk.ac.dundee.compbio.slivkaclient.SlivkaService; + +public class SlivkaWSClient implements WebServiceClientI +{ + final SlivkaService service; + + final SlivkaClient client; + + SlivkaWSClient(SlivkaService service) + { + this.service = service; + this.client = service.getClient(); + } + + @Override + public String getUrl() + { + return client.getUrl().toString(); + } + + @Override + public String getClientName() + { + return "slivka"; + } + + // pattern for matching media types + static final Pattern mediaTypePattern = Pattern.compile("(?:text|application)\\/(?:x-)?(\\w+)"); + + @Override + public WebServiceJobHandle submit(List sequences, + List args, Credentials credentials) throws IOException + { + var request = new uk.ac.dundee.compbio.slivkaclient.JobRequest(); + for (Parameter param : service.getParameters()) + { + // TODO: restrict input sequences parameter name to "sequences" + if (param instanceof Parameter.FileParameter) + { + Parameter.FileParameter fileParam = (Parameter.FileParameter) param; + FileFormat format = null; + var match = mediaTypePattern.matcher(fileParam.getMediaType()); + if (match.find()) + { + String fmt = match.group(1); + if (fmt.equalsIgnoreCase("pfam")) + format = FileFormat.Pfam; + else if (fmt.equalsIgnoreCase("stockholm")) + format = FileFormat.Stockholm; + else if (fmt.equalsIgnoreCase("clustal")) + format = FileFormat.Clustal; + else if (fmt.equalsIgnoreCase("fasta")) + format = FileFormat.Fasta; + } + if (format == null) + { + Cache.log.warn(String.format( + "Unknown input format %s, assuming fasta.", + fileParam.getMediaType())); + format = FileFormat.Fasta; + } + InputStream stream = new ByteArrayInputStream(format.getWriter(null) + .print(sequences.toArray(new SequenceI[0]), false) + .getBytes()); + request.addFile(param.getId(), stream); + } + } + if (args != null) + { + for (ArgumentI arg : args) + { + // multiple choice field names are name$number to avoid duplications + // the number is stripped here + String paramId = arg.getName().split("\\$", 2)[0]; + Parameter param = service.getParameter(paramId); + if (param instanceof Parameter.FlagParameter) + { + if (arg.getValue() != null && !arg.getValue().isBlank()) + request.addData(paramId, true); + else + request.addData(paramId, false); + } + else if (param instanceof Parameter.FileParameter) + { + request.addFile(paramId, new File(arg.getValue())); + } + else + { + request.addData(paramId, arg.getValue()); + } + } + } + var job = service.submitJob(request); + return createJobHandle(job.getId()); + } + + protected WebServiceJobHandle createJobHandle(String jobId) + { + return new WebServiceJobHandle( + getClientName(), service.getName(), client.getUrl().toString(), + jobId); + } + + @Override + public JobStatus getStatus(WebServiceJobHandle job) throws IOException + { + var slivkaJob = client.getJob(job.getJobId()); + return statusMap.getOrDefault(slivkaJob.getStatus(), JobStatus.UNKNOWN); + } + + protected static final EnumMap statusMap = new EnumMap<>(Job.Status.class); + static + { + statusMap.put(Job.Status.PENDING, JobStatus.SUBMITTED); + statusMap.put(Job.Status.REJECTED, JobStatus.INVALID); + statusMap.put(Job.Status.ACCEPTED, JobStatus.SUBMITTED); + statusMap.put(Job.Status.QUEUED, JobStatus.QUEUED); + statusMap.put(Job.Status.RUNNING, JobStatus.RUNNING); + statusMap.put(Job.Status.COMPLETED, JobStatus.COMPLETED); + statusMap.put(Job.Status.INTERRUPTED, JobStatus.CANCELLED); + statusMap.put(Job.Status.DELETED, JobStatus.CANCELLED); + statusMap.put(Job.Status.FAILED, JobStatus.FAILED); + statusMap.put(Job.Status.ERROR, JobStatus.SERVER_ERROR); + statusMap.put(Job.Status.UNKNOWN, JobStatus.UNKNOWN); + } + + @Override + public String getLog(WebServiceJobHandle job) throws IOException + { + var slivkaJob = client.getJob(job.getJobId()); + for (var f : slivkaJob.getResults()) + { + if (f.getLabel().equals("log")) + { + ByteArrayOutputStream stream = new ByteArrayOutputStream(); + f.writeTo(stream); + return stream.toString(StandardCharsets.UTF_8); + } + } + return ""; + } + + @Override + public String getErrorLog(WebServiceJobHandle job) throws IOException + { + var slivkaJob = client.getJob(job.getJobId()); + for (var f : slivkaJob.getResults()) + { + if (f.getLabel().equals("error-log")) + { + ByteArrayOutputStream stream = new ByteArrayOutputStream(); + f.writeTo(stream); + return stream.toString(StandardCharsets.UTF_8); + } + } + return ""; + } + + @Override + public void cancel(WebServiceJobHandle job) + throws IOException, UnsupportedOperationException + { + throw new UnsupportedOperationException( + "slivka client does not support job cancellation"); + } +} + +class SlivkaAlignmentWSClient extends SlivkaWSClient + implements AlignmentWebServiceClientI +{ + + SlivkaAlignmentWSClient(SlivkaService service) + { + super(service); + } + + @Override + public AlignmentI getAlignment(WebServiceJobHandle job) throws IOException + { + var slivkaJob = client.getJob(job.getJobId()); + for (var f : slivkaJob.getResults()) + { + // TODO: restrict result file label to "alignment" + FileFormat format; + var match = mediaTypePattern.matcher(f.getMediaType()); + if (!match.find()) + continue; + String fmt = match.group(1); + if (fmt.equalsIgnoreCase("clustal")) + format = FileFormat.Clustal; + else if (fmt.equalsIgnoreCase("fasta")) + format = FileFormat.Fasta; + else + continue; + return new FormatAdapter().readFile(f.getContentUrl().toString(), + DataSourceType.URL, format); + } + Cache.log.warn("No alignment found on the server"); + throw new IOException("no alignment found"); + } + +} \ No newline at end of file -- 1.7.10.2