JAL-3690 refactoring web-services discovery
[jalview.git] / src / jalview / ws / slivkaws / SlivkaWSDiscoverer.java
index 6b3856a..0e66c28 100644 (file)
@@ -1,37 +1,39 @@
 package jalview.ws.slivkaws;
 
-import jalview.datamodel.AlignmentView;
-import jalview.gui.AlignFrame;
-import jalview.ws.WSMenuEntryProviderI;
-import jalview.ws.jws2.MsaWSClient;
-import jalview.ws.jws2.SequenceAnnotationWSClient;
-
-import java.awt.event.ActionEvent;
-import java.io.IOError;
+import jalview.bin.Cache;
+import jalview.ws.ServiceChangeListener;
+import jalview.ws.WSDiscovererI;
+import jalview.ws.api.ServiceWithParameters;
+import java.beans.PropertyChangeListener;
 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.CopyOnWriteArraySet;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.FutureTask;
 
 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 List<ServiceWithParameters> services = List.of();
 
   private SlivkaWSDiscoverer()
   {
-    try
-    {
-      client = new SlivkaClient("gjb-www-1.cluster.lifesci.dundee.ac.uk", 3203);
-    } catch (URISyntaxException e)
-    {
-      throw new RuntimeException(e);
-    }
   }
 
   public static SlivkaWSDiscoverer getInstance()
@@ -43,109 +45,169 @@ public class SlivkaWSDiscoverer implements Runnable, WSMenuEntryProviderI
     return instance;
   }
 
+  private Set<ServiceChangeListener> serviceListeners = new CopyOnWriteArraySet<>();
+  
   @Override
-  public void attachWSMenuEntry(JMenu wsmenu, final AlignFrame alignFrame)
+  public void addServiceChangeListener(ServiceChangeListener l) {
+    serviceListeners.add(l);
+  }
+  
+  @Override
+  public void removeServiceChangeListener(ServiceChangeListener l) {
+    serviceListeners.remove(l);
+  }
+  
+  public void notifyServiceListeners(List<ServiceWithParameters> services) {
+    for (var listener : serviceListeners) {
+      listener.servicesChanged(this, services);
+    }
+  }
+
+  private final ExecutorService executor = Executors.newSingleThreadExecutor();
+  private Vector<Future<?>> discoveryTasks = new Vector<>();
+
+  public Future<WSDiscovererI> startDiscoverer()
+  {
+    FutureTask<WSDiscovererI> task = new FutureTask<>(this::reloadServices, this);
+    discoveryTasks.add(task);
+    executor.execute(task);
+    return task;
+  }
+
+  private List<ServiceWithParameters> reloadServices()
   {
-    JMenu slivkaMenu = new JMenu("Slivka");
-    wsmenu.add(slivkaMenu);
+    Cache.log.info("Reloading Slivka services");
+    notifyServiceListeners(Collections.emptyList());
+    ArrayList<ServiceWithParameters> instances = new ArrayList<>();
 
-    JMenu alignmentMenu = new JMenu("Sequence Alignment");
-    slivkaMenu.add(alignmentMenu);
-    try
+    for (String url : getServiceUrls())
     {
-      for (SlivkaService service : client.getServices())
+      Cache.log.info(url);
+      SlivkaClient client;
+      client = new SlivkaClient(url);
+      try
       {
-        msaClassifier:
+        for (SlivkaService service : client.getServices())
         {
+          SlivkaWSInstance newinstance = null;
           for (String classifier : service.classifiers)
           {
             if (classifier.contains("Multiple sequence alignment"))
             {
-              break msaClassifier;
+              newinstance = new SlivkaMsaServiceInstance(client, service);
             }
-          }
-          continue;
-        }
-        SlivkaMsaServiceInstance instance = new SlivkaMsaServiceInstance(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);
-          }
-        });
-        alignmentMenu.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
+            if (classifier.contains("Protein sequence analysis")
+                && newinstance == null)
             {
-              SlivkaParamSet paramSet = new SlivkaParamSet(service);
-              new MsaWSClient(instance, paramSet, null, true, alignFrame.getTitle(), msa, false, true,
-                  alignFrame.getViewport().getAlignment().getDataset(), alignFrame);
-            } catch (IOException exc)
+              newinstance = new SlivkaAnnotationServiceInstance(client,
+                  service, false);
+            }
+            if (classifier
+                .contains("Sequence alignment analysis (conservation)"))
             {
-              throw new IOError(exc);
+              newinstance = new SlivkaAnnotationServiceInstance(client,
+                  service, true);
             }
           }
-        });
-        alignmentMenu.add(customEntry);
-
-        alignmentMenu.addSeparator();
+          if (newinstance != null)
+          {
+            instances.add(newinstance);
+          }
+        }
+      } catch (IOException e)
+      {
+        e.printStackTrace();
+        continue;
       }
-    } catch (IOException e)
+    }
+
+    services = instances;
+    notifyServiceListeners(instances);
+    Cache.log.info("Slivka services reloading finished");
+    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
     {
-      // TODO Auto-generated catch block
-      e.printStackTrace();
+      Cache.removeProperty(SLIVKA_HOST_URLS);
     }
+  }
 
-    JMenu disorderMenu = new JMenu("Protein sequence analysis");
-    slivkaMenu.add(disorderMenu);
-    try
+  @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)
     {
-      for (SlivkaService service : client.getServices())
+      try
       {
-        msaClassifier:
-        {
-          for (String classifier : service.classifiers)
-          {
-            if (classifier.contains("Protein sequence analysis"))
-            {
-              break msaClassifier;
-            }
-          }
-          continue;
-        }
-        JMenuItem menuEntry = new JMenuItem(String.format("%s with custom parameters", service.label));
-        disorderMenu.add(menuEntry);
-        SlivkaAnnotationServiceInstance serviceInstance = new SlivkaAnnotationServiceInstance(client, service);
-        menuEntry.addActionListener((ActionEvent evt) -> {
-          try
-          {
-            SlivkaParamSet paramSet = new SlivkaParamSet(service);
-            SequenceAnnotationWSClient client = new SequenceAnnotationWSClient();
-            client.initSequenceAnnotationWSClient(serviceInstance, alignFrame, paramSet, true);
-          } catch (IOException e)
-          {
-            throw new IOError(e);
-          }
-
-        });
+        new URL(url);
+        valid.add(url);
+      } catch (MalformedURLException e)
+      {
+        Cache.log.warn("Problem whilst trying to make a URL from '"
+            + ((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)");
+        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)
     {
-      throw new IOError(e);
+      Cache.log.error("Slivka could not retrieve services list", e);
+      return STATUS_INVALID;
     }
   }
 
   @Override
-  public void run()
+  public String getErrorMessages()
   {
-
+    // TODO Auto-generated method stub
+    return "";
   }
 }