JAL-3515 Extract core WSDiscoverer methods to a separate interface
authorMMWarowny <mmzwarowny@dundee.ac.uk>
Tue, 4 Feb 2020 11:26:36 +0000 (11:26 +0000)
committerMMWarowny <mmzwarowny@dundee.ac.uk>
Tue, 4 Feb 2020 11:27:24 +0000 (11:27 +0000)
Creating WSDiscovererI interface helps to separating exposed methods from internal implementation details of Jws2Discoverer and implements the same functionality in SlivkaWSDiscoverer

src/jalview/gui/AlignFrame.java
src/jalview/gui/Desktop.java
src/jalview/gui/WsPreferences.java
src/jalview/ws/WSDiscovererI.java [new file with mode: 0644]
src/jalview/ws/jws2/Jws2Discoverer.java
src/jalview/ws/slivkaws/SlivkaWSDiscoverer.java
test/jalview/ws/jabaws/AAConAnnotAndSettingsIO.java
test/jalview/ws/jabaws/DisorderAnnotExportImport.java

index e66b8d1..9647e0f 100644 (file)
@@ -102,6 +102,7 @@ import jalview.viewmodel.AlignmentViewport;
 import jalview.viewmodel.ViewportRanges;
 import jalview.ws.DBRefFetcher;
 import jalview.ws.DBRefFetcher.FetchFinishedListenerI;
+import jalview.ws.WSDiscovererI;
 import jalview.ws.api.ServiceWithParameters;
 import jalview.ws.jws1.Discoverer;
 import jalview.ws.jws2.Jws2Discoverer;
@@ -4414,9 +4415,10 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
                 }
                 // TODO: move into separate menu builder class.
                 boolean new_sspred = false;
+
                 if (Cache.getDefault("SHOW_JWS2_SERVICES", true))
                 {
-                  Jws2Discoverer jws2servs = Jws2Discoverer.getDiscoverer();
+                  WSDiscovererI jws2servs = Jws2Discoverer.getDiscoverer();
                   if (jws2servs != null)
                   {
                     if (jws2servs.hasServices())
@@ -4443,30 +4445,28 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
                     }
                   }
                 }
-                build_urlServiceMenu(me.webService);
-
-
-                // TODO Mateusz - follow pattern for adding web service
-                // JMenuItems for slivka-based services
 
-                SlivkaWSDiscoverer slivkaDiscoverer = SlivkaWSDiscoverer.getInstance();
-                if (slivkaDiscoverer.hasServices())
+                if (Cache.getDefault("SHOW_SLIVKA_SERVICES", true))
                 {
-                slivkaDiscoverer.attachWSMenuEntry(webService, me);
-                } else {
-                  if (slivkaDiscoverer.isRunning())
+                  WSDiscovererI discoverer = SlivkaWSDiscoverer
+                          .getInstance();
+                  if (discoverer != null)
                   {
+                    if (discoverer.hasServices())
+                    {
+                      discoverer.attachWSMenuEntry(webService, me);
+                    }
+                    if (discoverer.isRunning())
                     {
                       JMenuItem tm = new JMenuItem(
                               "Still discovering Slivka Services");
                       tm.setEnabled(false);
                       webService.add(tm);
                     }
-
                   }
                 }
-              
 
+                build_urlServiceMenu(me.webService);
                 build_fetchdbmenu(webService);
                 for (JMenu item : wsmenu)
                 {
index 84e6aed..2402aef 100644 (file)
@@ -45,6 +45,7 @@ import jalview.util.MessageManager;
 import jalview.util.Platform;
 import jalview.util.UrlConstants;
 import jalview.viewmodel.AlignmentViewport;
+import jalview.ws.WSDiscovererI;
 import jalview.ws.params.ParamManager;
 import jalview.ws.utils.UrlDownloadClient;
 
@@ -2534,7 +2535,7 @@ public class Desktop extends jalview.jbgui.GDesktop
   public void startServiceDiscovery(boolean blocking)
   {
     boolean alive = true;
-    Thread t0 = null, t1 = null, t2 = null;
+    Thread t0 = null, t1 = null, t2 = null, t3 = null;
     // JAL-940 - JALVIEW 1 services are now being EOLed as of JABA 2.1 release
     if (true)
     {
@@ -2552,14 +2553,14 @@ public class Desktop extends jalview.jbgui.GDesktop
 
     if (Cache.getDefault("SHOW_JWS2_SERVICES", true))
     {
-      t2 = jalview.ws.jws2.Jws2Discoverer.getDiscoverer()
-              .startDiscoverer(changeSupport);
+      t2 = startServiceDiscovery(
+          jalview.ws.jws2.Jws2Discoverer.getDiscoverer(), false);
     }
-    Thread t3 = null;
+    if (Cache.getDefault("SHOW_SLIVKA_SERVICES", true))
     {
       // start slivka discovery
-      t3 = new Thread(jalview.ws.slivkaws.SlivkaWSDiscoverer.getInstance());
-      t3.start();
+      t3 = startServiceDiscovery(
+          jalview.ws.slivkaws.SlivkaWSDiscoverer.getInstance(), false);
     }
     if (blocking)
     {
@@ -2571,13 +2572,31 @@ public class Desktop extends jalview.jbgui.GDesktop
         } catch (Exception e)
         {
         }
+        // FIXME: Condition should check the discoverer's isRunning rather than
+        // threads
         alive = (t1 != null && t1.isAlive()) || (t2 != null && t2.isAlive())
-                || (t3 != null && t3.isAlive())
-                || (t0 != null && t0.isAlive());
+            || (t3 != null && t3.isAlive()) || (t0 != null && t0.isAlive());
       }
     }
   }
 
+  public Thread startServiceDiscovery(WSDiscovererI discoverer,
+      boolean blocking)
+  {
+    Thread thread = discoverer.startDiscoverer(changeSupport);
+    if (blocking)
+    {
+      try
+      {
+        thread.join();
+      } catch (InterruptedException e)
+      {
+        e.printStackTrace();
+      }
+    }
+    return thread;
+  }
+
   /**
    * called to check if the service discovery process completed successfully.
    * 
@@ -2587,8 +2606,9 @@ public class Desktop extends jalview.jbgui.GDesktop
   {
     if (evt.getNewValue() == null || evt.getNewValue() instanceof Vector)
     {
-      final String ermsg = jalview.ws.jws2.Jws2Discoverer.getDiscoverer()
-              .getErrorMessages();
+      final WSDiscovererI discoverer = jalview.ws.jws2.Jws2Discoverer
+          .getDiscoverer();
+      final String ermsg = discoverer.getErrorMessages();
       if (ermsg != null)
       {
         if (Cache.getDefault("SHOW_WSDISCOVERY_ERRORS", true))
index 5186a26..51b40dd 100644 (file)
@@ -23,6 +23,7 @@ package jalview.gui;
 import jalview.bin.Cache;
 import jalview.jbgui.GWsPreferences;
 import jalview.util.MessageManager;
+import jalview.ws.WSDiscovererI;
 import jalview.ws.jws2.Jws2Discoverer;
 import jalview.ws.rest.RestServiceDescription;
 
@@ -33,11 +34,11 @@ import java.awt.Dimension;
 import java.awt.event.ActionEvent;
 import java.awt.event.ActionListener;
 import java.net.URL;
+import java.util.ArrayList;
 import java.util.List;
 import java.util.Vector;
 
 import javax.swing.JLabel;
-import javax.swing.JOptionPane;
 import javax.swing.JPanel;
 import javax.swing.JTable;
 import javax.swing.JTextField;
@@ -156,21 +157,22 @@ public class WsPreferences extends GWsPreferences
       String t = new String("");
       switch (((Integer) status).intValue())
       {
-      case 1:
+      case WSDiscovererI.STATUS_OK:
         // cb.setSelected(true);
         // cb.setBackground(
         c = Color.green;
         break;
-      case 0:
+      case WSDiscovererI.STATUS_NO_SERVICES:
         // cb.setSelected(true);
         // cb.setBackground(
         c = Color.lightGray;
         break;
-      case -1:
+      case WSDiscovererI.STATUS_INVALID:
         // cb.setSelected(false);
         // cb.setBackground(
         c = Color.red;
         break;
+      case WSDiscovererI.STATUS_UNKNOWN:
       default:
         // cb.setSelected(false);
         // cb.setBackground(
@@ -485,7 +487,7 @@ public class WsPreferences extends GWsPreferences
 
       if (validate == JvOptionPane.OK_OPTION)
       {
-        if (Jws2Discoverer.testServiceUrl(foo))
+        if (Jws2Discoverer.getDiscoverer().testServiceUrl(foo))
         {
           return foo.toString();
         }
diff --git a/src/jalview/ws/WSDiscovererI.java b/src/jalview/ws/WSDiscovererI.java
new file mode 100644 (file)
index 0000000..e5b94f0
--- /dev/null
@@ -0,0 +1,34 @@
+package jalview.ws;
+
+import jalview.ws.api.ServiceWithParameters;
+
+import java.beans.PropertyChangeListener;
+import java.net.URL;
+import java.util.List;
+
+public interface WSDiscovererI extends WSMenuEntryProviderI
+{
+  public static final int STATUS_OK = 1;
+  public static final int STATUS_NO_SERVICES = 0;
+  public static final int STATUS_INVALID = -1;
+  public static final int STATUS_UNKNOWN = -2;
+
+  public void setServiceUrls(List<String> wsUrls);
+
+  public List<String> getServiceUrls();
+
+  public List<ServiceWithParameters> getServices();
+
+  public boolean testServiceUrl(URL url);
+
+  public int getServerStatusFor(String url);
+
+  // TODO: should not return Thread but something generic providing isRunning method
+  public Thread startDiscoverer(PropertyChangeListener changeListener);
+
+  public String getErrorMessages();
+
+  public boolean hasServices();
+
+  public boolean isRunning();
+}
index d11174e..197d0ee 100644 (file)
@@ -23,7 +23,7 @@ package jalview.ws.jws2;
 import jalview.bin.Cache;
 import jalview.gui.AlignFrame;
 import jalview.util.MessageManager;
-import jalview.ws.WSMenuEntryProviderI;
+import jalview.ws.WSDiscovererI;
 import jalview.ws.api.ServiceWithParameters;
 import jalview.ws.jws2.jabaws2.Jws2Instance;
 import jalview.ws.params.ParamDatastoreI;
@@ -51,7 +51,7 @@ import compbio.ws.client.Services;
  * @author JimP
  * 
  */
-public class Jws2Discoverer implements Runnable, WSMenuEntryProviderI
+public class Jws2Discoverer implements WSDiscovererI, Runnable
 {
   public static final String COMPBIO_JABAWS = "http://www.compbio.dundee.ac.uk/jabaws";
 
@@ -415,16 +415,19 @@ public class Jws2Discoverer implements Runnable, WSMenuEntryProviderI
     return discoverer;
   }
 
+  @Override
   public boolean hasServices()
   {
     return !running && services != null && services.size() > 0;
   }
 
+  @Override
   public boolean isRunning()
   {
     return running;
   }
 
+  @Override
   public void setServiceUrls(List<String> wsUrls)
   {
     if (wsUrls != null && !wsUrls.isEmpty())
@@ -451,6 +454,7 @@ public class Jws2Discoverer implements Runnable, WSMenuEntryProviderI
    * 
    * @return
    */
+  @Override
   public List<String> getServiceUrls()
   {
     if (testUrls != null)
@@ -504,6 +508,7 @@ public class Jws2Discoverer implements Runnable, WSMenuEntryProviderI
     return urls;
   }
 
+  @Override
   public Vector<ServiceWithParameters> getServices()
   {
     return (services == null) ? new Vector<>()
@@ -516,7 +521,8 @@ public class Jws2Discoverer implements Runnable, WSMenuEntryProviderI
    * @param foo
    * @return
    */
-  public static boolean testServiceUrl(URL foo)
+  @Override
+  public boolean testServiceUrl(URL foo)
   {
     try
     {
@@ -564,6 +570,7 @@ public class Jws2Discoverer implements Runnable, WSMenuEntryProviderI
    * @param changeSupport2
    * @return new thread
    */
+  @Override
   public Thread startDiscoverer(PropertyChangeListener changeSupport2)
   {
     /*    if (restart())
@@ -645,6 +652,7 @@ public class Jws2Discoverer implements Runnable, WSMenuEntryProviderI
    * @return a human readable report of any problems with the service URLs used
    *         for discovery
    */
+  @Override
   public String getErrorMessages()
   {
     if (!isRunning() && !isAborted())
@@ -693,21 +701,22 @@ public class Jws2Discoverer implements Runnable, WSMenuEntryProviderI
     return null;
   }
 
+  @Override
   public int getServerStatusFor(String url)
   {
     if (validServiceUrls != null && validServiceUrls.contains(url))
     {
-      return 1;
+      return STATUS_OK;
     }
     if (urlsWithoutServices != null && urlsWithoutServices.contains(url))
     {
-      return 0;
+      return STATUS_NO_SERVICES;
     }
     if (invalidServiceUrls != null && invalidServiceUrls.contains(url))
     {
-      return -1;
+      return STATUS_INVALID;
     }
-    return -2;
+    return STATUS_UNKNOWN;
   }
 
   /**
index 2b4a958..925cd55 100644 (file)
@@ -1,14 +1,17 @@
 package jalview.ws.slivkaws;
 
+import jalview.bin.Cache;
 import jalview.gui.AlignFrame;
-import jalview.ws.WSMenuEntryProviderI;
+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.IOError;
 import java.io.IOException;
+import java.net.MalformedURLException;
 import java.net.URISyntaxException;
+import java.net.URL;
 import java.util.ArrayList;
 import java.util.List;
 
@@ -17,21 +20,18 @@ import javax.swing.JMenu;
 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 slivkaClient;
+  private List<ServiceWithParameters> services = List.of();
 
   private SlivkaWSDiscoverer()
   {
-    try
-    {
-      slivkaClient = new SlivkaClient("https://www.compbio.dundee.ac.uk/slivka");
-    } catch (URISyntaxException e)
-    {
-      throw new RuntimeException(e);
-    }
   }
 
   public static SlivkaWSDiscoverer getInstance()
@@ -43,111 +43,214 @@ public class SlivkaWSDiscoverer implements Runnable, WSMenuEntryProviderI
     return instance;
   }
 
-  /**
-   * TODO: tests needed for logic for determining type of each discovered
-   * service. Then reimplement this routine !
-   * 
-   * @return (MSA instances, one AAUI type instance, and the remaining all
-   *         sequence analysis instances taking 1 sequence only)
-   */
-  List<ServiceWithParameters> getServiceInstances()
+  private PropertyChangeSupport changeSupport = new PropertyChangeSupport(
+      this);
+
+  @Override
+  public void attachWSMenuEntry(JMenu wsmenu, final AlignFrame alignFrame)
+  {
+    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);
+
+  }
+
+  volatile boolean ready = false;
+
+  volatile Thread discovererThread = null;
+
+  private class DiscovererThread extends Thread
   {
-    List<ServiceWithParameters> instances = new ArrayList<>();
-    for (SlivkaService service : services)
+    private Thread oldThread;
+
+    DiscovererThread(Thread oldThread)
+    {
+      super();
+      this.oldThread = oldThread;
+    }
+
+    @Override
+    public void run()
     {
-      ServiceWithParameters newinstance = null;
-      for (String classifier : service.classifiers)
+      if (oldThread != null)
       {
-        if (classifier.contains("Multiple sequence alignment"))
+        oldThread.interrupt();
+        try
         {
-          // MSA services always overwrite
-          newinstance = new SlivkaMsaServiceInstance(slivkaClient, service);
+          oldThread.join();
+        } catch (InterruptedException e)
+        {
+          return;
+        } finally
+        {
+          oldThread = null;
         }
-        if (classifier.contains("Protein sequence analysis"))
+      }
+      ready = false;
+      reloadServices();
+      ready = !isInterrupted();
+    }
+  }
+
+  Thread discoverer = null;
+
+  @Override
+  public Thread startDiscoverer(PropertyChangeListener changeListener)
+  {
+    changeSupport.addPropertyChangeListener(changeListener);
+    ready = false;
+    (discovererThread = new DiscovererThread(discovererThread)).start();
+    return discovererThread;
+  }
+
+  private void reloadServices()
+  {
+    Cache.log.info("Reloading Slivka services");
+    changeSupport.firePropertyChange("services", services, List.of());
+    ArrayList<ServiceWithParameters> instances = new ArrayList<>();
+
+    for (String url : getServiceUrls())
+    {
+      Cache.log.info(url);
+      SlivkaClient client;
+      try
+      {
+        client = new SlivkaClient(url);
+      } catch (URISyntaxException e)
+      {
+        e.printStackTrace();
+        continue;
+      }
+      try
+      {
+        for (SlivkaService service : client.getServices())
         {
-          if (newinstance == null)
+          SlivkaWSInstance newinstance = null;
+          for (String classifier : service.classifiers)
+          {
+            if (classifier.contains("Multiple sequence alignment"))
             {
-              newinstance = (new SlivkaAnnotationServiceInstance(
-                      slivkaClient,
-
-                      service, false));
+              newinstance = new SlivkaMsaServiceInstance(client, service);
             }
-          }
-
-        if (classifier
+            if (classifier.contains("Protein sequence analysis")
+                && newinstance == null)
+            {
+              newinstance = new SlivkaAnnotationServiceInstance(client,
+                  service, false);
+            }
+            if (classifier
                 .contains("Sequence alignment analysis (conservation)"))
-        {
-          // always overwrite other instances
-          newinstance = new SlivkaAnnotationServiceInstance(slivkaClient,
+            {
+              newinstance = new SlivkaAnnotationServiceInstance(client,
                   service, true);
+            }
+          }
+          if (newinstance != null)
+          {
+            instances.add(newinstance);
+          }
         }
-      }
-      if (newinstance != null)
+      } catch (IOException e)
       {
-        instances.add(newinstance);
+        continue;
       }
     }
-    return instances;
-  }
 
-  private PropertyChangeSupport changeSupport = new PropertyChangeSupport(
-          this);
+    services = instances;
+    changeSupport.firePropertyChange("services", List.of(), services);
+
+    Cache.log.info("Slivka services reloading finished");
+  }
 
   @Override
-  public void attachWSMenuEntry(JMenu wsmenu, final AlignFrame alignFrame)
+  public List<ServiceWithParameters> getServices()
   {
-    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(
-            getServiceInstances(),
-              changeSupport, slivkaMenu, alignFrame, null);
-    
+    return services;
   }
 
-  List<SlivkaService>services=null;
+  @Override
+  public boolean hasServices()
+  {
+    return ready == true && services.size() > 0;
+  }
 
-  volatile boolean started = false, finished = false;
+  @Override
+  public boolean isRunning()
+  {
+    return discovererThread == null || discovererThread.isAlive()
+        || discovererThread.getState() == Thread.State.NEW;
+  }
 
-  Thread discoverer = null;
   @Override
-  public void run()
+  public void setServiceUrls(List<String> wsUrls)
   {
-    discoverer = Thread.currentThread();
-    started = true;
-    try
+    if (wsUrls != null && !wsUrls.isEmpty())
     {
-      services = slivkaClient.getServices();
-    } catch (IOException e)
+      Cache.setProperty(SLIVKA_HOST_URLS, String.join(",", wsUrls));
+    }
+    else
     {
-      throw new IOError(e);
+      Cache.removeProperty(SLIVKA_HOST_URLS);
     }
-    finished = true;
   }
 
-  public static List<ServiceWithParameters> getServices()
+  @Override
+  public List<String> getServiceUrls()
   {
-    SlivkaWSDiscoverer us = getInstance();
-    if (us.services == null)
+    String surls = Cache.getDefault(SLIVKA_HOST_URLS, COMPBIO_SLIVKA);
+    String[] urls = surls.split(",");
+    ArrayList<String> valid = new ArrayList<>(urls.length);
+    for (String url : urls)
     {
-      us.run();
+      try
+      {
+        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 us.getServiceInstances();
+    return valid;
   }
 
-  public boolean hasServices()
+  @Override
+  public boolean testServiceUrl(URL url)
   {
-    return finished == true && services != null && services.size() > 0;
+    return getServerStatusFor(url.toString()) == STATUS_OK;
   }
 
-  public boolean isRunning()
+  @Override
+  public int getServerStatusFor(String url)
+  {
+    try
+    {
+      List<?> services = new SlivkaClient(url).getServices();
+      return services.isEmpty() ? STATUS_NO_SERVICES : STATUS_OK;
+    } catch (IOException | URISyntaxException e)
+    {
+      return STATUS_INVALID;
+    }
+  }
+
+  @Override
+  public String getErrorMessages()
   {
-    return discoverer != null && discoverer.isAlive();
+    // TODO Auto-generated method stub
+    return "";
   }
 }
index 2a6c3d0..5428acd 100644 (file)
@@ -89,7 +89,8 @@ public class AAConAnnotAndSettingsIO
       }
     }
 
-    for (ServiceWithParameters svc : SlivkaWSDiscoverer.getServices())
+    for (ServiceWithParameters svc : SlivkaWSDiscoverer.getInstance()
+            .getServices())
     {
       if (svc.getNameURI().toLowerCase().contains("aacon"))
       {
index 8454e03..fef2828 100644 (file)
@@ -83,7 +83,11 @@ public class DisorderAnnotExportImport
       Thread.sleep(100);
     }
     SlivkaWSDiscoverer disc2 = SlivkaWSDiscoverer.getInstance();
-    disc2.run();
+    disc2.startDiscoverer(null);
+    while (disc2.isRunning())
+    {
+      Thread.sleep(100);
+    }
     iupreds = new ArrayList<>();
     for (ServiceWithParameters svc : disc2.getServices())
     {