JAL-3878 Create base interfaces for new web services.
authorMateusz Warowny <mmzwarowny@dundee.ac.uk>
Mon, 20 Sep 2021 16:13:21 +0000 (18:13 +0200)
committerMateusz Warowny <mmzwarowny@dundee.ac.uk>
Mon, 20 Sep 2021 16:44:44 +0000 (18:44 +0200)
15 files changed:
src/jalview/ws2/JalviewWebServiceI.java [deleted file]
src/jalview/ws2/MenuEntryProviderI.java
src/jalview/ws2/MsaMenuEntryProvider.java [deleted file]
src/jalview/ws2/ResultSupplier.java [new file with mode: 0644]
src/jalview/ws2/WSJob.java [new file with mode: 0755]
src/jalview/ws2/WSJobID.java [deleted file]
src/jalview/ws2/WSJobState.java [deleted file]
src/jalview/ws2/WSJobStatus.java [new file with mode: 0755]
src/jalview/ws2/WSJobTrackerI.java [deleted file]
src/jalview/ws2/WebServiceDiscoverer.java [new file with mode: 0644]
src/jalview/ws2/WebServiceI.java [new file with mode: 0755]
src/jalview/ws2/WebServiceInfoUpdater.java [new file with mode: 0644]
src/jalview/ws2/WebServiceThreadListenerI.java [new file with mode: 0644]
src/jalview/ws2/WebServiceWorkerI.java
src/jalview/ws2/utils/WSJobList.java [new file with mode: 0644]

diff --git a/src/jalview/ws2/JalviewWebServiceI.java b/src/jalview/ws2/JalviewWebServiceI.java
deleted file mode 100755 (executable)
index 6e65dd0..0000000
+++ /dev/null
@@ -1,67 +0,0 @@
-package jalview.ws2;
-
-import java.io.IOException;
-import java.util.List;
-
-import jalview.datamodel.SequenceI;
-import jalview.ws.params.ArgumentI;
-import jalview.ws.params.ParamDatastoreI;
-import jalview.ws.params.WsParamSetI;
-
-/**
- * Provides information about the web service and sub-routines
- * to submit and track the jobs running on the server as well as
- * retrieve the results.
- * The instances should not depend on any other jalview components, especially
- * must be oblivious to the existence of any UI.
- * They are used by other classes such as WebServiceWorkers rather than
- * manipulate data themselves.
- *
- * @author mmwarowny
- *
- * @param <R>
- */
-public interface JalviewWebServiceI<R>
-{
-  public static final int PROTEIN_SERVICE = 0x01;
-  public static final int NUCLEOTIDE_SERVICE = 0x02;
-  public static final int ALIGNMENT_ANALYSIS = 0x04;
-  
-  public String getHostName();
-  public String getName();
-  public String getDescription();
-  public String getOperationType();
-  public int getTypeFlags();
-  public boolean canSubmitGaps();
-  public int getMinSequences();
-  public int getMaxSequences();
-  public boolean hasParameters();
-  public ParamDatastoreI getParamStore();
-  
-  public default boolean isProteinService() {
-    return (getTypeFlags() & PROTEIN_SERVICE) > 0;
-  }
-  public default boolean isNucleotideService() {
-    return (getTypeFlags() & NUCLEOTIDE_SERVICE) > 0;
-  }
-  public default boolean isAlignmentAnalysis() {
-    return (getTypeFlags() & ALIGNMENT_ANALYSIS) > 0;
-  }
-  
-  public WSJobID submit(List<SequenceI> sequences, WsParamSetI preset,
-      List<ArgumentI> parameters) throws IOException;
-  
-  public void updateProgress(WSJobID id, WSJobTrackerI tracker)
-      throws IOException;
-  
-  public R getResult(WSJobID id) throws IOException;
-  
-  public void cancel(WSJobID id) throws IOException;
-  
-  public boolean handleSubmissionError(WSJobID id, Throwable th,
-      WSJobTrackerI tracker);
-  
-  public boolean handleCollectionError(WSJobID id, Throwable th,
-      WSJobTrackerI tracker);
-  
-}
index ac08c49..a5e2d0f 100755 (executable)
@@ -7,6 +7,5 @@ import jalview.gui.AlignFrame;
 @FunctionalInterface
 public interface MenuEntryProviderI
 {
-  public void buildMenu(JMenu parent, JalviewWebServiceI<?> service,
-      AlignFrame frame);
+  public void buildMenu(JMenu parent, AlignFrame frame);
 }
diff --git a/src/jalview/ws2/MsaMenuEntryProvider.java b/src/jalview/ws2/MsaMenuEntryProvider.java
deleted file mode 100755 (executable)
index d29f638..0000000
+++ /dev/null
@@ -1,211 +0,0 @@
-package jalview.ws2;
-
-import java.util.List;
-import java.awt.event.MouseAdapter;
-import java.awt.event.MouseEvent;
-import java.util.Collections;
-
-import javax.swing.JMenu;
-import javax.swing.JMenuItem;
-import javax.swing.ToolTipManager;
-
-import jalview.datamodel.AlignmentI;
-import jalview.datamodel.AlignmentView;
-import jalview.datamodel.SequenceI;
-import jalview.gui.AlignFrame;
-import jalview.gui.JvSwingUtils;
-import jalview.gui.WsJobParameters;
-import jalview.util.MathUtils;
-import jalview.util.MessageManager;
-import jalview.ws.params.ArgumentI;
-import jalview.ws.params.WsParamSetI;
-import static java.lang.String.format;
-
-
-class MsaWSWorker implements WebServiceWorkerI
-{
-  private long uid = MathUtils.getUID();
-  
-  JalviewWebServiceI<List<SequenceI>> service;
-
-  private final AlignmentView msa;
-
-  private final AlignmentI seqdataset;
-
-  private boolean submitGaps = false;
-
-  private boolean preserveOrder = false;
-
-  private List<ArgumentI> parameters = Collections.emptyList();
-
-  MsaWSWorker(JalviewWebServiceI<List<SequenceI>> service, AlignmentView msa,
-      boolean submitGaps, boolean preserveOrder, AlignmentI seqdataset)
-  {
-    this.service = service;
-    this.msa = msa;
-    this.seqdataset = seqdataset;
-    this.submitGaps = submitGaps;
-    this.preserveOrder = preserveOrder;
-  }
-  
-  @Override public long getUID() {
-    return uid;
-  };
-
-  void setParameters(List<ArgumentI> parameters)
-  {
-    this.parameters = parameters;
-  }
-  
-  @Override
-  public WSJobID startJob(WSJob job) {
-    return new WSJobID(service.getName(), service.getClass().toString(), "0");
-  }
-  
-  @Override
-  public List<WSJob> getJobs() {
-    return Collections.emptyList();
-  }
-  
-  @Override
-  public boolean pollJob(WSJob job) {
-    return false;
-  }
-
-}
-
-public class MsaMenuEntryProvider implements MenuEntryProviderI
-{
-  WebServiceExecutor executor;
-
-  public MsaMenuEntryProvider(WebServiceExecutor executor)
-  {
-    this.executor = executor;
-  }
-
-  @Override
-  public void buildMenu(JMenu parent, JalviewWebServiceI service, AlignFrame frame)
-  {
-    if (service.canSubmitGaps())
-    {
-      var alignSubmenu = new JMenu(service.getName());
-      buildMenu(alignSubmenu, service, frame, false);
-      parent.add(alignSubmenu);
-      var realignSubmenu = new JMenu(MessageManager.formatMessage(
-          "label.realign_with_params", service.getName()));
-      realignSubmenu.setToolTipText(MessageManager
-          .getString("label.align_sequences_to_existing_alignment"));
-      buildMenu(realignSubmenu, service, frame, true);
-      parent.add(realignSubmenu);
-    }
-    else
-    {
-      buildMenu(parent, service, frame, false);
-    }
-  }
-
-  private void buildMenu(JMenu parent, JalviewWebServiceI<List<SequenceI>> service,
-      AlignFrame frame, boolean submitGaps)
-  {
-    final String action = submitGaps ? "Align" : "Realign";
-    final var calcName = service.getName();
-
-    {
-      var item = new JMenuItem(MessageManager.formatMessage(
-          "label.calcname_with_default_settings", calcName));
-      item.setToolTipText(MessageManager.formatMessage(
-          "label.action_with_default_settings", action));
-      item.addActionListener((event) -> {
-        AlignmentView msa = frame.gatherSequencesForAlignment();
-        AlignmentI dataset = frame.getViewport().getAlignment().getDataset();
-        if (msa != null)
-        {
-          executor.submit(new MsaWSWorker(service, msa, submitGaps, true, dataset));
-        }
-      });
-      parent.add(item);
-    }
-
-    if (service.hasParameters())
-    {
-      var item = new JMenuItem(MessageManager.getString("label.edit_settings_and_run"));
-      item.setToolTipText(MessageManager.getString(
-          "label.view_and_change_parameters_before_alignment"));
-      item.addActionListener((event) -> {
-        AlignmentView msa = frame.gatherSequencesForAlignment();
-        AlignmentI dataset = frame.getViewport().getAlignment().getDataset();
-        if (msa != null)
-        {
-          var parameters = openEditParamsDialog(service, null, null);
-          if (parameters != null)
-          {
-            var thread = new MsaWSWorker(service, msa, submitGaps, true, dataset);
-            thread.setParameters(parameters);
-            executor.submit(thread);
-          }
-        }
-      });
-      parent.add(item);
-    }
-
-    var presets = service.getParamStore().getPresets();
-    if (presets != null && presets.size() > 0)
-    {
-      final var presetList = new JMenu(MessageManager.formatMessage(
-          "label.run_with_preset_params", calcName));
-      final var showToolTipFor = ToolTipManager.sharedInstance().getDismissDelay();
-      for (final var preset : presets)
-      {
-        var item = new JMenuItem(preset.getName());
-        final int QUICK_TOOLTIP = 1500;
-        item.addMouseListener(new MouseAdapter()
-        {
-          @Override public void mouseEntered(MouseEvent e)
-          {
-            ToolTipManager.sharedInstance().setDismissDelay(QUICK_TOOLTIP);
-          }
-          @Override public void mouseExited(MouseEvent e)
-          {
-            ToolTipManager.sharedInstance().setDismissDelay(showToolTipFor);
-          }
-        });
-        String tooltip = JvSwingUtils.wrapTooltip(true, format(
-            "<strong>%s</strong><br/>%s", 
-            MessageManager.getString(preset.isModifiable() ? 
-                "label.user_preset" : "label.service_preset"),
-            preset.getDescription()));
-        item.setToolTipText(tooltip);
-        item.addActionListener((event) -> {
-          AlignmentView msa = frame.gatherSequencesForAlignment();
-          AlignmentI dataset = frame.getViewport().getAlignment().getDataset();
-          if (msa != null)
-          {
-            var thread = new MsaWSWorker(service, msa, submitGaps, true, dataset);
-            thread.setParameters(preset.getArguments());
-            executor.submit(thread);
-          }
-        });
-        presetList.add(item);
-      }
-      parent.add(presetList);
-    }
-
-  }
-
-  private List<ArgumentI> openEditParamsDialog(JalviewWebServiceI service,
-      WsParamSetI preset, List<ArgumentI> arguments)
-  {
-    WsJobParameters jobParams;
-    if (preset == null && arguments != null && arguments.size() > 0)
-      jobParams = new WsJobParameters(service.getParamStore(), preset, arguments);
-    else
-      jobParams = new WsJobParameters(service.getParamStore(), preset, null);
-    if (!jobParams.showRunDialog())
-      // cancelled
-      return null;
-    if (jobParams.getPreset() == null)
-      return jobParams.getJobParams();
-    else
-      return jobParams.getPreset().getArguments();
-  }
-}
diff --git a/src/jalview/ws2/ResultSupplier.java b/src/jalview/ws2/ResultSupplier.java
new file mode 100644 (file)
index 0000000..e691b80
--- /dev/null
@@ -0,0 +1,9 @@
+package jalview.ws2;
+
+import java.io.IOException;
+
+@FunctionalInterface
+public interface ResultSupplier<T>
+{
+  public T getResult(WSJob job) throws IOException;
+}
diff --git a/src/jalview/ws2/WSJob.java b/src/jalview/ws2/WSJob.java
new file mode 100755 (executable)
index 0000000..90ee0b7
--- /dev/null
@@ -0,0 +1,198 @@
+package jalview.ws2;
+
+import java.io.Serializable;
+import java.net.URI;
+import java.util.Date;
+
+import jalview.util.MathUtils;
+
+import static java.lang.String.format;
+
+import java.beans.PropertyChangeListener;
+import java.beans.PropertyChangeSupport;
+
+/**
+ * An id handler to the external job running on the server. This is the smallest
+ * job unit which might me a part of a larger multi-stage or parallel task
+ * handled by the WebServiceWorker. Jobs are created by the JalviewWebService on
+ * job submission and are used to identify that job during its lifetime. It can
+ * be serialized and used between application launches.
+ *
+ * @author mmwarowny
+ *
+ */
+public final class WSJob implements Serializable
+{
+  private static final long serialVersionUID = -4600214977954333787L;
+
+  /* Client-side identifier */
+  public final long uid = MathUtils.getUID();
+
+  private int jobNum = 0;
+
+  /* Shortened server name e.g. "slivka" or "jabaws" */
+  private String serviceProvider = "";
+
+  /* Name of the service e.g. "ClustalW2" */
+  private String serviceName = "";
+
+  /* Server-side identifier */
+  private String jobID = "";
+
+  private WSJobStatus status = WSJobStatus.UNKNOWN;
+
+  private String log = "";
+
+  private String errorLog = "";
+
+  /* Base url of the server associated with the job */
+  private String hostName = "";
+
+  private Date creationTime = new Date();
+
+  public PropertyChangeSupport pcs = new PropertyChangeSupport(this);
+
+  public WSJob()
+  {
+  }
+
+  public WSJob(String serviceProvider, String serviceName, String jobID,
+          String hostName)
+  {
+    this.serviceProvider = serviceProvider;
+    this.serviceName = serviceName;
+    this.jobID = jobID;
+    this.hostName = hostName;
+  }
+
+  @Override
+  public String toString()
+  {
+    return format("%s:%s [%s] Created %s", serviceProvider, serviceName,
+            jobID, creationTime);
+  }
+
+  public int getJobNum()
+  {
+    return jobNum;
+  }
+
+  public void setJobNum(int jobNum)
+  {
+    this.jobNum = jobNum;
+  }
+
+  public WSJobStatus getStatus()
+  {
+    return status;
+  }
+
+  public void setStatus(WSJobStatus status)
+  {
+    var oldStatus = this.status;
+    this.status = status;
+    pcs.firePropertyChange("status", oldStatus, status);
+  }
+
+  public String getLog()
+  {
+    return log;
+  }
+
+  public void setLog(String log)
+  {
+    var oldLog = this.log;
+    this.log = log;
+    pcs.firePropertyChange("log", oldLog, log);
+  }
+
+  public String getErrorLog()
+  {
+    return errorLog;
+  }
+
+  public void setErrorLog(String log)
+  {
+    String oldErrorLog = this.errorLog;
+    this.errorLog = log;
+    pcs.firePropertyChange("errorLog", oldErrorLog, this.errorLog);
+  }
+
+  public long getUid()
+  {
+    return uid;
+  }
+
+  public String getServiceProvider()
+  {
+    return serviceProvider;
+  }
+
+  public void setServiceProvider(String serviceProvider)
+  {
+    this.serviceProvider = serviceProvider;
+  }
+
+  public String getServiceName()
+  {
+    return serviceName;
+  }
+
+  public void setServiceName(String serviceName)
+  {
+    this.serviceName = serviceName;
+  }
+
+  public String getJobID()
+  {
+    return jobID;
+  }
+
+  public void setJobID(String jobID)
+  {
+    this.jobID = jobID;
+  }
+
+  public String getHostName()
+  {
+    return hostName;
+  }
+
+  public void setHostName(String hostName)
+  {
+    this.hostName = hostName;
+  }
+
+  public Date getCreationTime()
+  {
+    return creationTime;
+  }
+
+  public void setCreationTime(Date creationTime)
+  {
+    this.creationTime = creationTime;
+  }
+
+  public void addPropertyChangeListener(PropertyChangeListener listener)
+  {
+    pcs.addPropertyChangeListener(listener);
+  }
+
+  public void addPropertyChangeListener(String propertyName,
+          PropertyChangeListener listener)
+  {
+    pcs.addPropertyChangeListener(propertyName, listener);
+  }
+
+  public void removePropertyChangeListener(PropertyChangeListener listener)
+  {
+    pcs.removePropertyChangeListener(listener);
+  }
+
+  public void removePropertyChagneListener(String propertyName,
+          PropertyChangeListener listener)
+  {
+    pcs.removePropertyChangeListener(propertyName, listener);
+  }
+
+}
diff --git a/src/jalview/ws2/WSJobID.java b/src/jalview/ws2/WSJobID.java
deleted file mode 100755 (executable)
index 97a1f1a..0000000
+++ /dev/null
@@ -1,69 +0,0 @@
-package jalview.ws2;
-
-import java.io.Serializable;
-import java.util.Date;
-import static java.lang.String.format;
-
-public final class WSJobID implements Serializable
-{
-  private static final long serialVersionUID = -4600214977954333787L;
-  private String serviceType = "";
-  private String serviceImpl = "";
-  private String jobID = "";
-  private Date creationTime = new Date();
-  
-  public WSJobID() {}
-  
-  public WSJobID(String serviceType, String serviceImpl, String jobID) {
-    this.serviceType = serviceType;
-    this.serviceImpl = serviceImpl;
-    this.jobID = jobID;
-  }
-  
-  @Override
-  public String toString() {
-    return format("%s:%s [%s] Created %s",
-        serviceType, serviceImpl, jobID, creationTime);
-  }
-
-  public String getServiceType()
-  {
-    return serviceType;
-  }
-
-  public void setServiceType(String serviceType)
-  {
-    this.serviceType = serviceType;
-  }
-
-  public String getServiceImpl()
-  {
-    return serviceImpl;
-  }
-
-  public void setServiceImpl(String serviceImpl)
-  {
-    this.serviceImpl = serviceImpl;
-  }
-
-  public String getJobID()
-  {
-    return jobID;
-  }
-
-  public void setJobID(String jobID)
-  {
-    this.jobID = jobID;
-  }
-
-  public Date getCreationTime()
-  {
-    return creationTime;
-  }
-
-  public void setCreationTime(Date creationTime)
-  {
-    this.creationTime = creationTime;
-  }
-  
-}
diff --git a/src/jalview/ws2/WSJobState.java b/src/jalview/ws2/WSJobState.java
deleted file mode 100755 (executable)
index 719fdbd..0000000
+++ /dev/null
@@ -1,57 +0,0 @@
-package jalview.ws2;
-
-
-public enum WSJobState
-{
-  INVALID, READY, SUBMITTED, QUEUED, RUNNING, FINISHED, BROKEN, FAILED,
-  UNKNOWN, SERVER_ERROR, CANCELLED;
-
-  public boolean isSubmitted()
-  {
-    switch (this)
-    {
-    case INVALID:
-    case READY:
-      return false;
-    default:
-      return true;
-    }
-  }
-
-  public boolean isCancelled()
-  {
-    return this == WSJobState.CANCELLED;
-  }
-
-  public boolean isDone()
-  {
-    switch (this)
-    {
-    case INVALID:
-    case READY:
-    case SUBMITTED:
-    case QUEUED:
-    case RUNNING:
-      return false;
-    default:
-      return true;
-    }
-  }
-
-  public boolean isRunning()
-  {
-    switch (this)
-    {
-    case QUEUED:
-    case RUNNING:
-      return true;
-    default:
-      return false;
-    }
-  }
-
-  public boolean isQueued()
-  {
-    return this == WSJobState.SUBMITTED;
-  }
-}
\ No newline at end of file
diff --git a/src/jalview/ws2/WSJobStatus.java b/src/jalview/ws2/WSJobStatus.java
new file mode 100755 (executable)
index 0000000..056decf
--- /dev/null
@@ -0,0 +1,103 @@
+package jalview.ws2;
+
+
+public enum WSJobStatus
+{
+  /** Job has invalid parameters and cannot be started. */
+  INVALID,
+  /** Job is ready to be submitted. */
+  READY,
+  /** Job has been submitted and awaits processing. */
+  SUBMITTED,
+  /** Job has been queued for execution. */
+  QUEUED,
+  /** Job is running. */
+  RUNNING,
+  /** Job has finished with no errors. */
+  FINISHED,
+  BROKEN,
+  /** Job has finished with errors. */
+  FAILED,
+  /** Job cannot be processed or completed due to server error. */
+  SERVER_ERROR,
+  /** Job has been cancelled. */
+  CANCELLED,
+  /** Status cannot be determined. */
+  UNKNOWN;
+
+
+  public boolean isSubmitted()
+  {
+    switch (this)
+    {
+    case SUBMITTED:
+    case QUEUED:
+    case RUNNING:
+    case FINISHED:
+    case BROKEN:
+    case FAILED:
+    case SERVER_ERROR:
+    case CANCELLED:
+      return true;
+    case INVALID:
+    case READY:
+    default:
+      return false;
+    }
+  }
+
+  public boolean isCancelled()
+  {
+    return this == WSJobStatus.CANCELLED;
+  }
+
+  public boolean isDone()
+  {
+    switch (this)
+    {
+    case FINISHED:
+    case BROKEN:
+    case FAILED:
+    case SERVER_ERROR:
+    case CANCELLED:
+      return true;
+    case INVALID:
+    case READY:
+    case SUBMITTED:
+    case QUEUED:
+    case RUNNING:
+    default:
+      return false;
+    }
+  }
+
+  public boolean isFailed()
+  {
+    switch (this)
+    {
+    case INVALID:
+    case BROKEN:
+    case FAILED:
+    case SERVER_ERROR:
+      return true;
+    case READY:
+    case SUBMITTED:
+    case QUEUED:
+    case RUNNING:
+    case FINISHED:
+    case CANCELLED:
+    default:
+      return false;
+    }
+  }
+
+  public boolean isRunning()
+  {
+    return this == WSJobStatus.RUNNING;
+  }
+
+  public boolean isQueuing()
+  {
+    return this == WSJobStatus.SUBMITTED || this == WSJobStatus.QUEUED;
+  }
+}
\ No newline at end of file
diff --git a/src/jalview/ws2/WSJobTrackerI.java b/src/jalview/ws2/WSJobTrackerI.java
deleted file mode 100755 (executable)
index 671610a..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-package jalview.ws2;
-
-public interface WSJobTrackerI {
-  public void setState(WSJobState state);
-  public int getLogSize();
-  public void appendLog(String log);
-  public int getErrorLogSize();
-  public void appendErrorLog(String log);
-}
\ No newline at end of file
diff --git a/src/jalview/ws2/WebServiceDiscoverer.java b/src/jalview/ws2/WebServiceDiscoverer.java
new file mode 100644 (file)
index 0000000..0a1565e
--- /dev/null
@@ -0,0 +1,58 @@
+package jalview.ws2;
+
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+public interface WebServiceDiscoverer
+{
+  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 List<String> getUrls();
+
+  public void setUrls(List<String> wsUrls);
+
+  public boolean testUrl(URL url);
+
+  public int getStatusForUrl(String url);
+
+  public List<WebServiceI> getServices();
+
+  public boolean hasServices();
+
+  public boolean isRunning();
+
+  public boolean isDone();
+
+  public CompletableFuture<WebServiceDiscoverer> startDiscoverer();
+
+  public String getErrorMessages();
+
+  @FunctionalInterface
+  static interface ServiceChangeListener {
+    public void servicesChanged(WebServiceDiscoverer discoverer,
+            Collection<? extends WebServiceI> services);
+  }
+
+  List<ServiceChangeListener> serviceListeners = new CopyOnWriteArrayList<>();
+
+  public default void addServiceChangeListener(ServiceChangeListener listener) {
+    serviceListeners.add(listener);
+  }
+
+  public default void removeServiceChangeListener(ServiceChangeListener listener) {
+    serviceListeners.remove(listener);
+  }
+
+  default void fireServicesChanged(List<WebServiceI> services) {
+    for (var listener: serviceListeners) {
+      listener.servicesChanged(this, services);
+    }
+  }
+}
diff --git a/src/jalview/ws2/WebServiceI.java b/src/jalview/ws2/WebServiceI.java
new file mode 100755 (executable)
index 0000000..d93dd9a
--- /dev/null
@@ -0,0 +1,53 @@
+package jalview.ws2;
+
+import java.io.IOException;
+import java.util.List;
+
+import jalview.datamodel.SequenceI;
+import jalview.ws.params.ArgumentI;
+import jalview.ws.params.ParamDatastoreI;
+import jalview.ws.params.WsParamSetI;
+import jalview.ws2.operations.Operation;
+
+/**
+ * Provides information about the web service and sub-routines
+ * to submit, track and cancel the jobs running on the server as well as
+ * retrieve the results.
+ * The instances should not depend on any other jalview components, especially
+ * must be oblivious to the existence of any UI.
+ * They are used by other classes such as WebServiceWorkers rather than
+ * manipulate data themselves.
+ *
+ * @author mmwarowny
+ */
+public interface WebServiceI
+{
+  public String getHostName();
+  public String getName();
+  public String getDescription();
+  public String getOperationType();
+  public List<Operation> getOperations();
+  public boolean hasParameters();
+  public ParamDatastoreI getParamStore();
+
+  public WSJob submit(List<SequenceI> sequences, List<ArgumentI> args)
+          throws IOException;
+
+  public void updateProgress(WSJob job)
+      throws IOException;
+
+//  public <T> ResultSupplier<T> getResultSupplier(Class<T> type);
+
+  public void cancel(WSJob job) throws IOException;
+
+  /**
+   * Handle an exception that happened during job submission.
+   * If the exception was handled property by this method, it
+   * returns true. Otherwise, returns false indicating the exception
+   * should be handled by the caller.
+   */
+  public boolean handleSubmissionError(WSJob job, Exception ex);
+
+  public boolean handleCollectionError(WSJob job, Exception ex);
+
+}
diff --git a/src/jalview/ws2/WebServiceInfoUpdater.java b/src/jalview/ws2/WebServiceInfoUpdater.java
new file mode 100644 (file)
index 0000000..eb3e350
--- /dev/null
@@ -0,0 +1,89 @@
+package jalview.ws2;
+
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.util.Objects;
+
+import jalview.gui.WebserviceInfo;
+
+public class WebServiceInfoUpdater implements PropertyChangeListener
+{
+  private final WebserviceInfo wsInfo;
+  private String outputHeader = "";
+
+  public WebServiceInfoUpdater(WebserviceInfo wsInfo) {
+    this.wsInfo = wsInfo;
+  }
+
+  public String getOutputHeader() {
+    return outputHeader;
+  }
+
+  public void setOutputHeader(String header) {
+    this.outputHeader = header;
+  }
+
+  @Override
+  public void propertyChange(PropertyChangeEvent evt)
+  {
+    switch (evt.getPropertyName()) {
+    case "status":
+      statusChanged(evt);
+      break;
+    case "log":
+      logChanged(evt);
+      break;
+    case "errorLog":
+      errorLogChanged(evt);
+      break;
+    }
+  }
+
+  private void statusChanged(PropertyChangeEvent evt) {
+    WSJob job = (WSJob) evt.getSource();
+    WSJobStatus status = (WSJobStatus) evt.getNewValue();
+    int wsInfoStatus = 0;
+    switch (status) {
+    case READY:
+    case SUBMITTED:
+    case QUEUED:
+      wsInfoStatus = WebserviceInfo.STATE_QUEUING;
+      break;
+    case RUNNING:
+      wsInfoStatus = WebserviceInfo.STATE_RUNNING;
+      break;
+    case FINISHED:
+      wsInfoStatus = WebserviceInfo.STATE_STOPPED_OK;
+      break;
+    case CANCELLED:
+      wsInfoStatus = WebserviceInfo.STATE_CANCELLED_OK;
+      break;
+    case INVALID:
+    case BROKEN:
+    case FAILED:
+    case UNKNOWN:
+      wsInfoStatus = WebserviceInfo.STATE_STOPPED_ERROR;
+      break;
+    case SERVER_ERROR:
+      wsInfoStatus = WebserviceInfo.STATE_STOPPED_SERVERERROR;
+      break;
+    }
+    wsInfo.setStatus(job.getJobNum(), wsInfoStatus);
+  }
+
+  private void logChanged(PropertyChangeEvent evt) {
+    WSJob job = (WSJob) evt.getSource();
+    String oldLog = (String) evt.getOldValue();
+    String newLog = (String) evt.getNewValue();
+    wsInfo.appendProgressText(job.getJobNum(), newLog.substring(oldLog.length()));
+  }
+
+  private void errorLogChanged(PropertyChangeEvent evt) {
+    WSJob job = (WSJob) evt.getSource();
+    String oldLog = (String) evt.getOldValue();
+    String newLog = (String) evt.getNewValue();
+    wsInfo.appendProgressText(job.getJobNum(), newLog.substring(oldLog.length()));
+  }
+
+
+}
diff --git a/src/jalview/ws2/WebServiceThreadListenerI.java b/src/jalview/ws2/WebServiceThreadListenerI.java
new file mode 100644 (file)
index 0000000..386c6ac
--- /dev/null
@@ -0,0 +1,14 @@
+package jalview.ws2;
+
+public interface WebServiceThreadListenerI
+{
+  public void submitted(WebServiceWorkerI thread);
+
+  public void submissionFailed(WebServiceWorkerI thread, Exception e);
+
+  public void pollFailed(WebServiceWorkerI thread, Exception e);
+
+  public void cancelled(WebServiceWorkerI thread);
+
+  public void done(WebServiceWorkerI thread);
+}
\ No newline at end of file
index 1c40eb8..da6e540 100644 (file)
@@ -9,65 +9,29 @@ import jalview.util.MathUtils;
 
 public interface WebServiceWorkerI
 {
-  public class WSJob
-  {
-    public final long uid = MathUtils.getUID();
-    
-    protected WSJobState state = WSJobState.UNKNOWN;
+  long getUID();
 
-    protected String jobID = "";
+  List<WSJob> getJobs();
 
-    protected int jobNum = 0;
+  void startJobs() throws IOException;
 
-    protected int allowedExceptions = 3;
+  boolean pollJobs() throws IOException;
 
-    public long getUID() {
-      return uid;
-    }
-    
-    public WSJobState getState()
-    {
-      return state;
-    }
-
-    public void setState(WSJobState state)
-    {
-      this.state = state;
-    }
-
-    public String getJobID()
-    {
-      return jobID;
-    }
-
-    public void setJobID(String jobID) {
-      this.jobID = jobID;
-    }
-    
-    public int getJobNum()
-    {
-      return jobNum;
-    }
+  WebServiceI getWebService();
 
-    public int getAllowedExceptions()
-    {
-      return allowedExceptions;
-    }
-    
-    public boolean deductAllowedExceptions() {
-      return allowedExceptions-- > 0;
-    }
-    
-    public void resetAllowedExceptions() {
-      allowedExceptions = 3;
+  default boolean isDone() {
+    if (getJobs().size() == 0)
+      return false;
+    for (WSJob job : getJobs()) {
+      if (!job.getStatus().isDone())
+        return false;
     }
+    return true;
   }
 
-  public long getUID();
-
-  public List<WSJob> getJobs();
-
-  public WSJobID startJob(WSJob job) throws IOException;
-
-  public boolean pollJob(WSJob job) throws IOException;
+  /*
+   * Called by the executor when the worker transitions to the done state
+   * either normally or exceptionally.
+   */
+  void done();
 }
diff --git a/src/jalview/ws2/utils/WSJobList.java b/src/jalview/ws2/utils/WSJobList.java
new file mode 100644 (file)
index 0000000..181a7ed
--- /dev/null
@@ -0,0 +1,64 @@
+package jalview.ws2.utils;
+
+import java.util.ArrayList;
+
+import jalview.ws2.WSJob;
+
+public class WSJobList extends ArrayList<WSJob>
+{
+  private static final long serialVersionUID = -1684855135603987602L;
+
+  public int countSubmitted() {
+    int count = 0;
+    for (WSJob job : this) {
+      if (job.getStatus().isSubmitted()) {
+        count++;
+      }
+    }
+    return count;
+  }
+
+  public int countQueuing() {
+    int count = 0;
+    for (WSJob job : this) {
+      if (job.getStatus().isQueuing()) count++;
+    }
+    return count;
+  }
+
+  public int countRunning() {
+    int count = 0;
+    for (WSJob job : this) {
+      if (job.getStatus().isRunning()) count++;
+    }
+    return count;
+  }
+
+  public int countDone() {
+    int count = 0;
+    for (WSJob job : this) {
+      if (job.getStatus().isDone()) count++;
+    }
+    return count;
+  }
+
+  public int countFailed() {
+    int count = 0;
+    for (WSJob job : this) {
+      if (job.getStatus().isFailed()) count++;
+    }
+    return count;
+  }
+
+  public int countSuccessful() {
+    return countDone() - countFailed() - countCancelled();
+  }
+
+  public int countCancelled() {
+    int count = 0;
+    for (WSJob job : this) {
+      if (job.getStatus().isCancelled()) count++;
+    }
+    return count;
+  }
+}