package jalview.ws.slivkaws;
-import jalview.datamodel.AlignmentView;
-import jalview.gui.AlignFrame;
-import jalview.ws.WSMenuEntryProviderI;
-import jalview.ws.jws2.MsaWSClient;
+import jalview.bin.Cache;
+import jalview.bin.Console;
+import jalview.ws.ServiceChangeListener;
+import jalview.ws.WSDiscovererI;
+import jalview.ws.api.ServiceWithParameters;
+import javajs.http.HttpClientFactory;
-import java.awt.event.ActionEvent;
-import java.io.IOError;
import java.io.IOException;
-import java.net.URISyntaxException;
-
-import javax.swing.JMenu;
-import javax.swing.JMenuItem;
+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 Runnable, WSMenuEntryProviderI
+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 SlivkaClient client;
- private SlivkaWSDiscoverer() {
- try
- {
- client = new SlivkaClient("gjb-www-1.cluster.lifesci.dundee.ac.uk", 3203);
- } catch (URISyntaxException e)
- {
- throw new RuntimeException(e);
- }
+ private List<ServiceWithParameters> services = List.of();
+
+ private SlivkaWSDiscoverer()
+ {
}
public static SlivkaWSDiscoverer getInstance()
{
- if (instance == null) {
- instance = new SlivkaWSDiscoverer();
- }
+ if (instance == null)
+ {
+ instance = new SlivkaWSDiscoverer();
+ }
return instance;
}
+ private Set<ServiceChangeListener> serviceListeners = new CopyOnWriteArraySet<>();
+
@Override
- public void attachWSMenuEntry(JMenu wsmenu, final AlignFrame alignFrame)
+ public void addServiceChangeListener(ServiceChangeListener l)
{
- JMenu slivkaMenu = new JMenu("Slivka");
- wsmenu.add(slivkaMenu);
- JMenu categoryMenu = new JMenu("Alignment");
- slivkaMenu.add(categoryMenu);
+ serviceListeners.add(l);
+ }
- try
+ @Override
+ public void removeServiceChangeListener(ServiceChangeListener l)
+ {
+ serviceListeners.remove(l);
+ }
+
+ public void notifyServiceListeners(List<ServiceWithParameters> services)
+ {
+ for (var listener : serviceListeners)
{
- for (SlivkaService service: client.getServices()) {
- msaClassifier: {
- for (String classifier: service.classifiers) {
- if (classifier.contains("Multiple sequence alignment")) {
- break msaClassifier;
- }
- }
- continue;
- }
- SlivkaWSInstance instance = new SlivkaWSInstance(client, service);
- JMenuItem defaultEntry = new JMenuItem(String.format("%s with defaults", service.label));
- defaultEntry.addActionListener((ActionEvent evt) -> {
- AlignmentView msa = alignFrame.gatherSequencesForAlignment();
- if (msa != null) {
- new MsaWSClient(
- instance, alignFrame.getTitle(), msa, false, true,
- alignFrame.getViewport().getAlignment().getDataset(),
- alignFrame
- );
- }
- });
- categoryMenu.add(defaultEntry);
-
- JMenuItem customEntry = new JMenuItem(String.format("%s with custom parameters", service.label));
- customEntry.addActionListener((ActionEvent evt) -> {
- AlignmentView msa = alignFrame.gatherSequencesForAlignment();
- if (msa != null) {
- try
- {
- SlivkaParamSet paramSet = new SlivkaParamSet(service);
- new MsaWSClient(
- instance, paramSet, null, true, alignFrame.getTitle(), msa, false, true,
- alignFrame.getViewport().getAlignment().getDataset(), alignFrame);
- } catch (IOException exc)
+ listener.servicesChanged(this, services);
+ }
+ }
+
+ private final ExecutorService executor = Executors
+ .newSingleThreadExecutor();
+
+ private Vector<Future<?>> discoveryTasks = new Vector<>();
+
+ public CompletableFuture<WSDiscovererI> startDiscoverer()
+ {
+ CompletableFuture<WSDiscovererI> task = CompletableFuture
+ .supplyAsync(() -> {
+ reloadServices();
+ return SlivkaWSDiscoverer.this;
+ }, executor);
+ discoveryTasks.add(task);
+ return task;
+ }
+
+ private List<ServiceWithParameters> reloadServices()
+ {
+ Console.info("Reloading Slivka services");
+ notifyServiceListeners(Collections.emptyList());
+ ArrayList<ServiceWithParameters> instances = new ArrayList<>();
+
+ for (String url : getServiceUrls())
+ {
+ SlivkaClient client = SlivkaClient.newInstance(url);
+
+ List<SlivkaService> 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())
{
- throw new IOError(exc);
+ case "rna secondary structure prediction":
+ newInstance = new RNAalifoldServiceInstance(client,
+ service, "Secondary Structure Prediction");
+ break;
+ 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;
}
}
- });
- categoryMenu.add(customEntry);
-
- categoryMenu.addSeparator();
+ if (newInstance != null)
+ break;
+ }
+ if (newInstance != null)
+ instances.add(newInstance);
}
- } catch (IOException e)
+ }
+
+ services = instances;
+ Console.info("Slivka services reloading finished");
+ notifyServiceListeners(instances);
+ return instances;
+ }
+
+ @Override
+ public List<ServiceWithParameters> 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<String> wsUrls)
+ {
+ if (wsUrls != null && !wsUrls.isEmpty())
+ {
+ Cache.setProperty(SLIVKA_HOST_URLS, String.join(",", wsUrls));
+ }
+ else
+ {
+ Cache.removeProperty(SLIVKA_HOST_URLS);
+ }
+ }
+
+ @Override
+ public List<String> getServiceUrls()
+ {
+ String surls = Cache.getDefault(SLIVKA_HOST_URLS, COMPBIO_SLIVKA);
+ String[] urls = surls.split(",");
+ ArrayList<String> valid = new ArrayList<>(urls.length);
+ for (String url : urls)
{
- // TODO Auto-generated catch block
- e.printStackTrace();
+ try
+ {
+ new URL(url);
+ valid.add(url);
+ } catch (MalformedURLException e)
+ {
+ Console.warn("Problem whilst trying to make a URL from '"
+ + ((url != null) ? url : "<null>") + "'");
+ Console.warn(
+ "This was probably due to a malformed comma separated list"
+ + " in the " + SLIVKA_HOST_URLS
+ + " entry of $(HOME)/.jalview_properties)");
+ Console.debug("Exception was ", e);
+ }
}
+ return valid;
}
@Override
- public void run()
+ public boolean testServiceUrl(URL url)
{
+ return getServerStatusFor(url.toString()) == STATUS_OK;
+ }
+ @Override
+ public int getServerStatusFor(String url)
+ {
+ try
+ {
+ List<?> services = SlivkaClient.newInstance(url).getServices();
+ return services.isEmpty() ? STATUS_NO_SERVICES : STATUS_OK;
+ } catch (IOException | org.json.JSONException e)
+ {
+ Console.error("Slivka could not retrieve services list", e);
+ return STATUS_INVALID;
+ }
+ }
+
+ @Override
+ public String getErrorMessages()
+ {
+ // TODO Auto-generated method stub
+ return "";
}
}