package jalview.ws.slivkaws; import jalview.bin.Cache; import jalview.ws.ServiceChangeListener; import jalview.ws.WSDiscovererI; import jalview.ws.api.ServiceWithParameters; import java.io.IOException; import java.net.MalformedURLException; import java.net.URL; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Set; import java.util.Vector; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CopyOnWriteArraySet; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import compbio.data.msa.Category; import uk.ac.dundee.compbio.slivkaclient.SlivkaClient; import uk.ac.dundee.compbio.slivkaclient.SlivkaService; public class SlivkaWSDiscoverer implements WSDiscovererI { private static final String SLIVKA_HOST_URLS = "SLIVKAHOSTURLS"; private static final String COMPBIO_SLIVKA = "https://www.compbio.dundee.ac.uk/slivka/"; private static SlivkaWSDiscoverer instance = null; private List services = List.of(); private SlivkaWSDiscoverer() { } public static SlivkaWSDiscoverer getInstance() { if (instance == null) { instance = new SlivkaWSDiscoverer(); } return instance; } private Set serviceListeners = new CopyOnWriteArraySet<>(); @Override public void addServiceChangeListener(ServiceChangeListener l) { serviceListeners.add(l); } @Override public void removeServiceChangeListener(ServiceChangeListener l) { serviceListeners.remove(l); } public void notifyServiceListeners(List services) { for (var listener : serviceListeners) { listener.servicesChanged(this, services); } } private final ExecutorService executor = Executors .newSingleThreadExecutor(); private Vector> discoveryTasks = new Vector<>(); public CompletableFuture startDiscoverer() { CompletableFuture task = CompletableFuture .supplyAsync(() -> { reloadServices(); return SlivkaWSDiscoverer.this; }, executor); discoveryTasks.add(task); return task; } private List reloadServices() { Cache.log.info("Reloading Slivka services"); notifyServiceListeners(Collections.emptyList()); ArrayList instances = new ArrayList<>(); for (String url : getServiceUrls()) { SlivkaClient client = new SlivkaClient(url); List services; try { services = client.getServices(); } catch (IOException e) { e.printStackTrace(); continue; } for (SlivkaService service : services) { SlivkaWSInstance newInstance = null; for (String classifier : service.classifiers) { String[] path = classifier.split("\\s*::\\s*"); if (path.length >= 3 && path[0].toLowerCase().equals("operation") && path[1].toLowerCase().equals("analysis")) { switch (path[path.length - 1].toLowerCase()) { case "sequence alignment analysis (conservation)": newInstance = new SlivkaAnnotationServiceInstance(client, service, Category.CATEGORY_CONSERVATION); break; case "protein sequence analysis": newInstance = new SlivkaAnnotationServiceInstance(client, service, Category.CATEGORY_DISORDER); break; case "protein secondary structure prediction": newInstance = new SlivkaAnnotationServiceInstance(client, service, "Secondary Structure Prediction"); break; case "multiple sequence alignment": newInstance = new SlivkaMsaServiceInstance(client, service, Category.CATEGORY_ALIGNMENT); break; } } if (newInstance != null) break; } if (newInstance != null) instances.add(newInstance); } } services = instances; Cache.log.info("Slivka services reloading finished"); notifyServiceListeners(instances); return instances; } @Override public List getServices() { return services; } @Override public boolean hasServices() { return !isRunning() && services.size() > 0; } @Override public boolean isRunning() { return !discoveryTasks.stream().allMatch(Future::isDone); } @Override public void setServiceUrls(List wsUrls) { if (wsUrls != null && !wsUrls.isEmpty()) { Cache.setProperty(SLIVKA_HOST_URLS, String.join(",", wsUrls)); } else { Cache.removeProperty(SLIVKA_HOST_URLS); } } @Override public List getServiceUrls() { String surls = Cache.getDefault(SLIVKA_HOST_URLS, COMPBIO_SLIVKA); String[] urls = surls.split(","); ArrayList valid = new ArrayList<>(urls.length); for (String url : urls) { try { new URL(url); valid.add(url); } catch (MalformedURLException e) { Cache.log.warn("Problem whilst trying to make a URL from '" + ((url != null) ? url : "") + "'"); Cache.log.warn( "This was probably due to a malformed comma separated list" + " in the " + SLIVKA_HOST_URLS + " entry of $(HOME)/.jalview_properties)"); Cache.log.debug("Exception was ", e); } } return valid; } @Override public boolean testServiceUrl(URL url) { return getServerStatusFor(url.toString()) == STATUS_OK; } @Override public int getServerStatusFor(String url) { try { List services = new SlivkaClient(url).getServices(); return services.isEmpty() ? STATUS_NO_SERVICES : STATUS_OK; } catch (IOException e) { Cache.log.error("Slivka could not retrieve services list", e); return STATUS_INVALID; } } @Override public String getErrorMessages() { // TODO Auto-generated method stub return ""; } }