Merge branch 'alpha/merge_212_JalviewJS_2112' into spike/JAL-3622_Scanner_swingjs_J21... spike/JAL-3622_Scanner_swingjs_J212_merge
authorJim Procter <jprocter@issues.jalview.org>
Thu, 9 Jul 2020 14:58:39 +0000 (15:58 +0100)
committerJim Procter <jprocter@issues.jalview.org>
Thu, 9 Jul 2020 14:58:39 +0000 (15:58 +0100)
13 files changed:
j11lib/slivka-client.jar
src/jalview/gui/AlignFrame.java
src/jalview/gui/Desktop.java
src/jalview/gui/Preferences.java
src/jalview/gui/SlivkaPreferences.java [new file with mode: 0644]
src/jalview/gui/WsPreferences.java
src/jalview/jbgui/GPreferences.java
src/jalview/ws/WSDiscovererI.java [new file with mode: 0644]
src/jalview/ws/jws2/Jws2Discoverer.java
src/jalview/ws/slivkaws/SlivkaWSDiscoverer.java
src/jalview/ws/slivkaws/SlivkaWSInstance.java
test/jalview/ws/jabaws/AAConAnnotAndSettingsIO.java
test/jalview/ws/jabaws/DisorderAnnotExportImport.java

index b29679f..34d2de9 100644 (file)
Binary files a/j11lib/slivka-client.jar and b/j11lib/slivka-client.jar differ
index 4114298..cd1893d 100644 (file)
@@ -155,6 +155,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;
@@ -4522,9 +4523,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())
@@ -4551,30 +4553,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 61f342f..a99bab9 100644 (file)
@@ -120,6 +120,7 @@ import jalview.util.Platform;
 import jalview.util.ShortcutKeyMaskExWrapper;
 import jalview.util.UrlConstants;
 import jalview.viewmodel.AlignmentViewport;
+import jalview.ws.WSDiscovererI;
 import jalview.ws.params.ParamManager;
 import jalview.ws.utils.UrlDownloadClient;
 
@@ -2631,7 +2632,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)
     {
@@ -2649,14 +2650,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)
     {
@@ -2668,13 +2669,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.
    * 
@@ -2684,8 +2703,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 f720e0a..50ee476 100755 (executable)
@@ -192,6 +192,8 @@ public class Preferences extends GPreferences
 
   private WsPreferences wsPrefs;
 
+  private SlivkaPreferences slivkaPrefs;
+
   private OptionsParam promptEachTimeOpt = new OptionsParam(
           MessageManager.getString("label.prompt_each_time"),
           "Prompt each time");
@@ -219,6 +221,8 @@ public class Preferences extends GPreferences
     {
       wsPrefs = new WsPreferences();
       wsTab.add(wsPrefs, BorderLayout.CENTER);
+      slivkaPrefs = new SlivkaPreferences();
+      slivkaTab.add(slivkaPrefs, BorderLayout.CENTER);
     }
     int width = 500, height = 450;
     if (Platform.isAMacAndNotJS())
@@ -489,7 +493,7 @@ public class Preferences extends GPreferences
     doReset.addActionListener(onReset);
 
     // filter to display only custom urls
-    final RowFilter<TableModel, Object> customUrlFilter = new RowFilter<TableModel, Object>()
+    final RowFilter<TableModel, Object> customUrlFilter = new RowFilter<>()
     {
       @Override
       public boolean include(
diff --git a/src/jalview/gui/SlivkaPreferences.java b/src/jalview/gui/SlivkaPreferences.java
new file mode 100644 (file)
index 0000000..f4f6a9a
--- /dev/null
@@ -0,0 +1,360 @@
+package jalview.gui;
+
+import jalview.bin.Cache;
+import jalview.util.MessageManager;
+import jalview.ws.WSDiscovererI;
+import jalview.ws.slivkaws.SlivkaWSDiscoverer;
+
+import java.awt.BorderLayout;
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.Dimension;
+import java.awt.Font;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseListener;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.NoSuchElementException;
+
+import javax.swing.BorderFactory;
+import javax.swing.Box;
+import javax.swing.BoxLayout;
+import javax.swing.JButton;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.JProgressBar;
+import javax.swing.JScrollPane;
+import javax.swing.JTable;
+import javax.swing.UIManager;
+import javax.swing.table.AbstractTableModel;
+import javax.swing.table.DefaultTableCellRenderer;
+
+@SuppressWarnings("serial")
+public class SlivkaPreferences extends JPanel
+{
+  {
+    setLayout(new BoxLayout(this, BoxLayout.PAGE_AXIS));
+    setPreferredSize(new Dimension(500, 450));
+  }
+
+  WSDiscovererI discoverer;
+
+  private final ArrayList<String> urls = new ArrayList<>();
+
+  private final ArrayList<Integer> statuses = new ArrayList<>();
+
+  private final AbstractTableModel urlTableModel = new AbstractTableModel()
+  {
+    final String[] columnNames = { "Service URL", "Status" };
+
+    @Override
+    public String getColumnName(int col)
+    {
+      return columnNames[col];
+    }
+
+    @Override
+    public Object getValueAt(int rowIndex, int columnIndex)
+    {
+      switch (columnIndex)
+      {
+      case 0:
+        return urls.get(rowIndex);
+      case 1:
+        return statuses.get(rowIndex);
+      default:
+        throw new NoSuchElementException();
+      }
+    }
+
+    @Override
+    public int getRowCount()
+    {
+      return urls.size();
+    }
+
+    @Override
+    public int getColumnCount()
+    {
+      return 2;
+    }
+  };
+
+  private class WSStatusCellRenderer extends DefaultTableCellRenderer
+  {
+    @Override
+    public Component getTableCellRendererComponent(JTable table,
+        Object value, boolean isSelected, boolean hasFocus, int row,
+        int column)
+    {
+      setHorizontalAlignment(CENTER);
+      super.getTableCellRendererComponent(table, "\u25CF", isSelected,
+          hasFocus, row, column);
+      switch ((Integer) value)
+      {
+      case WSDiscovererI.STATUS_NO_SERVICES:
+        setForeground(Color.ORANGE);
+        break;
+      case WSDiscovererI.STATUS_OK:
+        setForeground(Color.GREEN);
+        break;
+      case WSDiscovererI.STATUS_INVALID:
+        setForeground(Color.RED);
+        break;
+      case WSDiscovererI.STATUS_UNKNOWN:
+      default:
+        setForeground(Color.LIGHT_GRAY);
+      }
+      return this;
+    }
+  }
+
+  private JTable urlListTable = new JTable(urlTableModel);
+  {
+    urlListTable.getColumnModel().getColumn(1).setMaxWidth(60);
+    urlListTable.getColumnModel().getColumn(1)
+        .setCellRenderer(new WSStatusCellRenderer());
+  }
+
+  // URL control panel buttons
+  JButton newWsUrl = new JButton(
+      MessageManager.getString("label.new_service_url"));
+
+  JButton editWsUrl = new JButton(
+      MessageManager.getString("label.edit_service_url"));
+
+  JButton deleteWsUrl = new JButton(
+      MessageManager.getString("label.delete_service_url"));
+
+  JButton moveUrlUp = new JButton(
+      MessageManager.getString("action.move_up"));
+
+  JButton moveUrlDown = new JButton(
+      MessageManager.getString("action.move_down"));
+
+  private String showEditUrlDialog(String oldUrl)
+  {
+    String input = (String) JvOptionPane
+        .showInternalInputDialog(
+            this, 
+            MessageManager.getString("label.url:"),
+            UIManager.getString("OptionPane.inputDialogTitle", MessageManager.getLocale()),
+            JOptionPane.QUESTION_MESSAGE,
+            null,
+            null,
+            oldUrl);
+    if (input == null)
+    {
+      return null;
+    }
+    try
+    {
+      new URL(input);
+    } catch (MalformedURLException ex)
+    {
+      JvOptionPane.showInternalMessageDialog(this,
+          MessageManager.getString("label.invalid_url"),
+          UIManager.getString("OptionPane.messageDialogTitle",
+              MessageManager.getLocale()),
+          JOptionPane.WARNING_MESSAGE);
+      return null;
+    }
+    return input;
+  }
+
+  // Button Action Listeners
+  private ActionListener newUrlAction = (ActionEvent e) -> {
+    final String input = showEditUrlDialog("");
+    if (input != null)
+    {
+      urls.add(input);
+      statuses.add(discoverer.getServerStatusFor(input));
+      urlTableModel.fireTableRowsInserted(urls.size(), urls.size());
+      discoverer.setServiceUrls(urls);
+    }
+  };
+
+  private ActionListener editUrlAction = (ActionEvent e) -> {
+    final int i = urlListTable.getSelectedRow();
+    if (i >= 0)
+    {
+      final String input = showEditUrlDialog(urls.get(i));
+      if (input != null)
+      {
+        urls.set(i, input);
+        statuses.set(i, discoverer.getServerStatusFor(input));
+        urlTableModel.fireTableRowsUpdated(i, i);
+        discoverer.setServiceUrls(urls);
+      }
+    }
+  };
+
+  private ActionListener deleteUrlAction = (ActionEvent e) -> {
+    final int i = urlListTable.getSelectedRow();
+    if (i >= 0)
+    {
+      urls.remove(i);
+      statuses.remove(i);
+      urlTableModel.fireTableRowsDeleted(i, i);
+      discoverer.setServiceUrls(urls);
+    }
+  };
+
+  private ActionListener moveUrlUpAction = (ActionEvent e) -> {
+    final int i = urlListTable.getSelectedRow();
+    if (i > 0)
+    {
+      moveTableRow(i, i - 1);
+      discoverer.setServiceUrls(urls);
+    }
+  };
+
+  private ActionListener moveUrlDownAction = (ActionEvent e) -> {
+    final int i = urlListTable.getSelectedRow();
+    if (i >= 0 && i < urls.size() - 1)
+    {
+      moveTableRow(i, i + 1);
+      discoverer.setServiceUrls(urls);
+    }
+  };
+
+  private MouseListener tableClickListener = new MouseAdapter()
+  {
+    final ActionEvent actionEvent = new ActionEvent(urlListTable,
+        ActionEvent.ACTION_PERFORMED, "edit");
+
+    @Override
+    public void mouseClicked(MouseEvent e)
+    {
+      if (e.getClickCount() > 1)
+      {
+        editUrlAction.actionPerformed(actionEvent);
+      }
+    }
+  };
+
+  // Setting up URL list Pane
+  {
+    Font font = new Font("Verdana", Font.PLAIN, 10);
+    JPanel urlPaneContainer = new JPanel(new BorderLayout(5, 5));
+    urlPaneContainer.setBorder(BorderFactory.createCompoundBorder(
+        BorderFactory.createTitledBorder(BorderFactory.createEtchedBorder(),
+            "Slivka Web Services"),
+        BorderFactory.createEmptyBorder(10, 5, 5, 5)));
+
+    newWsUrl.setFont(font);
+    editWsUrl.setFont(font);
+    deleteWsUrl.setFont(font);
+    moveUrlUp.setFont(font);
+    moveUrlDown.setFont(font);
+    JPanel editContainer = new JPanel();
+    editContainer.add(newWsUrl);
+    editContainer.add(editWsUrl);
+    editContainer.add(deleteWsUrl);
+    urlPaneContainer.add(editContainer, BorderLayout.PAGE_END);
+
+    JPanel moveContainer = new JPanel();
+    moveContainer
+        .setLayout(new BoxLayout(moveContainer, BoxLayout.PAGE_AXIS));
+    moveContainer.add(moveUrlUp);
+    moveContainer.add(Box.createRigidArea(new Dimension(0, 5)));
+    moveContainer.add(moveUrlDown);
+    urlPaneContainer.add(moveContainer, BorderLayout.LINE_START);
+
+    urlPaneContainer.add(new JScrollPane(urlListTable),
+        BorderLayout.CENTER);
+    this.add(urlPaneContainer);
+
+    // Connecting action listeners
+    urlListTable.addMouseListener(tableClickListener);
+    newWsUrl.addActionListener(newUrlAction);
+    editWsUrl.addActionListener(editUrlAction);
+    deleteWsUrl.addActionListener(deleteUrlAction);
+    moveUrlUp.addActionListener(moveUrlUpAction);
+    moveUrlDown.addActionListener(moveUrlDownAction);
+  }
+
+  private void moveTableRow(int fromIndex, int toIndex)
+  {
+    String url = urls.get(fromIndex);
+    int status = statuses.get(fromIndex);
+    urls.set(fromIndex, urls.get(toIndex));
+    statuses.set(fromIndex, statuses.get(toIndex));
+    urls.set(toIndex, url);
+    statuses.set(toIndex, status);
+    if (urlListTable.getSelectedRow() == fromIndex)
+    {
+      urlListTable.setRowSelectionInterval(toIndex, toIndex);
+    }
+    int firstRow = Math.min(toIndex, fromIndex);
+    int lastRow = Math.max(fromIndex, toIndex);
+    urlTableModel.fireTableRowsUpdated(firstRow, lastRow);
+  }
+
+  // Discoverer reloading buttons
+  JButton refreshServices = new JButton(
+      MessageManager.getString("action.refresh_services"));
+
+  JButton resetServices = new JButton(
+      MessageManager.getString("action.reset_services"));
+
+  JProgressBar progressBar = new JProgressBar();
+
+  // Discoverer buttons action listeners
+  private ActionListener refreshServicesAction = (ActionEvent e) -> {
+    new Thread(() -> {
+      progressBar.setVisible(true);
+      Cache.log.info("Requesting service reload");
+      Desktop.instance.startServiceDiscovery(discoverer, true);
+      Cache.log.info("Reloading done");
+      progressBar.setVisible(false);
+    }).start();
+  };
+
+  private ActionListener resetServicesAction = (ActionEvent e) -> {
+    discoverer.setServiceUrls(null);
+    urls.clear();
+    statuses.clear();
+    urls.addAll(discoverer.getServiceUrls());
+    for (String url : urls)
+    {
+      statuses.add(discoverer.getServerStatusFor(url));
+    }
+    urlTableModel.fireTableDataChanged();
+  };
+
+  {
+    Font font = new Font("Verdana", Font.PLAIN, 11);
+    refreshServices.setFont(font);
+    resetServices.setFont(font);
+    JPanel container = new JPanel();
+    container.add(refreshServices);
+    container.add(resetServices);
+    this.add(container);
+
+    // Connecting action listeners
+    refreshServices.addActionListener(refreshServicesAction);
+    resetServices.addActionListener(resetServicesAction);
+  }
+
+  {
+    progressBar.setVisible(false);
+    progressBar.setIndeterminate(true);
+    add(progressBar);
+  }
+
+  SlivkaPreferences()
+  {
+    // Initial URLs loading
+    discoverer = SlivkaWSDiscoverer.getInstance();
+    urls.addAll(discoverer.getServiceUrls());
+    for (String url : urls)
+    {
+      statuses.add(discoverer.getServerStatusFor(url));
+    }
+  }
+}
index 0f776c6..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,6 +34,7 @@ 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;
 
@@ -155,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(
@@ -484,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();
         }
index 8a6e8ae..b354acb 100755 (executable)
@@ -318,6 +318,8 @@ public class GPreferences extends JPanel
    */
   protected JPanel wsTab = new JPanel();
 
+  protected JPanel slivkaTab = new JPanel();
+
   /*
    * Backups tab components
    * a lot of these are member variables instead of local variables only so that they
@@ -430,6 +432,8 @@ public class GPreferences extends JPanel
     {
       wsTab.setLayout(new BorderLayout());
       tabbedPane.add(wsTab, MessageManager.getString("label.web_services"));
+      slivkaTab.setLayout(new BorderLayout());
+      tabbedPane.add(slivkaTab, "Slivka Services");
     }
 
     /*
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 f1f8690..30c1cb7 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";
 
@@ -417,16 +417,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())
@@ -453,6 +456,7 @@ public class Jws2Discoverer implements Runnable, WSMenuEntryProviderI
    * 
    * @return
    */
+  @Override
   public List<String> getServiceUrls()
   {
     if (testUrls != null)
@@ -506,6 +510,7 @@ public class Jws2Discoverer implements Runnable, WSMenuEntryProviderI
     return urls;
   }
 
+  @Override
   public Vector<ServiceWithParameters> getServices()
   {
     return (services == null) ? new Vector<>()
@@ -518,7 +523,8 @@ public class Jws2Discoverer implements Runnable, WSMenuEntryProviderI
    * @param foo
    * @return
    */
-  public static boolean testServiceUrl(URL foo)
+  @Override
+  public boolean testServiceUrl(URL foo)
   {
     try
     {
@@ -566,6 +572,7 @@ public class Jws2Discoverer implements Runnable, WSMenuEntryProviderI
    * @param changeSupport2
    * @return new thread
    */
+  @Override
   public Thread startDiscoverer(PropertyChangeListener changeSupport2)
   {
     /*    if (restart())
@@ -647,6 +654,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())
@@ -695,21 +703,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..7a41431 100644 (file)
@@ -1,14 +1,16 @@
 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.URISyntaxException;
+import java.net.MalformedURLException;
+import java.net.URL;
 import java.util.ArrayList;
 import java.util.List;
 
@@ -17,21 +19,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 +42,208 @@ 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;
+      client = new SlivkaClient(url);
+      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 e)
+    {
+      Cache.log.error("Slivka could not retrieve services list", e);
+      return STATUS_INVALID;
+    }
+  }
+
+  @Override
+  public String getErrorMessages()
   {
-    return discoverer != null && discoverer.isAlive();
+    // TODO Auto-generated method stub
+    return "";
   }
 }
index 952aab6..33c1205 100644 (file)
@@ -13,10 +13,10 @@ import jalview.ws.params.ParamManager;
 import jalview.ws.params.WsParamSetI;
 
 import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
 import java.io.IOError;
 import java.io.IOException;
 import java.io.InputStream;
-import java.io.InputStreamReader;
 import java.util.Arrays;
 import java.util.EnumMap;
 import java.util.HashSet;
@@ -106,7 +106,7 @@ public abstract class SlivkaWSInstance extends ServiceWithParameters
         }
         else
         {
-          form.insert(fieldName, field.valueOf(arg.getValue()));
+          form.insert(fieldName, arg.getValue());
         }
       }
     }
@@ -130,44 +130,37 @@ public abstract class SlivkaWSInstance extends ServiceWithParameters
   {
     List<RemoteFile> files = client.getJobResults(job.getJobId());
     Optional<RemoteFile> logFile = files.stream()
-        .filter(f -> f.getLabel().equals("log")).findFirst();
+            .filter(f -> f.getLabel().equals("log")).findFirst();
     boolean newContent = false;
     if (logFile.isPresent())
     {
-      InputStream stream = logFile.get().getContent();
-      long nextChunk = stream.skip(job.getNextChunk());
-      int len = appendJobStatus(job, stream);
-      job.setnextChunk(nextChunk + len);
-      newContent |= len > 0;
+      ByteArrayOutputStream output = new ByteArrayOutputStream();
+      logFile.get().writeTo(output);
+      if (output.size() > job.getNextChunk())
+      {
+        newContent = true;
+        job.setStatus(output.toString("UTF-8"));
+        job.setnextChunk(output.size());
+      }
     }
     if (failedStates.contains(job.getJobState()))
     {
       Optional<RemoteFile> errLogFile = files.stream()
-          .filter(f -> f.getLabel().equals("error-log")).findFirst();
+              .filter(f -> f.getLabel().equals("error-log")).findFirst();
       if (errLogFile.isPresent())
       {
-        newContent |= appendJobStatus(job, errLogFile.get().getContent()) > 0;
+        ByteArrayOutputStream output = new ByteArrayOutputStream();
+        errLogFile.get().writeTo(output);
+        if (output.size() > 0)
+        {
+          newContent = true;
+          job.setStatus(job.getStatus() + "\n" + output.toString("UTF-8"));
+        }
       }
     }
     return newContent;
   }
 
-  private int appendJobStatus(WsJob job, InputStream stream) throws IOException
-  {
-    StringBuilder builder = new StringBuilder(job.getStatus());
-    InputStreamReader reader = new InputStreamReader(stream);
-    char[] buffer = new char[4096];
-    int chunkLen = 0;
-    int len = 0;
-    while ((len = reader.read(buffer)) != -1)
-    {
-      chunkLen += len;
-      builder.append(buffer, 0, len);
-    }
-    job.setStatus(builder.toString());
-    return chunkLen;
-  }
-
   @Override
   public final boolean handleSubmitError(Throwable _lex, WsJob j, WebserviceInfo wsInfo)
   {
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())
     {