package jalview.ws2.slivka; import java.io.IOException; import java.net.MalformedURLException; import java.net.URL; import java.util.*; import java.util.concurrent.*; import jalview.bin.Cache; import jalview.ws2.*; import jalview.ws2.operations.AlignmentOperation; import jalview.ws2.operations.Operation; import uk.ac.dundee.compbio.slivkaclient.SlivkaClient; import uk.ac.dundee.compbio.slivkaclient.SlivkaService; public class SlivkaWSDiscoverer implements WebServiceDiscoverer { private static final String SLIVKA_HOST_URLS = "SLIVKSHOSTURLS"; private static final String DEFAULT_URL = "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; } @Override public List getUrls() { String surls = Cache.getDefault(SLIVKA_HOST_URLS, DEFAULT_URL); 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 '" + Objects.toString(url, "") + "'. " + "This was probably due to malformed comma-separated-list " + "in the " + SLIVKA_HOST_URLS + " entry of ${HOME}/.jalview_properties"); Cache.log.debug("Exception occurred while reading url list", e); } } return valid; } @Override public void setUrls(List wsUrls) { if (wsUrls != null && !wsUrls.isEmpty()) { Cache.setProperty(SLIVKA_HOST_URLS, String.join(",", wsUrls)); } else { Cache.removeProperty(SLIVKA_HOST_URLS); } } @Override public boolean testUrl(URL url) { return getStatusForUrl(url.toString()) == STATUS_OK; } @Override public int getStatusForUrl(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 from " + url, e); return STATUS_INVALID; } } public List getServices() { return Collections.unmodifiableList(services); } public boolean hasServices() { return !isRunning() && services.size() > 0; } public boolean isRunning() { for (Future task : discoveryTasks) { if (!task.isDone()) { return false; } } return true; } public boolean isDone() { return !isRunning() && discoveryTasks.size() > 0; } private Vector> discoveryTasks = new Vector<>(); @Override public CompletableFuture startDiscoverer() { CompletableFuture task = CompletableFuture .supplyAsync(() -> { reloadServices(); return SlivkaWSDiscoverer.this; }); discoveryTasks.add(task); return task; } private List reloadServices() { Cache.log.info("Reloading Slivka services"); fireServicesChanged(Collections.emptyList()); ArrayList allServices = new ArrayList<>(); for (String url : getUrls()) { SlivkaClient client = new SlivkaClient(url); List services; try { services = client.getServices(); } catch (IOException e) { Cache.log.error("Unable to fetch services from " + url, e); continue; } for (SlivkaService service : services) { SlivkaWebService instance = new SlivkaWebService(client, service); 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")) { Operation op = null; switch (path[path.length - 1].toLowerCase()) { case "multiple sequence alignment": op = new AlignmentOperation(instance, instance::getAlignment); } if (op != null) instance.addOperation(op); } } if (instance.operations.size() > 0) { allServices.add(instance); } } } this.services = allServices; Cache.log.info("Reloading slivka services finished"); fireServicesChanged(getServices()); return allServices; } @Override public String getErrorMessages() { return ""; } }