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 String operation; protected final ArrayList operations = new ArrayList<>(); protected int typeFlags = 0; protected static final EnumMap stateMap = new EnumMap<>( Job.Status.class); { stateMap.put(Job.Status.PENDING, WsJob.JobState.QUEUED); stateMap.put(Job.Status.REJECTED, WsJob.JobState.INVALID); stateMap.put(Job.Status.ACCEPTED, WsJob.JobState.QUEUED); stateMap.put(Job.Status.QUEUED, WsJob.JobState.QUEUED); stateMap.put(Job.Status.RUNNING, WsJob.JobState.RUNNING); stateMap.put(Job.Status.COMPLETED, WsJob.JobState.FINISHED); stateMap.put(Job.Status.INTERRUPTED, WsJob.JobState.CANCELLED); stateMap.put(Job.Status.DELETED, WsJob.JobState.CANCELLED); stateMap.put(Job.Status.FAILED, WsJob.JobState.FAILED); stateMap.put(Job.Status.ERROR, WsJob.JobState.SERVERERROR); stateMap.put(Job.Status.UNKNOWN, WsJob.JobState.UNKNOWN); } protected final Set failedStates = new HashSet<>( Arrays.asList(WsJob.JobState.INVALID, WsJob.JobState.BROKEN, WsJob.JobState.FAILED, WsJob.JobState.SERVERERROR, WsJob.JobState.CANCELLED)); public SlivkaWebService(SlivkaClient client, SlivkaService service, String operation) { this.client = client; this.service = service; this.operation = operation; } @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 String getOperationType() { return operation; } @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 { // TODO Auto-generated method stub } @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()); } }