JAL-3848 Update slivka client library.
[jalview.git] / src / jalview / ws / slivkaws / SlivkaWSDiscoverer.java
index 925cd55..accb40d 100644 (file)
@@ -1,22 +1,24 @@
 package jalview.ws.slivkaws;
 
 import jalview.bin.Cache;
-import jalview.gui.AlignFrame;
+import jalview.ws.ServiceChangeListener;
 import jalview.ws.WSDiscovererI;
 import jalview.ws.api.ServiceWithParameters;
-import jalview.ws.jws2.PreferredServiceRegistry;
-
-import java.beans.PropertyChangeListener;
-import java.beans.PropertyChangeSupport;
 import java.io.IOException;
 import java.net.MalformedURLException;
-import java.net.URISyntaxException;
 import java.net.URL;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
-
-import javax.swing.JMenu;
-
+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;
 
@@ -24,7 +26,7 @@ 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 final String COMPBIO_SLIVKA = "https://www.compbio.dundee.ac.uk/slivka/";
 
   private static SlivkaWSDiscoverer instance = null;
 
@@ -43,131 +45,104 @@ public class SlivkaWSDiscoverer implements WSDiscovererI
     return instance;
   }
 
-  private PropertyChangeSupport changeSupport = new PropertyChangeSupport(
-      this);
+  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 alignmentMenu = new JMenu("Sequence Alignment");
-    slivkaMenu.add(alignmentMenu);
-    JMenu disorderMenu = new JMenu("Protein sequence analysis");
-    slivkaMenu.add(disorderMenu);
-    JMenu conservationMenu = new JMenu("Conservation");
-    slivkaMenu.add(conservationMenu);
-    PreferredServiceRegistry.getRegistry().populateWSMenuEntry(services,
-        changeSupport, slivkaMenu, alignFrame, null);
-
+    serviceListeners.add(l);
   }
 
-  volatile boolean ready = false;
-
-  volatile Thread discovererThread = null;
-
-  private class DiscovererThread extends Thread
+  @Override
+  public void removeServiceChangeListener(ServiceChangeListener l)
   {
-    private Thread oldThread;
-
-    DiscovererThread(Thread oldThread)
-    {
-      super();
-      this.oldThread = oldThread;
-    }
+    serviceListeners.remove(l);
+  }
 
-    @Override
-    public void run()
+  public void notifyServiceListeners(List<ServiceWithParameters> services)
+  {
+    for (var listener : serviceListeners)
     {
-      if (oldThread != null)
-      {
-        oldThread.interrupt();
-        try
-        {
-          oldThread.join();
-        } catch (InterruptedException e)
-        {
-          return;
-        } finally
-        {
-          oldThread = null;
-        }
-      }
-      ready = false;
-      reloadServices();
-      ready = !isInterrupted();
+      listener.servicesChanged(this, services);
     }
   }
 
-  Thread discoverer = null;
+  private final ExecutorService executor = Executors
+          .newSingleThreadExecutor();
 
-  @Override
-  public Thread startDiscoverer(PropertyChangeListener changeListener)
+  private Vector<Future<?>> discoveryTasks = new Vector<>();
+
+  public CompletableFuture<WSDiscovererI> startDiscoverer()
   {
-    changeSupport.addPropertyChangeListener(changeListener);
-    ready = false;
-    (discovererThread = new DiscovererThread(discovererThread)).start();
-    return discovererThread;
+    CompletableFuture<WSDiscovererI> task = CompletableFuture
+            .supplyAsync(() -> {
+              reloadServices();
+              return SlivkaWSDiscoverer.this;
+            }, executor);
+    discoveryTasks.add(task);
+    return task;
   }
 
-  private void reloadServices()
+  private List<ServiceWithParameters> reloadServices()
   {
     Cache.log.info("Reloading Slivka services");
-    changeSupport.firePropertyChange("services", services, List.of());
+    notifyServiceListeners(Collections.emptyList());
     ArrayList<ServiceWithParameters> instances = new ArrayList<>();
 
     for (String url : getServiceUrls())
     {
-      Cache.log.info(url);
-      SlivkaClient client;
+      SlivkaClient client = new SlivkaClient(url);
+
+      List<SlivkaService> services;
       try
       {
-        client = new SlivkaClient(url);
-      } catch (URISyntaxException e)
+        services = client.getServices();
+      } catch (IOException e)
       {
         e.printStackTrace();
         continue;
       }
-      try
+      for (SlivkaService service : services)
       {
-        for (SlivkaService service : client.getServices())
+        SlivkaWSInstance newInstance = null;
+        for (String classifier : service.classifiers)
         {
-          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"))
           {
-            if (classifier.contains("Multiple sequence alignment"))
-            {
-              newinstance = new SlivkaMsaServiceInstance(client, service);
-            }
-            if (classifier.contains("Protein sequence analysis")
-                && newinstance == null)
+            switch (path[path.length - 1].toLowerCase())
             {
-              newinstance = new SlivkaAnnotationServiceInstance(client,
-                  service, false);
+            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 (classifier
-                .contains("Sequence alignment analysis (conservation)"))
-            {
-              newinstance = new SlivkaAnnotationServiceInstance(client,
-                  service, true);
-            }
-          }
-          if (newinstance != null)
-          {
-            instances.add(newinstance);
           }
+          if (newInstance != null)
+            break;
         }
-      } catch (IOException e)
-      {
-        continue;
+        if (newInstance != null)
+          instances.add(newInstance);
       }
     }
 
     services = instances;
-    changeSupport.firePropertyChange("services", List.of(), services);
-
     Cache.log.info("Slivka services reloading finished");
+    notifyServiceListeners(instances);
+    return instances;
   }
 
   @Override
@@ -179,14 +154,13 @@ public class SlivkaWSDiscoverer implements WSDiscovererI
   @Override
   public boolean hasServices()
   {
-    return ready == true && services.size() > 0;
+    return !isRunning() && services.size() > 0;
   }
 
   @Override
   public boolean isRunning()
   {
-    return discovererThread == null || discovererThread.isAlive()
-        || discovererThread.getState() == Thread.State.NEW;
+    return !discoveryTasks.stream().allMatch(Future::isDone);
   }
 
   @Override
@@ -217,11 +191,11 @@ public class SlivkaWSDiscoverer implements WSDiscovererI
       } catch (MalformedURLException e)
       {
         Cache.log.warn("Problem whilst trying to make a URL from '"
-            + ((url != null) ? url : "<null>") + "'");
+                + ((url != null) ? url : "<null>") + "'");
         Cache.log.warn(
-            "This was probably due to a malformed comma separated list"
-                + " in the " + SLIVKA_HOST_URLS
-                + " entry of $(HOME)/.jalview_properties)");
+                "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);
       }
     }
@@ -241,8 +215,9 @@ public class SlivkaWSDiscoverer implements WSDiscovererI
     {
       List<?> services = new SlivkaClient(url).getServices();
       return services.isEmpty() ? STATUS_NO_SERVICES : STATUS_OK;
-    } catch (IOException | URISyntaxException e)
+    } catch (IOException | org.json.JSONException e)
     {
+      Cache.log.error("Slivka could not retrieve services list", e);
       return STATUS_INVALID;
     }
   }