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 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()) { Cache.log.info(url); SlivkaClient client; client = new SlivkaClient(url); try { for (SlivkaService service : client.getServices()) { SlivkaWSInstance newinstance = null; for (String classifier : service.classifiers) { if (classifier.contains("Multiple sequence alignment")) { newinstance = new SlivkaMsaServiceInstance(client, service); } if (classifier.contains("Protein sequence analysis") && newinstance == null) { newinstance = new SlivkaAnnotationServiceInstance(client, service, false); } if (classifier .contains("Sequence alignment analysis (conservation)")) { newinstance = new SlivkaAnnotationServiceInstance(client, service, true); } } if (newinstance != null) { instances.add(newinstance); } } } catch (IOException e) { e.printStackTrace(); continue; } } services = instances; notifyServiceListeners(instances); Cache.log.info("Slivka services reloading finished"); 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 ""; } }