package jalview.ws2.client.slivka; import java.io.IOException; import java.net.MalformedURLException; import java.net.URISyntaxException; import java.net.URL; import java.util.ArrayList; import java.util.List; import jalview.bin.Cache; import jalview.ws.params.ParamManager; import jalview.ws2.actions.alignment.AlignmentAction; import jalview.ws2.actions.annotation.AnnotationAction; import jalview.ws2.api.WebService; import jalview.ws2.client.api.AbstractWebServiceDiscoverer; import uk.ac.dundee.compbio.slivkaclient.SlivkaClient; import uk.ac.dundee.compbio.slivkaclient.SlivkaService; public class SlivkaWSDiscoverer extends AbstractWebServiceDiscoverer { private static final String SLIVKA_HOST_URLS = "SLIVKAHOSTURLS"; private static final URL DEFAULT_URL; static { try { DEFAULT_URL = new URL("https://www.compbio.dundee.ac.uk/slivka/"); } catch (MalformedURLException e) { throw new AssertionError(e); } } private static SlivkaWSDiscoverer instance = null; private static ParamManager paramManager = null; private SlivkaWSDiscoverer() { } public static SlivkaWSDiscoverer getInstance() { if (instance == null) instance = new SlivkaWSDiscoverer(); return instance; } public static void setParamManager(ParamManager manager) { paramManager = manager; } @Override public int getStatusForUrl(URL url) { try { List services = new SlivkaClient(url.toString()).getServices(); return services.isEmpty() ? STATUS_NO_SERVICES : STATUS_OK; } catch (IOException e) { Cache.log.error("slivka could not retrieve services from " + url, e); return STATUS_INVALID; } } @Override protected String getUrlsPropertyKey() { return SLIVKA_HOST_URLS; } @Override protected URL getDefaultUrl() { return DEFAULT_URL; } @Override protected List> fetchServices(URL url) throws IOException { ArrayList> allServices = new ArrayList<>(); SlivkaClient slivkaClient; try { slivkaClient = new SlivkaClient(url.toURI()); } catch (URISyntaxException e) { throw new MalformedURLException(e.getMessage()); } for (var slivkaService : slivkaClient.getServices()) { int serviceClass = getServiceClass(slivkaService); if (serviceClass == SERVICE_CLASS_MSA) { var wsb = WebService. newBuilder(); initServiceBuilder(slivkaService, wsb); wsb.category("Alignment"); wsb.interactive(false); wsb.actionClass(AlignmentAction.class); var msaService = wsb.build(); boolean canRealign = msaService.getName().contains("lustal"); var client = new SlivkaAlignmentWSClient(slivkaService); var actionBuilder = AlignmentAction.newBuilder(client); actionBuilder.name("Alignment"); actionBuilder.webService(msaService); if (canRealign) actionBuilder.subcategory("Align"); actionBuilder.minSequences(2); msaService.addAction(actionBuilder.build()); if (canRealign) { actionBuilder.name("Re-alignment"); actionBuilder.subcategory("Realign"); actionBuilder.submitGaps(true); msaService.addAction(actionBuilder.build()); } allServices.add(msaService); } else if (serviceClass == SERVICE_CLASS_PROT_SEQ_ANALYSIS) { var wsb = WebService. newBuilder(); initServiceBuilder(slivkaService, wsb); wsb.category("Protein Disorder"); wsb.interactive(false); wsb.actionClass(AnnotationAction.class); var psaService = wsb.build(); var client = new SlivkaAnnotationWSClient(slivkaService); var actionBuilder = AnnotationAction.newBuilder(client); actionBuilder.webService(psaService); actionBuilder.name("Analysis"); psaService.addAction(actionBuilder.build()); allServices.add(psaService); } else if (serviceClass == SERVICE_CLASS_CONSERVATION) { var wsb = WebService. newBuilder(); initServiceBuilder(slivkaService, wsb); wsb.category("Conservation"); wsb.interactive(true); wsb.actionClass(AnnotationAction.class); var conService = wsb.build(); var client = new SlivkaAnnotationWSClient(slivkaService); var actionBuilder = AnnotationAction.newBuilder(client); actionBuilder.webService(conService); actionBuilder.name(""); actionBuilder.alignmentAnalysis(true); actionBuilder.requireAlignedSequences(true); actionBuilder.filterSymbols(true); conService.addAction(actionBuilder.build()); allServices.add(conService); } else if (serviceClass == SERVICE_CLASS_RNA_SEC_STR_PRED) { var wsb = WebService. newBuilder(); initServiceBuilder(slivkaService, wsb); wsb.category("Secondary Structure Prediction"); wsb.interactive(true); wsb.actionClass(AnnotationAction.class); var predService = wsb.build(); var client = new SlivkaAnnotationWSClient(slivkaService); var actionBuilder = AnnotationAction.newBuilder(client); actionBuilder.webService(predService); actionBuilder.name("Prediction"); actionBuilder.minSequences(2); actionBuilder.allowNucleotide(true); actionBuilder.allowProtein(false); actionBuilder.alignmentAnalysis(true); actionBuilder.requireAlignedSequences(true); actionBuilder.filterSymbols(false); predService.addAction(actionBuilder.build()); allServices.add(predService); } else { continue; } } return allServices; } private void initServiceBuilder(SlivkaService service, WebService.Builder wsBuilder) { try { wsBuilder.url(service.getClient().getUrl().toURL()); } catch (MalformedURLException e) { e.printStackTrace(); } wsBuilder.clientName("slivka"); wsBuilder.name(service.getName()); wsBuilder.description(service.getDescription()); var storeBuilder = new SlivkaParamStoreFactory(service, paramManager); wsBuilder.paramDatastore(storeBuilder.createParamDatastore()); } static final int SERVICE_CLASS_UNSUPPORTED = -1; static final int SERVICE_CLASS_MSA = 1; static final int SERVICE_CLASS_RNA_SEC_STR_PRED = 2; static final int SERVICE_CLASS_CONSERVATION = 3; static final int SERVICE_CLASS_PROT_SEQ_ANALYSIS = 4; static final int SERVICE_CLASS_PROT_SEC_STR_PRED = 5; /** * Scan service classifiers starting with operation :: analysis to decide the * operation class. * * @return service class flag */ private static int getServiceClass(SlivkaService service) { for (String classifier : service.getClassifiers()) { String[] path = classifier.split("\\s*::\\s*"); if (path.length < 3 || !path[0].equalsIgnoreCase("operation") || !path[1].equalsIgnoreCase("analysis")) continue; // classifier is operation :: analysis :: * var tail = path[path.length - 1].toLowerCase(); switch (tail) { case "multiple sequence alignment": return SERVICE_CLASS_MSA; case "rna secondary structure prediction": return SERVICE_CLASS_RNA_SEC_STR_PRED; case "sequence alignment analysis (conservation)": return SERVICE_CLASS_CONSERVATION; case "protein sequence analysis": return SERVICE_CLASS_PROT_SEQ_ANALYSIS; case "protein secondary structure prediction": return SERVICE_CLASS_PROT_SEC_STR_PRED; } } return SERVICE_CLASS_UNSUPPORTED; } }