package jalview.ws2.slivka; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.EnumMap; import java.util.HashSet; import java.util.List; import java.util.Set; import jalview.bin.Cache; import jalview.datamodel.AlignmentI; import jalview.datamodel.SequenceI; import jalview.io.DataSourceType; import jalview.io.FileFormat; import jalview.io.FileFormatI; import jalview.io.FormatAdapter; import jalview.ws.gui.WsJob; import jalview.ws.params.ArgumentI; import jalview.ws.params.ParamDatastoreI; import jalview.ws.params.WsParamSetI; import jalview.ws.slivkaws.SlivkaDatastore; import jalview.ws2.WebServiceI; import jalview.ws2.operations.Operation; import jalview.ws2.ResultSupplier; import jalview.ws2.WSJob; import jalview.ws2.WSJobStatus; import javajs.http.ClientProtocolException; import uk.ac.dundee.compbio.slivkaclient.Job; import uk.ac.dundee.compbio.slivkaclient.Parameter; import uk.ac.dundee.compbio.slivkaclient.RemoteFile; import uk.ac.dundee.compbio.slivkaclient.SlivkaClient; import uk.ac.dundee.compbio.slivkaclient.SlivkaService; public class SlivkaWebService implements WebServiceI { protected final SlivkaClient client; protected final SlivkaService service; protected SlivkaDatastore store = null; protected final ArrayList operations = new ArrayList<>(); protected int typeFlags = 0; protected static final EnumMap statusMap = new EnumMap<>( Job.Status.class); { statusMap.put(Job.Status.PENDING, WSJobStatus.SUBMITTED); statusMap.put(Job.Status.REJECTED, WSJobStatus.INVALID); statusMap.put(Job.Status.ACCEPTED, WSJobStatus.QUEUED); statusMap.put(Job.Status.QUEUED, WSJobStatus.QUEUED); statusMap.put(Job.Status.RUNNING, WSJobStatus.RUNNING); statusMap.put(Job.Status.COMPLETED, WSJobStatus.FINISHED); statusMap.put(Job.Status.INTERRUPTED, WSJobStatus.CANCELLED); statusMap.put(Job.Status.DELETED, WSJobStatus.CANCELLED); statusMap.put(Job.Status.FAILED, WSJobStatus.FAILED); statusMap.put(Job.Status.ERROR, WSJobStatus.SERVER_ERROR); statusMap.put(Job.Status.UNKNOWN, WSJobStatus.UNKNOWN); } public SlivkaWebService(SlivkaClient client, SlivkaService service) { this.client = client; this.service = service; } @Override public String getHostName() { return client.getUrl().toString(); } @Override public String getProviderName() { return "slivka"; } @Override public String getName() { return service.getName(); } @Override public String getDescription() { return service.getDescription(); } @Override public List getOperations() { return operations; } void addOperation(Operation operation) { operations.add(operation); } void removeOperation(Operation operation) { operations.remove(operation); } @Override public boolean hasParameters() { return getParamStore().getServiceParameters().size() > 0; } @Override public ParamDatastoreI getParamStore() { if (store == null) { store = new SlivkaDatastore(service); } return store; } @Override public String submit(List sequences, List args) throws IOException { var request = new uk.ac.dundee.compbio.slivkaclient.JobRequest(); for (Parameter param : service.getParameters()) { if (param instanceof Parameter.FileParameter) { // if finds a file input, gives it sequences stream Parameter.FileParameter fileParam = (Parameter.FileParameter) param; FileFormat format; switch (fileParam.getMediaType()) { case "application/pfam": format = FileFormat.Pfam; break; case "application/stockholm": format = FileFormat.Stockholm; break; case "application/clustal": format = FileFormat.Clustal; break; case "application/fasta": default: format = FileFormat.Fasta; break; } 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 { request.addData(paramId, arg.getValue()); } } } var job = service.submitJob(request); return job.getId(); } @Override public void updateProgress(WSJob job) throws IOException { var slivkaJob = client.getJob(job.getJobId()); job.setStatus(statusMap.get(slivkaJob.getStatus())); } @Override public void cancel(WSJob job) throws IOException { Cache.log.warn("Slivka does not support job cancellation yet."); } @Override public boolean handleSubmissionError(WSJob job, Exception ex) { if (ex instanceof ClientProtocolException) { Cache.log.error("Job submission failed due to exception.", ex); return true; } return false; } @Override public boolean handleCollectionError(WSJob job, Exception ex) { // TODO Auto-generated method stub return false; } public AlignmentI getAlignment(WSJob job) throws IOException { Collection files; var slivkaJob = client.getJob(job.getJobId()); files = slivkaJob.getResults(); for (RemoteFile f : files) { if (f.getMediaType().equals("application/clustal")) { return new FormatAdapter().readFile(f.getContentUrl().toString(), DataSourceType.URL, FileFormat.Clustal); } else if (f.getMediaType().equals("application/fasta")) { return new FormatAdapter().readFile(f.getContentUrl().toString(), DataSourceType.URL, FileFormat.Fasta); } } return null; } @Override public String toString() { return String.format("SlivkaWebService[%s]", getName()); } }