JAL-3070 refactored Client specific code from jalview.ws.jws2.MSA* as implementors...
authorJim Procter <jprocter@issues.jalview.org>
Wed, 1 Aug 2018 20:17:29 +0000 (21:17 +0100)
committerJim Procter <jprocter@issues.jalview.org>
Wed, 1 Aug 2018 20:17:29 +0000 (21:17 +0100)
15 files changed:
src/jalview/ws/AWsJob.java
src/jalview/ws/WSClient.java
src/jalview/ws/api/CancellableI.java [new file with mode: 0644]
src/jalview/ws/api/JalviewWebServiceI.java [new file with mode: 0644]
src/jalview/ws/api/JobId.java [new file with mode: 0644]
src/jalview/ws/api/MsaI.java [new file with mode: 0644]
src/jalview/ws/api/MsaResultI.java [new file with mode: 0644]
src/jalview/ws/api/MultipleSequenceAlignmentI.java [new file with mode: 0644]
src/jalview/ws/gui/MsaWSJob.java [new file with mode: 0644]
src/jalview/ws/gui/MsaWSThread.java [moved from src/jalview/ws/jws2/MsaWSThread.java with 54% similarity]
src/jalview/ws/gui/WsJob.java [new file with mode: 0644]
src/jalview/ws/jws2/JabaPreset.java
src/jalview/ws/jws2/MsaWSClient.java
src/jalview/ws/jws2/jabaws2/JabawsMsaInstance.java [new file with mode: 0644]
src/jalview/ws/jws2/jabaws2/Jws2Instance.java

index bb203fc..4a9cb74 100644 (file)
@@ -251,4 +251,36 @@ public abstract class AWsJob
     arguments = paramset;
 
   }
+
+  public boolean isPresetJob()
+  {
+    return preset!=null && arguments==null; 
+  }
+
+  public List<ArgumentI> getArguments()
+  {
+    return arguments;
+  }
+
+  public WsParamSetI getPreset()
+  {
+    return preset;
+  }
+
+  long nextChunk = 0;
+
+  /**
+   * update the record of the last position in the log file read for this job
+   * 
+   * @param nextChunk
+   */
+  public void setnextChunk(long nextChunk)
+  {
+    this.nextChunk = nextChunk;
+  }
+
+  public long getNextChunk()
+  {
+    return nextChunk;
+  }
 }
index d031142..33aea90 100755 (executable)
@@ -67,6 +67,11 @@ public abstract class WSClient // implements WSMenuEntryProviderI
   protected WebserviceInfo wsInfo;
 
   /**
+   * the root object for the service client
+   */
+  protected UIinfo serviceHandle;
+
+  /**
    * total number of jobs managed by this web service client instance.
    */
   int jobsRunning = 0;
diff --git a/src/jalview/ws/api/CancellableI.java b/src/jalview/ws/api/CancellableI.java
new file mode 100644 (file)
index 0000000..adef2b9
--- /dev/null
@@ -0,0 +1,8 @@
+package jalview.ws.api;
+
+import jalview.ws.gui.WsJob;
+
+public interface CancellableI
+{
+  public boolean cancel(WsJob job);
+}
diff --git a/src/jalview/ws/api/JalviewWebServiceI.java b/src/jalview/ws/api/JalviewWebServiceI.java
new file mode 100644 (file)
index 0000000..ecc8bab
--- /dev/null
@@ -0,0 +1,19 @@
+package jalview.ws.api;
+
+import jalview.gui.WebserviceInfo;
+import jalview.ws.gui.WsJob;
+
+public interface JalviewWebServiceI
+{
+
+  void updateStatus(WsJob job);
+
+  boolean updateJobProgress(WsJob job) throws Exception;
+
+  boolean handleSubmitError(Throwable _lex, WsJob j, WebserviceInfo wsInfo)
+          throws Exception, Error;
+
+  boolean handleCollectionException(Exception e, WsJob msjob,
+          WebserviceInfo wsInfo);
+
+}
diff --git a/src/jalview/ws/api/JobId.java b/src/jalview/ws/api/JobId.java
new file mode 100644 (file)
index 0000000..2a092bd
--- /dev/null
@@ -0,0 +1,59 @@
+package jalview.ws.api;
+
+import java.time.Instant;
+import java.util.Date;
+
+public class JobId
+{
+  // TODO: JobId could include sequenceI anonymisation stuff
+  // TODO: getProgress() -> input stream to log file for job.
+  private String serviceType;
+
+  private String serviceImpl;
+
+  private String jobId;
+
+  private Instant creationTime;
+
+  public JobId(String serviceType, String serviceImpl, String id)
+  {
+    this.serviceType = serviceType;
+    this.serviceImpl = serviceImpl;
+    jobId = id;
+    creationTime = Instant.now();
+  }
+
+  @Override
+  public String toString()
+  {
+    return "" + serviceType + ":" + serviceImpl + ":" + jobId + "\nCreated "
+            + Date.from(creationTime);
+  }
+  /**
+   * a stringified version of the Job Id that can be saved in project.
+   */
+  public String getURI()
+  {
+    return jobId;
+  }
+
+  public String getServiceType()
+  {
+    return serviceType;
+  }
+
+  public String getServiceImpl()
+  {
+    return serviceImpl;
+  }
+
+  public String getJobId()
+  {
+    return jobId;
+  }
+
+  public Instant getCreationTime()
+  {
+    return creationTime;
+  }
+}
diff --git a/src/jalview/ws/api/MsaI.java b/src/jalview/ws/api/MsaI.java
new file mode 100644 (file)
index 0000000..3c426c0
--- /dev/null
@@ -0,0 +1,36 @@
+package jalview.ws.api;
+
+import jalview.datamodel.SequenceI;
+import jalview.ws.params.ArgumentI;
+import jalview.ws.params.WsParamSetI;
+
+import java.util.List;
+
+/**
+ * MSA analysis interface
+ * 
+ * @author jprocter
+ * 
+ *         Generic job submission/management model: - A service instance
+ *         implements one or more analysis interfaces, a status interface, a
+ *         progress interface, and one or more results interface, plus any
+ *         informational/descriptional interfaces - analysis interfaces return
+ *         JobId or throw exceptions/errors.
+ * 
+ *
+ */
+public interface MsaI
+{
+  /**
+   * Given a set of sequences
+   * 
+   * @param toalign
+   * @param parameters
+   * @param list
+   * @return JobId or exceptions are thrown.
+   * @throws Throwable
+   */
+  public JobId align(List<SequenceI> toalign, WsParamSetI parameters,
+          List<ArgumentI> list)
+          throws Throwable;
+}
diff --git a/src/jalview/ws/api/MsaResultI.java b/src/jalview/ws/api/MsaResultI.java
new file mode 100644 (file)
index 0000000..38c0a0a
--- /dev/null
@@ -0,0 +1,13 @@
+package jalview.ws.api;
+
+import jalview.datamodel.AlignmentI;
+import jalview.ws.params.InvalidArgumentException;
+
+import java.io.IOError;
+import java.rmi.ServerError;
+
+public interface MsaResultI
+{
+  public AlignmentI getAlignmentFor(JobId jobId)
+          throws InvalidArgumentException, ServerError, IOError;
+}
\ No newline at end of file
diff --git a/src/jalview/ws/api/MultipleSequenceAlignmentI.java b/src/jalview/ws/api/MultipleSequenceAlignmentI.java
new file mode 100644 (file)
index 0000000..10b8383
--- /dev/null
@@ -0,0 +1,13 @@
+package jalview.ws.api;
+
+/**
+ * A simple parameterisable multiple sequence alignment service
+ * 
+ * @author jprocter
+ *
+ */
+public interface MultipleSequenceAlignmentI
+        extends JalviewWebServiceI, MsaI, MsaResultI
+{
+
+}
diff --git a/src/jalview/ws/gui/MsaWSJob.java b/src/jalview/ws/gui/MsaWSJob.java
new file mode 100644 (file)
index 0000000..86d299a
--- /dev/null
@@ -0,0 +1,381 @@
+package jalview.ws.gui;
+
+import jalview.analysis.AlignSeq;
+import jalview.datamodel.AlignmentI;
+import jalview.datamodel.AlignmentOrder;
+import jalview.datamodel.Sequence;
+import jalview.datamodel.SequenceI;
+import jalview.util.MessageManager;
+import jalview.ws.api.JobId;
+import jalview.ws.jws2.dm.JabaWsParamSet;
+import jalview.ws.params.ArgumentI;
+
+import java.util.ArrayList;
+import java.util.Vector;
+
+class MsaWSJob extends WsJob
+{
+  /**
+   * holds basic MSA analysis configuration - todo - encapsulate
+   */
+  private final MsaWSThread msaWSThread;
+
+  long lastChunk = 0;
+
+  /**
+   * input
+   */
+  ArrayList<SequenceI> seqs = new ArrayList<>();
+
+  /**
+   * output
+   */
+  AlignmentI alignment;
+
+  // set if the job didn't get run - then the input is simply returned to the
+  // user
+  private boolean returnInput = false;
+
+  /**
+   * MsaWSJob
+   * 
+   * @param jobNum
+   *          int
+   * @param msaWSThread
+   *          TODO - abstract the properties provided by the thread
+   * @param jobId
+   *          String
+   */
+  public MsaWSJob(MsaWSThread msaWSThread, int jobNum, SequenceI[] inSeqs)
+  {
+    this.msaWSThread = msaWSThread;
+    this.jobnum = jobNum;
+    if (!prepareInput(inSeqs, 2))
+    {
+      submitted = true;
+      subjobComplete = true;
+      returnInput = true;
+    }
+
+  }
+
+  Vector<String[]> emptySeqs = new Vector();
+
+  /**
+   * prepare input sequences for MsaWS service
+   * 
+   * @param seqs
+   *          jalview sequences to be prepared
+   * @param minlen
+   *          minimum number of residues required for this MsaWS service
+   * @return true if seqs contains sequences to be submitted to service.
+   */
+  // TODO: return compbio.seqs list or nothing to indicate validity.
+  private boolean prepareInput(SequenceI[] seqs, int minlen)
+  {
+    // TODO: service specific input data is generated in this method - for
+    // JABAWS it is client-side
+    // prepared, but for Slivka it could be uploaded at this stage.
+
+    int nseqs = 0;
+    if (minlen < 0)
+    {
+      throw new Error(MessageManager.getString(
+              "error.implementation_error_minlen_must_be_greater_zero"));
+    }
+    for (int i = 0; i < seqs.length; i++)
+    {
+      if (seqs[i].getEnd() - seqs[i].getStart() > minlen - 1)
+      {
+        nseqs++;
+      }
+    }
+    boolean valid = nseqs > 1; // need at least two seqs
+    Sequence seq;
+    for (int i = 0, n = 0; i < seqs.length; i++)
+    {
+      String newname = jalview.analysis.SeqsetUtils.unique_name(i); // same
+      // for
+      // any
+      // subjob
+      SeqNames.put(newname,
+              jalview.analysis.SeqsetUtils.SeqCharacterHash(seqs[i]));
+      if (valid && seqs[i].getEnd() - seqs[i].getStart() > minlen - 1)
+      {
+        // make new input sequence with or without gaps
+        seq = new Sequence(newname,
+                (this.msaWSThread.submitGaps) ? seqs[i].getSequenceAsString()
+                        : AlignSeq.extractGaps(
+                                jalview.util.Comparison.GapChars,
+                                seqs[i].getSequenceAsString()));
+        this.seqs.add(seq);
+      }
+      else
+      {
+        String empty = null;
+        if (seqs[i].getEnd() >= seqs[i].getStart())
+        {
+          empty = (this.msaWSThread.submitGaps) ? seqs[i].getSequenceAsString()
+                  : AlignSeq.extractGaps(jalview.util.Comparison.GapChars,
+                          seqs[i].getSequenceAsString());
+        }
+        emptySeqs.add(new String[] { newname, empty });
+      }
+    }
+    return valid;
+  }
+
+  /**
+   * 
+   * @return true if getAlignment will return a valid alignment result.
+   */
+  @Override
+  public boolean hasResults()
+  {
+    if (subjobComplete && isFinished() && (alignment != null
+            || (emptySeqs != null && emptySeqs.size() > 0)))
+    {
+      return true;
+    }
+    return false;
+  }
+
+  /**
+   * 
+   * get the alignment including any empty sequences in the original order
+   * with original ids. Caller must access the alignment.getMetadata() object
+   * to annotate the final result passsed to the user.
+   * 
+   * @return { SequenceI[], AlignmentOrder }
+   */
+  public Object[] getAlignment()
+  {
+    // TODO: make this generic based on MsaResultI
+    // TODO: decide if the data loss for this return signature is avoidable
+    // (ie should we just return AlignmentI instead ?)
+    if (hasResults())
+    {
+      SequenceI[] alseqs = null;
+      char alseq_gapchar = '-';
+      int alseq_l = 0;
+      alseqs = new SequenceI[alignment.getSequences().size()];
+      if (alignment.getSequences().size() > 0)
+      {
+        for (SequenceI seq : alignment
+                .getSequences())
+        {
+          alseqs[alseq_l++] = new Sequence(seq);
+        }
+        alseq_gapchar = alignment.getGapCharacter();
+
+      }
+      // add in the empty seqs.
+      if (emptySeqs.size() > 0)
+      {
+        SequenceI[] t_alseqs = new SequenceI[alseq_l + emptySeqs.size()];
+        // get width
+        int i, w = 0;
+        if (alseq_l > 0)
+        {
+          for (i = 0, w = alseqs[0].getLength(); i < alseq_l; i++)
+          {
+            if (w < alseqs[i].getLength())
+            {
+              w = alseqs[i].getLength();
+            }
+            t_alseqs[i] = alseqs[i];
+            alseqs[i] = null;
+          }
+        }
+        // check that aligned width is at least as wide as emptySeqs width.
+        int ow = w, nw = w;
+        for (i = 0, w = emptySeqs.size(); i < w; i++)
+        {
+          String[] es = emptySeqs.get(i);
+          if (es != null && es[1] != null)
+          {
+            int sw = es[1].length();
+            if (nw < sw)
+            {
+              nw = sw;
+            }
+          }
+        }
+        // make a gapped string.
+        StringBuffer insbuff = new StringBuffer(w);
+        for (i = 0; i < nw; i++)
+        {
+          insbuff.append(alseq_gapchar);
+        }
+        if (ow < nw)
+        {
+          for (i = 0; i < alseq_l; i++)
+          {
+            int sw = t_alseqs[i].getLength();
+            if (nw > sw)
+            {
+              // pad at end
+              alseqs[i].setSequence(t_alseqs[i].getSequenceAsString()
+                      + insbuff.substring(0, sw - nw));
+            }
+          }
+        }
+        for (i = 0, w = emptySeqs.size(); i < w; i++)
+        {
+          String[] es = emptySeqs.get(i);
+          if (es[1] == null)
+          {
+            t_alseqs[i + alseq_l] = new jalview.datamodel.Sequence(es[0],
+                    insbuff.toString(), 1, 0);
+          }
+          else
+          {
+            if (es[1].length() < nw)
+            {
+              t_alseqs[i + alseq_l] = new jalview.datamodel.Sequence(
+                      es[0],
+                      es[1] + insbuff.substring(0, nw - es[1].length()),
+                      1, 1 + es[1].length());
+            }
+            else
+            {
+              t_alseqs[i + alseq_l] = new jalview.datamodel.Sequence(
+                      es[0], es[1]);
+            }
+          }
+        }
+        alseqs = t_alseqs;
+      }
+      AlignmentOrder msaorder = new AlignmentOrder(alseqs);
+      // always recover the order - makes parseResult()'s life easier.
+      jalview.analysis.AlignmentSorter.recoverOrder(alseqs);
+      // account for any missing sequences
+      jalview.analysis.SeqsetUtils.deuniquify(SeqNames, alseqs);
+      return new Object[] { alseqs, msaorder };
+    }
+    return null;
+  }
+
+  /**
+   * mark subjob as cancelled and set result object appropriatly
+   */
+  void cancel()
+  {
+    cancelled = true;
+    subjobComplete = true;
+    alignment = null;
+  }
+
+  /**
+   * 
+   * @return boolean true if job can be submitted.
+   */
+  @Override
+  public boolean hasValidInput()
+  {
+    // TODO: get attributes for this MsaWS instance to check if it can do two
+    // sequence alignment.
+    if (seqs != null && seqs.size() >= 2) // two or more sequences is valid ?
+    {
+      return true;
+    }
+    return false;
+  }
+
+  StringBuffer jobProgress = new StringBuffer();
+
+  @Override
+  public void setStatus(String string)
+  {
+    jobProgress.setLength(0);
+    jobProgress.append(string);
+  }
+
+  @Override
+  public String getStatus()
+  {
+    return jobProgress.toString();
+  }
+
+  @Override
+  public boolean hasStatus()
+  {
+    return jobProgress != null;
+  }
+
+  /**
+   * @return the lastChunk
+   */
+  public long getLastChunk()
+  {
+    return lastChunk;
+  }
+
+  /**
+   * @param lastChunk
+   *          the lastChunk to set
+   */
+  public void setLastChunk(long lastChunk)
+  {
+    this.lastChunk = lastChunk;
+  }
+
+  String alignmentProgram = null;
+
+  public String getAlignmentProgram()
+  {
+    return alignmentProgram;
+  }
+
+  public boolean hasArguments()
+  {
+    return (arguments != null && arguments.size() > 0)
+            || (preset != null && preset instanceof JabaWsParamSet);
+  }
+
+  /**
+   * add a progess header to status string containing presets/args used
+   */
+  public void addInitialStatus()
+  {
+    // TODO: decide if it is useful to report 'JABAWS format' argument lists
+    // rather than generic Jalview service arguments
+    if (preset != null)
+    {
+      jobProgress.append(
+              "Using " + (preset.isModifiable() ? "Server" : "User")
+                      + "Preset: " + preset.getName());
+      for (ArgumentI opt : preset.getArguments())
+      {
+        jobProgress.append(opt.getName() + " " + opt.getValue() + "\n");
+      }
+    }
+    else
+    {
+      if (arguments != null && arguments.size() > 0)
+      {
+        jobProgress.append("With custom parameters : \n");
+        // merge arguments with preset's own arguments.
+        for (ArgumentI opt : arguments)
+        {
+          jobProgress.append(opt.getName() + " " + opt.getValue() + "\n");
+        }
+      }
+      jobProgress.append("\nJob Output:\n");
+    }
+  }
+
+  JobId jobHandle = null;
+  public void setJobHandle(JobId align)
+  {
+    jobHandle = align;
+    setJobId(jobHandle.getJobId());
+
+  }
+
+  public JobId getJobHandle()
+  {
+    return jobHandle;
+  }
+
+}
\ No newline at end of file
similarity index 54%
rename from src/jalview/ws/jws2/MsaWSThread.java
rename to src/jalview/ws/gui/MsaWSThread.java
index b1d6452..7141e73 100644 (file)
  * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
  * The Jalview Authors are detailed in the 'AUTHORS' file.
  */
-package jalview.ws.jws2;
+package jalview.ws.gui;
 
-import jalview.analysis.AlignSeq;
 import jalview.bin.Cache;
 import jalview.datamodel.Alignment;
 import jalview.datamodel.AlignmentI;
 import jalview.datamodel.AlignmentOrder;
 import jalview.datamodel.AlignmentView;
 import jalview.datamodel.HiddenColumns;
-import jalview.datamodel.Sequence;
 import jalview.datamodel.SequenceI;
 import jalview.gui.AlignFrame;
 import jalview.gui.Desktop;
 import jalview.gui.SplitFrame;
 import jalview.gui.WebserviceInfo;
 import jalview.util.MessageManager;
+import jalview.ws.AWSThread;
 import jalview.ws.AWsJob;
 import jalview.ws.JobStateSummary;
 import jalview.ws.WSClientI;
-import jalview.ws.jws2.dm.JabaWsParamSet;
+import jalview.ws.api.CancellableI;
+import jalview.ws.api.JobId;
+import jalview.ws.api.MultipleSequenceAlignmentI;
+import jalview.ws.gui.WsJob.JobState;
 import jalview.ws.params.ArgumentI;
 import jalview.ws.params.WsParamSetI;
 
 import java.util.ArrayList;
 import java.util.List;
-import java.util.Vector;
 
 import javax.swing.JInternalFrame;
 
-import compbio.data.msa.MsaWS;
-import compbio.metadata.Argument;
-import compbio.metadata.ChunkHolder;
-import compbio.metadata.JobStatus;
-import compbio.metadata.Preset;
-
-class MsaWSThread extends AWS2Thread implements WSClientI
+public class MsaWSThread extends AWSThread implements WSClientI
 {
   boolean submitGaps = false; // pass sequences including gaps to alignment
 
@@ -63,377 +58,13 @@ class MsaWSThread extends AWS2Thread implements WSClientI
 
   // order
 
-  class MsaWSJob extends JWs2Job
-  {
-    long lastChunk = 0;
-
-    /**
-     * input
-     */
-    ArrayList<compbio.data.sequence.FastaSequence> seqs = new ArrayList<>();
-
-    /**
-     * output
-     */
-    compbio.data.sequence.Alignment alignment;
-
-    // set if the job didn't get run - then the input is simply returned to the
-    // user
-    private boolean returnInput = false;
-
-    /**
-     * MsaWSJob
-     * 
-     * @param jobNum
-     *          int
-     * @param jobId
-     *          String
-     */
-    public MsaWSJob(int jobNum, SequenceI[] inSeqs)
-    {
-      this.jobnum = jobNum;
-      if (!prepareInput(inSeqs, 2))
-      {
-        submitted = true;
-        subjobComplete = true;
-        returnInput = true;
-      }
-
-    }
-
-    Vector<String[]> emptySeqs = new Vector();
-
-    /**
-     * prepare input sequences for MsaWS service
-     * 
-     * @param seqs
-     *          jalview sequences to be prepared
-     * @param minlen
-     *          minimum number of residues required for this MsaWS service
-     * @return true if seqs contains sequences to be submitted to service.
-     */
-    // TODO: return compbio.seqs list or nothing to indicate validity.
-    private boolean prepareInput(SequenceI[] seqs, int minlen)
-    {
-      int nseqs = 0;
-      if (minlen < 0)
-      {
-        throw new Error(MessageManager.getString(
-                "error.implementation_error_minlen_must_be_greater_zero"));
-      }
-      for (int i = 0; i < seqs.length; i++)
-      {
-        if (seqs[i].getEnd() - seqs[i].getStart() > minlen - 1)
-        {
-          nseqs++;
-        }
-      }
-      boolean valid = nseqs > 1; // need at least two seqs
-      compbio.data.sequence.FastaSequence seq;
-      for (int i = 0, n = 0; i < seqs.length; i++)
-      {
-        String newname = jalview.analysis.SeqsetUtils.unique_name(i); // same
-        // for
-        // any
-        // subjob
-        SeqNames.put(newname,
-                jalview.analysis.SeqsetUtils.SeqCharacterHash(seqs[i]));
-        if (valid && seqs[i].getEnd() - seqs[i].getStart() > minlen - 1)
-        {
-          // make new input sequence with or without gaps
-          seq = new compbio.data.sequence.FastaSequence(newname,
-                  (submitGaps) ? seqs[i].getSequenceAsString()
-                          : AlignSeq.extractGaps(
-                                  jalview.util.Comparison.GapChars,
-                                  seqs[i].getSequenceAsString()));
-          this.seqs.add(seq);
-        }
-        else
-        {
-          String empty = null;
-          if (seqs[i].getEnd() >= seqs[i].getStart())
-          {
-            empty = (submitGaps) ? seqs[i].getSequenceAsString()
-                    : AlignSeq.extractGaps(jalview.util.Comparison.GapChars,
-                            seqs[i].getSequenceAsString());
-          }
-          emptySeqs.add(new String[] { newname, empty });
-        }
-      }
-      return valid;
-    }
-
-    /**
-     * 
-     * @return true if getAlignment will return a valid alignment result.
-     */
-    @Override
-    public boolean hasResults()
-    {
-      if (subjobComplete && isFinished() && (alignment != null
-              || (emptySeqs != null && emptySeqs.size() > 0)))
-      {
-        return true;
-      }
-      return false;
-    }
-
-    /**
-     * 
-     * get the alignment including any empty sequences in the original order
-     * with original ids. Caller must access the alignment.getMetadata() object
-     * to annotate the final result passsed to the user.
-     * 
-     * @return { SequenceI[], AlignmentOrder }
-     */
-    public Object[] getAlignment()
-    {
-      // is this a generic subjob or a Jws2 specific Object[] return signature
-      if (hasResults())
-      {
-        SequenceI[] alseqs = null;
-        char alseq_gapchar = '-';
-        int alseq_l = 0;
-        if (alignment.getSequences().size() > 0)
-        {
-          alseqs = new SequenceI[alignment.getSequences().size()];
-          for (compbio.data.sequence.FastaSequence seq : alignment
-                  .getSequences())
-          {
-            alseqs[alseq_l++] = new Sequence(seq.getId(),
-                    seq.getSequence());
-          }
-          alseq_gapchar = alignment.getMetadata().getGapchar();
-
-        }
-        // add in the empty seqs.
-        if (emptySeqs.size() > 0)
-        {
-          SequenceI[] t_alseqs = new SequenceI[alseq_l + emptySeqs.size()];
-          // get width
-          int i, w = 0;
-          if (alseq_l > 0)
-          {
-            for (i = 0, w = alseqs[0].getLength(); i < alseq_l; i++)
-            {
-              if (w < alseqs[i].getLength())
-              {
-                w = alseqs[i].getLength();
-              }
-              t_alseqs[i] = alseqs[i];
-              alseqs[i] = null;
-            }
-          }
-          // check that aligned width is at least as wide as emptySeqs width.
-          int ow = w, nw = w;
-          for (i = 0, w = emptySeqs.size(); i < w; i++)
-          {
-            String[] es = emptySeqs.get(i);
-            if (es != null && es[1] != null)
-            {
-              int sw = es[1].length();
-              if (nw < sw)
-              {
-                nw = sw;
-              }
-            }
-          }
-          // make a gapped string.
-          StringBuffer insbuff = new StringBuffer(w);
-          for (i = 0; i < nw; i++)
-          {
-            insbuff.append(alseq_gapchar);
-          }
-          if (ow < nw)
-          {
-            for (i = 0; i < alseq_l; i++)
-            {
-              int sw = t_alseqs[i].getLength();
-              if (nw > sw)
-              {
-                // pad at end
-                alseqs[i].setSequence(t_alseqs[i].getSequenceAsString()
-                        + insbuff.substring(0, sw - nw));
-              }
-            }
-          }
-          for (i = 0, w = emptySeqs.size(); i < w; i++)
-          {
-            String[] es = emptySeqs.get(i);
-            if (es[1] == null)
-            {
-              t_alseqs[i + alseq_l] = new jalview.datamodel.Sequence(es[0],
-                      insbuff.toString(), 1, 0);
-            }
-            else
-            {
-              if (es[1].length() < nw)
-              {
-                t_alseqs[i + alseq_l] = new jalview.datamodel.Sequence(
-                        es[0],
-                        es[1] + insbuff.substring(0, nw - es[1].length()),
-                        1, 1 + es[1].length());
-              }
-              else
-              {
-                t_alseqs[i + alseq_l] = new jalview.datamodel.Sequence(
-                        es[0], es[1]);
-              }
-            }
-          }
-          alseqs = t_alseqs;
-        }
-        AlignmentOrder msaorder = new AlignmentOrder(alseqs);
-        // always recover the order - makes parseResult()'s life easier.
-        jalview.analysis.AlignmentSorter.recoverOrder(alseqs);
-        // account for any missing sequences
-        jalview.analysis.SeqsetUtils.deuniquify(SeqNames, alseqs);
-        return new Object[] { alseqs, msaorder };
-      }
-      return null;
-    }
-
-    /**
-     * mark subjob as cancelled and set result object appropriatly
-     */
-    void cancel()
-    {
-      cancelled = true;
-      subjobComplete = true;
-      alignment = null;
-    }
-
-    /**
-     * 
-     * @return boolean true if job can be submitted.
-     */
-    @Override
-    public boolean hasValidInput()
-    {
-      // TODO: get attributes for this MsaWS instance to check if it can do two
-      // sequence alignment.
-      if (seqs != null && seqs.size() >= 2) // two or more sequences is valid ?
-      {
-        return true;
-      }
-      return false;
-    }
-
-    StringBuffer jobProgress = new StringBuffer();
-
-    public void setStatus(String string)
-    {
-      jobProgress.setLength(0);
-      jobProgress.append(string);
-    }
-
-    @Override
-    public String getStatus()
-    {
-      return jobProgress.toString();
-    }
-
-    @Override
-    public boolean hasStatus()
-    {
-      return jobProgress != null;
-    }
-
-    /**
-     * @return the lastChunk
-     */
-    public long getLastChunk()
-    {
-      return lastChunk;
-    }
-
-    /**
-     * @param lastChunk
-     *          the lastChunk to set
-     */
-    public void setLastChunk(long lastChunk)
-    {
-      this.lastChunk = lastChunk;
-    }
-
-    String alignmentProgram = null;
-
-    public String getAlignmentProgram()
-    {
-      return alignmentProgram;
-    }
-
-    public boolean hasArguments()
-    {
-      return (arguments != null && arguments.size() > 0)
-              || (preset != null && preset instanceof JabaWsParamSet);
-    }
-
-    public List<Argument> getJabaArguments()
-    {
-      List<Argument> newargs = new ArrayList<>();
-      if (preset != null && preset instanceof JabaWsParamSet)
-      {
-        newargs.addAll(((JabaWsParamSet) preset).getjabaArguments());
-      }
-      if (arguments != null && arguments.size() > 0)
-      {
-        newargs.addAll(JabaParamStore.getJabafromJwsArgs(arguments));
-      }
-      return newargs;
-    }
-
-    /**
-     * add a progess header to status string containing presets/args used
-     */
-    public void addInitialStatus()
-    {
-      if (preset != null)
-      {
-        jobProgress.append("Using "
-                + (preset instanceof JabaPreset ? "Server" : "User")
-                + "Preset: " + preset.getName());
-        if (preset instanceof JabaWsParamSet)
-        {
-          for (Argument opt : getJabaArguments())
-          {
-            jobProgress.append(
-                    opt.getName() + " " + opt.getDefaultValue() + "\n");
-          }
-        }
-      }
-      if (arguments != null && arguments.size() > 0)
-      {
-        jobProgress.append("With custom parameters : \n");
-        // merge arguments with preset's own arguments.
-        for (Argument opt : getJabaArguments())
-        {
-          jobProgress.append(
-                  opt.getName() + " " + opt.getDefaultValue() + "\n");
-        }
-      }
-      jobProgress.append("\nJob Output:\n");
-    }
-
-    public boolean isPresetJob()
-    {
-      return preset != null && preset instanceof JabaPreset;
-    }
-
-    public Preset getServerPreset()
-    {
-      return (isPresetJob()) ? ((JabaPreset) preset).p : null;
-    }
-  }
-
   String alTitle; // name which will be used to form new alignment window.
 
   AlignmentI dataset; // dataset to which the new alignment will be
 
   // associated.
 
-  @SuppressWarnings("unchecked")
-  MsaWS server = null;
+  MultipleSequenceAlignmentI server = null;
 
   /**
    * set basic options for this (group) of Msa jobs
@@ -443,7 +74,8 @@ class MsaWSThread extends AWS2Thread implements WSClientI
    * @param presorder
    *          boolean
    */
-  private MsaWSThread(MsaWS server, String wsUrl, WebserviceInfo wsinfo,
+  private MsaWSThread(MultipleSequenceAlignmentI server, String wsUrl,
+          WebserviceInfo wsinfo,
           jalview.gui.AlignFrame alFrame, AlignmentView alview,
           String wsname, boolean subgaps, boolean presorder)
   {
@@ -454,7 +86,7 @@ class MsaWSThread extends AWS2Thread implements WSClientI
   }
 
   /**
-   * create one or more Msa jobs to align visible seuqences in _msa
+   * create one or more Msa jobs to align visible sequences in _msa
    * 
    * @param title
    *          String
@@ -467,7 +99,8 @@ class MsaWSThread extends AWS2Thread implements WSClientI
    * @param seqset
    *          Alignment
    */
-  MsaWSThread(MsaWS server2, WsParamSetI preset, List<ArgumentI> paramset,
+  public MsaWSThread(MultipleSequenceAlignmentI server2, WsParamSetI preset,
+          List<ArgumentI> paramset,
           String wsUrl, WebserviceInfo wsinfo,
           jalview.gui.AlignFrame alFrame, String wsname, String title,
           AlignmentView _msa, boolean subgaps, boolean presorder,
@@ -487,11 +120,11 @@ class MsaWSThread extends AWS2Thread implements WSClientI
       {
         if (j != 0)
         {
-          jobs[j] = new MsaWSJob(wsinfo.addJobPane(), conmsa[j]);
+          jobs[j] = new MsaWSJob(this, wsinfo.addJobPane(), conmsa[j]);
         }
         else
         {
-          jobs[j] = new MsaWSJob(0, conmsa[j]);
+          jobs[j] = new MsaWSJob(this, 0, conmsa[j]);
         }
         if (jobs[j].hasValidInput())
         {
@@ -525,12 +158,14 @@ class MsaWSThread extends AWS2Thread implements WSClientI
   @Override
   public boolean isCancellable()
   {
-    return true;
+    return server instanceof CancellableI;
   }
 
   @Override
   public void cancelJob()
   {
+    // TODO decide if when some jobs are not cancellable to shut down the thread
+    // anyhow ?
     if (!jobComplete && jobs != null)
     {
       boolean cancelled = true;
@@ -541,13 +176,11 @@ class MsaWSThread extends AWS2Thread implements WSClientI
           String cancelledMessage = "";
           try
           {
-            boolean cancelledJob = server.cancelJob(jobs[job].getJobId());
-            if (true) // cancelledJob || true)
+            CancellableI service = (CancellableI) server;
+            boolean cancelledJob = service.cancel((WsJob) jobs[job]);
+            if (cancelledJob)
             {
               // CANCELLED_JOB
-              // if the Jaba server indicates the job can't be cancelled, its
-              // because its running on the server's local execution engine
-              // so we just close the window anyway.
               cancelledMessage = "Job cancelled.";
               ((MsaWSJob) jobs[job]).cancel(); // TODO: refactor to avoid this
                                                // ugliness -
@@ -607,42 +240,8 @@ class MsaWSThread extends AWS2Thread implements WSClientI
     // this is standard code, but since the interface doesn't comprise of a
     // basic one that implements (getJobStatus, pullExecStatistics) we have to
     // repeat the code for all jw2s services.
-    j.setjobStatus(server.getJobStatus(job.getJobId()));
-    updateJobProgress(j);
-  }
-
-  /**
-   * 
-   * @param j
-   * @return true if more job progress data was available
-   * @throws Exception
-   */
-  protected boolean updateJobProgress(MsaWSJob j) throws Exception
-  {
-    StringBuffer response = j.jobProgress;
-    long lastchunk = j.getLastChunk();
-    boolean changed = false;
-    do
-    {
-      j.setLastChunk(lastchunk);
-      ChunkHolder chunk = server.pullExecStatistics(j.getJobId(),
-              lastchunk);
-      if (chunk != null)
-      {
-        changed |= chunk.getChunk().length() > 0;
-        response.append(chunk.getChunk());
-        lastchunk = chunk.getNextPosition();
-        try
-        {
-          Thread.sleep(50);
-        } catch (InterruptedException x)
-        {
-        }
-        ;
-      }
-      ;
-    } while (lastchunk >= 0 && j.getLastChunk() != lastchunk);
-    return changed;
+    server.updateStatus(j);
+    server.updateJobProgress(j);
   }
 
   @Override
@@ -671,26 +270,37 @@ class MsaWSThread extends AWS2Thread implements WSClientI
     if (j.seqs == null || j.seqs.size() == 0)
     {
       // special case - selection consisted entirely of empty sequences...
-      j.setjobStatus(JobStatus.FINISHED);
+      j.setState(JobState.FINISHED);
       j.setStatus(MessageManager.getString("label.empty_alignment_job"));
     }
     try
     {
       j.addInitialStatus(); // list the presets/parameters used for the job in
                             // status
-      if (j.isPresetJob())
+      try
       {
-        j.setJobId(server.presetAlign(j.seqs, j.getServerPreset()));
-      }
-      else if (j.hasArguments())
-      {
-        j.setJobId(server.customAlign(j.seqs, j.getJabaArguments()));
-      }
-      else
+        JobId jobHandle = server.align(j.seqs, j.getPreset(),
+                j.getArguments());
+        if (jobHandle != null)
+        {
+          j.setJobHandle(jobHandle);
+        }
+
+      } catch (Throwable throwable)
       {
-        j.setJobId(server.align(j.seqs));
+        if (!server.handleSubmitError(throwable, j, wsInfo))
+        {
+          if (throwable instanceof Exception)
+          {
+            throw ((Exception) throwable);
+          }
+          if (throwable instanceof Error)
+          {
+            throw ((Error) throwable);
+          }
+        }
       }
-
+      ///// generic
       if (j.getJobId() != null)
       {
         j.setSubmitted(true);
@@ -705,40 +315,11 @@ class MsaWSThread extends AWS2Thread implements WSClientI
                 new String[]
                 { WsUrl }));
       }
-    } catch (compbio.metadata.UnsupportedRuntimeException _lex)
-    {
-      lex = _lex;
-      wsInfo.appendProgressText(MessageManager.formatMessage(
-              "info.job_couldnt_be_run_server_doesnt_support_program",
-              new String[]
-              { _lex.getMessage() }));
-      wsInfo.warnUser(_lex.getMessage(),
-              MessageManager.getString("warn.service_not_supported"));
-      wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_SERVERERROR);
-      wsInfo.setStatus(j.getJobnum(),
-              WebserviceInfo.STATE_STOPPED_SERVERERROR);
-    } catch (compbio.metadata.LimitExceededException _lex)
-    {
-      lex = _lex;
-      wsInfo.appendProgressText(MessageManager.formatMessage(
-              "info.job_couldnt_be_run_exceeded_hard_limit", new String[]
-              { _lex.getMessage() }));
-      wsInfo.warnUser(_lex.getMessage(),
-              MessageManager.getString("warn.input_is_too_big"));
-      wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_ERROR);
-      wsInfo.setStatus(j.getJobnum(), WebserviceInfo.STATE_STOPPED_ERROR);
-    } catch (compbio.metadata.WrongParameterException _lex)
-    {
-      lex = _lex;
-      wsInfo.warnUser(_lex.getMessage(),
-              MessageManager.getString("warn.invalid_job_param_set"));
-      wsInfo.appendProgressText(MessageManager.formatMessage(
-              "info.job_couldnt_be_run_incorrect_param_setting",
-              new String[]
-              { _lex.getMessage() }));
-      wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_ERROR);
-      wsInfo.setStatus(j.getJobnum(), WebserviceInfo.STATE_STOPPED_ERROR);
-    } catch (Error e)
+    }
+    //// jabaws specific
+
+    //// generic
+    catch (Error e)
     {
       // For unexpected errors
       System.err.println(WebServiceName
@@ -794,7 +375,7 @@ class MsaWSThread extends AWS2Thread implements WSClientI
           {
             try
             {
-              jpchanged = updateJobProgress(msjob);
+              jpchanged = server.updateJobProgress(msjob);
               jpex = false;
               if (jpchanged)
               {
@@ -835,25 +416,18 @@ class MsaWSThread extends AWS2Thread implements WSClientI
             System.out.println("*** End of status");
 
           }
+          ///// jabaws specific(ish) Get Result from Server when available
           try
           {
-            msjob.alignment = server.getResult(msjob.getJobId());
-          } catch (compbio.metadata.ResultNotAvailableException e)
-          {
-            // job has failed for some reason - probably due to invalid
-            // parameters
-            Cache.log.debug(
-                    "Results not available for finished job - marking as broken job.",
-                    e);
-            msjob.jobProgress.append(
-                    "\nResult not available. Probably due to invalid input or parameter settings. Server error message below:\n\n"
-                            + e.getLocalizedMessage());
-            msjob.setjobStatus(JobStatus.FAILED);
+            msjob.alignment = server.getAlignmentFor(msjob.getJobHandle());
           } catch (Exception e)
           {
-            Cache.log.error("Couldn't get Alignment for job.", e);
-            // TODO: Increment count and retry ?
-            msjob.setjobStatus(JobStatus.UNDEFINED);
+            if (!server.handleCollectionException(e, msjob, wsInfo))
+            {
+              Cache.log.error("Couldn't get Alignment for job.", e);
+              // TODO: Increment count and retry ?
+              msjob.setState(JobState.SERVERERROR);
+            }
           }
         }
         finalState.updateJobPanelState(wsInfo, OutputHeader, jobs[j]);
@@ -861,19 +435,6 @@ class MsaWSThread extends AWS2Thread implements WSClientI
                 && jobs[j].hasResults())
         {
           results++;
-          compbio.data.sequence.Alignment alignment = ((MsaWSJob) jobs[j]).alignment;
-          if (alignment != null)
-          {
-            // server.close(jobs[j].getJobnum());
-            // wsInfo.appendProgressText(jobs[j].getJobnum(),
-            // "\nAlignment Object Method Notes\n");
-            // wsInfo.appendProgressText(jobs[j].getJobnum(),
-            // "Calculated with
-            // "+alignment.getMetadata().getProgram().toString());
-            // JBPNote The returned files from a webservice could be
-            // hidden behind icons in the monitor window that,
-            // when clicked, pop up their corresponding data
-          }
         }
       }
     } catch (Exception ex)
diff --git a/src/jalview/ws/gui/WsJob.java b/src/jalview/ws/gui/WsJob.java
new file mode 100644 (file)
index 0000000..05379d7
--- /dev/null
@@ -0,0 +1,176 @@
+/**
+ * 
+ */
+package jalview.ws.gui;
+
+import jalview.ws.AWsJob;
+
+/**
+ * Bean that holds state for a job
+ * 
+ * @author jprocter
+ *
+ */
+public class WsJob extends AWsJob
+{
+
+  public enum JobState
+  {
+    INVALID, READY, SUBMITTED, QUEUED, RUNNING, FINISHED, BROKEN, FAILED,
+    UNKNOWN, SERVERERROR, CANCELLED;
+  };
+
+  JobState state = JobState.UNKNOWN;
+
+  boolean hasResults = false, validInput = false;
+  /* (non-Javadoc)
+   * @see jalview.ws.AWsJob#hasResults()
+   */
+  @Override
+  public boolean hasResults()
+  {
+    return hasResults;
+  }
+
+  /* (non-Javadoc)
+   * @see jalview.ws.AWsJob#hasValidInput()
+   */
+  @Override
+  public boolean hasValidInput()
+  {
+    return validInput;
+  }
+
+  /* (non-Javadoc)
+   * @see jalview.ws.AWsJob#isRunning()
+   */
+  @Override
+  public boolean isRunning()
+  {
+    return JobState.RUNNING.equals(state);
+  }
+
+  /* (non-Javadoc)
+   * @see jalview.ws.AWsJob#isQueued()
+   */
+  @Override
+  public boolean isQueued()
+  {
+    return JobState.QUEUED.equals(state);
+  }
+
+  /* (non-Javadoc)
+   * @see jalview.ws.AWsJob#isFinished()
+   */
+  @Override
+  public boolean isFinished()
+  {
+    // TODO isSubjobComplete and finished flags mean same thing ?
+    return JobState.FINISHED.equals(state);
+  }
+
+  /* (non-Javadoc)
+   * @see jalview.ws.AWsJob#isFailed()
+   */
+  @Override
+  public boolean isFailed()
+  {
+    return JobState.FAILED.equals(state);
+  }
+
+  /* (non-Javadoc)
+   * @see jalview.ws.AWsJob#isBroken()
+   */
+  @Override
+  public boolean isBroken()
+  {
+    return JobState.BROKEN.equals(state);
+  }
+
+  /* (non-Javadoc)
+   * @see jalview.ws.AWsJob#isServerError()
+   */
+  @Override
+  public boolean isServerError()
+  {
+    return JobState.SERVERERROR.equals(state);
+  }
+
+  /* (non-Javadoc)
+   * @see jalview.ws.AWsJob#hasStatus()
+   */
+  @Override
+  public boolean hasStatus()
+  {
+    return status != null && status.length() > 0;
+  }
+
+  /* (non-Javadoc)
+   * @see jalview.ws.AWsJob#getStatus()
+   */
+  @Override
+  public String getStatus()
+  {
+    return status;
+  }
+
+  /* (non-Javadoc)
+   * @see jalview.ws.AWsJob#hasResponse()
+   */
+  @Override
+  public boolean hasResponse()
+  {
+    // TODO Auto-generated method stub
+    return hasStatus();
+  }
+
+  /* (non-Javadoc)
+   * @see jalview.ws.AWsJob#clearResponse()
+   */
+  @Override
+  public void clearResponse()
+  {
+    status = "";
+  }
+
+  /* (non-Javadoc)
+   * @see jalview.ws.AWsJob#getState()
+   */
+  @Override
+  public String getState()
+  {
+    return state.toString();
+  }
+
+  /**
+   * @return the current JobState
+   */
+  public JobState getJobState()
+  {
+    return state;
+  }
+
+  /**
+   * set the job state
+   * 
+   * @param state
+   */
+  public void setState(JobState state)
+  {
+    this.state = state;
+  }
+
+  String status = "";
+
+  /**
+   * Set the log for this job
+   * 
+   * @parag log
+   */
+  public void setStatus(String log)
+  {
+    status = log;
+
+  }
+
+}
index 981b288..b6a18dd 100644 (file)
@@ -100,4 +100,9 @@ public class JabaPreset implements WsParamSetI
     throw new Error(MessageManager
             .getString("error.cannot_set_params_for_ws_preset"));
   }
+
+  public Preset getJabaPreset()
+  {
+    return p;
+  }
 }
index dccdcea..a545d5d 100644 (file)
@@ -27,6 +27,8 @@ import jalview.gui.Desktop;
 import jalview.gui.JvOptionPane;
 import jalview.gui.JvSwingUtils;
 import jalview.util.MessageManager;
+import jalview.ws.api.MultipleSequenceAlignmentI;
+import jalview.ws.gui.MsaWSThread;
 import jalview.ws.jws2.jabaws2.Jws2Instance;
 import jalview.ws.params.ArgumentI;
 import jalview.ws.params.WsParamSetI;
@@ -41,8 +43,6 @@ import javax.swing.JMenu;
 import javax.swing.JMenuItem;
 import javax.swing.ToolTipManager;
 
-import compbio.data.msa.MsaWS;
-
 /**
  * DOCUMENT ME!
  * 
@@ -52,9 +52,11 @@ import compbio.data.msa.MsaWS;
 public class MsaWSClient extends Jws2Client
 {
   /**
-   * server is a WSDL2Java generated stub for an archetypal MsaWSI service.
+   * server is a proxy class implementing the core methods for submitting,
+   * monitoring and retrieving results from a multiple sequence alignment
+   * service
    */
-  MsaWS server;
+  MultipleSequenceAlignmentI server;
 
   public MsaWSClient(Jws2Instance sh, String altitle,
           jalview.datamodel.AlignmentView msa, boolean submitGaps,
@@ -104,7 +106,7 @@ public class MsaWSClient extends Jws2Client
       return;
     }
 
-    if (!(sh.service instanceof MsaWS))
+    if (!(sh.getEndpoint() instanceof MultipleSequenceAlignmentI))
     {
       // redundant at mo - but may change
       JvOptionPane.showMessageDialog(Desktop.desktop,
@@ -117,7 +119,8 @@ public class MsaWSClient extends Jws2Client
 
       return;
     }
-    server = (MsaWS) sh.service;
+    serviceHandle = sh;
+    server = (MultipleSequenceAlignmentI) sh.getEndpoint();
     if ((wsInfo = setWebService(sh, false)) == null)
     {
       JvOptionPane.showMessageDialog(Desktop.desktop, MessageManager
diff --git a/src/jalview/ws/jws2/jabaws2/JabawsMsaInstance.java b/src/jalview/ws/jws2/jabaws2/JabawsMsaInstance.java
new file mode 100644 (file)
index 0000000..80de5b2
--- /dev/null
@@ -0,0 +1,285 @@
+package jalview.ws.jws2.jabaws2;
+
+import jalview.bin.Cache;
+import jalview.datamodel.AlignmentI;
+import jalview.datamodel.Sequence;
+import jalview.datamodel.SequenceI;
+import jalview.gui.WebserviceInfo;
+import jalview.util.MessageManager;
+import jalview.ws.api.CancellableI;
+import jalview.ws.api.JobId;
+import jalview.ws.api.MultipleSequenceAlignmentI;
+import jalview.ws.gui.WsJob;
+import jalview.ws.gui.WsJob.JobState;
+import jalview.ws.jws2.JabaParamStore;
+import jalview.ws.jws2.JabaPreset;
+import jalview.ws.jws2.dm.JabaWsParamSet;
+import jalview.ws.params.ArgumentI;
+import jalview.ws.params.InvalidArgumentException;
+import jalview.ws.params.WsParamSetI;
+
+import java.io.IOError;
+import java.rmi.ServerError;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import compbio.data.msa.MsaWS;
+import compbio.data.sequence.Alignment;
+import compbio.data.sequence.FastaSequence;
+import compbio.metadata.Argument;
+import compbio.metadata.ChunkHolder;
+import compbio.metadata.JobStatus;
+import compbio.metadata.Preset;
+import compbio.metadata.ResultNotAvailableException;
+
+public class JabawsMsaInstance
+        implements MultipleSequenceAlignmentI, CancellableI
+{
+  /**
+   * our service instance handler generated by the discoverer
+   */
+  Jws2Instance our;
+
+  MsaWS<?> service;
+
+  @Override
+  public JobId align(List<SequenceI> toalign, WsParamSetI parameters,
+          List<ArgumentI> arguments) throws Throwable
+  {
+    List<compbio.data.sequence.FastaSequence> seqs = new ArrayList<>();
+    for (SequenceI seq : toalign)
+    {
+      seqs.add(new FastaSequence(seq.getName(), seq.getSequenceAsString()));
+    }
+    String jobid = null;
+    if (parameters != null)
+    {
+      if (parameters instanceof JabaPreset)
+      {
+        jobid = service.presetAlign(seqs,
+                ((JabaPreset) parameters).getJabaPreset());
+      }
+      else
+      {
+        jobid = service.customAlign(seqs, JabaParamStore
+                .getJabafromJwsArgs(parameters.getArguments()));
+      }
+    }
+    else if (arguments != null && arguments.size() > 0)
+    {
+      jobid = service.customAlign(seqs,
+              JabaParamStore.getJabafromJwsArgs(arguments));
+    }
+    else
+    {
+      jobid = service.align(seqs);
+    }
+
+    if (jobid == null)
+    {
+      return null;
+    }
+    return new JobId(our.getServiceType(), our.getName(), jobid);
+  }
+
+  @Override
+  public AlignmentI getAlignmentFor(JobId jobId)
+          throws InvalidArgumentException, ServerError, IOError
+  {
+    Alignment alignment = null;
+    try
+    {
+      alignment = service.getResult(jobId.getJobId());
+    } catch (ResultNotAvailableException rnotav)
+    {
+
+      // TODO - migrate JABA exception
+      // throw new ServerError("Couldn't get result for job",rnotav);
+    }
+    SequenceI[] alseqs;
+    int alseq_l = 0;
+    if (alignment.getSequences().size() == 0)
+    {
+      return null;
+    }
+
+    alseqs = new SequenceI[alignment.getSequences().size()];
+    for (compbio.data.sequence.FastaSequence seq : alignment.getSequences())
+    {
+      alseqs[alseq_l++] = new Sequence(seq.getId(), seq.getSequence());
+    }
+    AlignmentI jv_al = new jalview.datamodel.Alignment(alseqs);
+    jv_al.setGapCharacter(alignment.getMetadata().getGapchar());
+    return jv_al;
+
+  }
+
+  public JabawsMsaInstance(Jws2Instance handle)
+  {
+    our = handle;
+    service = (MsaWS<?>) our.service;
+  }
+
+  @Override
+  public boolean cancel(WsJob job)
+  {
+    service.cancelJob(job.getJobId());
+    // if the Jaba server indicates the job can't be cancelled, its
+    // because its running on the server's local execution engine
+    // so we just close the window anyway.
+
+    return true;
+  }
+
+  Map<JobStatus, JobState> jwsState = new HashMap<>();
+  {
+    jwsState.put(JobStatus.CANCELLED, JobState.CANCELLED);
+    jwsState.put(JobStatus.COLLECTED, JobState.FINISHED);
+    jwsState.put(JobStatus.FAILED, JobState.FAILED);
+    jwsState.put(JobStatus.FINISHED, JobState.FINISHED);
+    jwsState.put(JobStatus.PENDING, JobState.QUEUED);
+    jwsState.put(JobStatus.RUNNING, JobState.RUNNING);
+    jwsState.put(JobStatus.STARTED, JobState.RUNNING);
+    jwsState.put(JobStatus.SUBMITTED, JobState.SUBMITTED);
+    jwsState.put(JobStatus.UNDEFINED, JobState.UNKNOWN);
+  }
+  @Override
+  public void updateStatus(WsJob job)
+  {
+    JobStatus jwsstatus = service.getJobStatus(job.getJobId());
+    job.setState(jwsState.get(jwsstatus));
+  }
+
+  @Override
+  public boolean updateJobProgress(WsJob job) throws Exception
+  {
+    StringBuilder response = new StringBuilder(job.getStatus());
+    long lastchunk = job.getNextChunk();
+    boolean changed = false;
+    do
+    {
+      ChunkHolder chunk = service.pullExecStatistics(job.getJobId(),
+              lastchunk);
+      if (chunk != null)
+      {
+        changed |= chunk.getChunk().length() > 0;
+        response.append(chunk.getChunk());
+        lastchunk = chunk.getNextPosition();
+        try
+        {
+          Thread.sleep(50);
+        } catch (InterruptedException x)
+        {
+        }
+        ;
+      }
+      ;
+      job.setnextChunk(lastchunk);
+    } while (lastchunk >= 0 && job.getNextChunk() != lastchunk);
+    if (job instanceof WsJob)
+    {
+      // TODO decide if WsJob will be the bean for all ng-webservices
+      job.setStatus(response.toString());
+    }
+    return changed;
+  }
+
+  public boolean isPresetJob(WsJob job)
+  {
+    return job.getPreset() != null && job.getPreset() instanceof JabaPreset;
+  }
+
+  public Preset getServerPreset(WsJob job)
+  {
+    return (isPresetJob(job))
+            ? ((JabaPreset) job.getPreset()).getJabaPreset()
+            : null;
+  }
+
+  public List<Argument> getJabaArguments(WsParamSetI preset)
+  {
+    List<Argument> newargs = new ArrayList<>();
+    if (preset != null)
+    {
+      if (preset instanceof JabaWsParamSet)
+      {
+        newargs.addAll(((JabaWsParamSet) preset).getjabaArguments());
+      }
+      else
+      {
+        newargs.addAll(
+                JabaParamStore.getJabafromJwsArgs(preset.getArguments()));
+      }
+    }
+    return newargs;
+  }
+
+  @Override
+  public boolean handleSubmitError(Throwable _lex, WsJob j,
+          WebserviceInfo wsInfo) throws Exception, Error
+  {
+    if (_lex instanceof compbio.metadata.UnsupportedRuntimeException)
+    {
+      wsInfo.appendProgressText(MessageManager.formatMessage(
+              "info.job_couldnt_be_run_server_doesnt_support_program",
+              new String[]
+              { _lex.getMessage() }));
+      wsInfo.warnUser(_lex.getMessage(),
+              MessageManager.getString("warn.service_not_supported"));
+      wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_SERVERERROR);
+      wsInfo.setStatus(j.getJobnum(),
+              WebserviceInfo.STATE_STOPPED_SERVERERROR);
+      return true;
+    }
+    if (_lex instanceof compbio.metadata.LimitExceededException)
+    {
+      wsInfo.appendProgressText(MessageManager.formatMessage(
+              "info.job_couldnt_be_run_exceeded_hard_limit", new String[]
+              { _lex.getMessage() }));
+      wsInfo.warnUser(_lex.getMessage(),
+              MessageManager.getString("warn.input_is_too_big"));
+      wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_ERROR);
+      wsInfo.setStatus(j.getJobnum(), WebserviceInfo.STATE_STOPPED_ERROR);
+      return true;
+    }
+    if (_lex instanceof compbio.metadata.WrongParameterException)
+    {
+      wsInfo.warnUser(_lex.getMessage(),
+              MessageManager.getString("warn.invalid_job_param_set"));
+      wsInfo.appendProgressText(MessageManager.formatMessage(
+              "info.job_couldnt_be_run_incorrect_param_setting",
+              new String[]
+              { _lex.getMessage() }));
+      wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_ERROR);
+      wsInfo.setStatus(j.getJobnum(), WebserviceInfo.STATE_STOPPED_ERROR);
+      return true;
+    }
+    // pass on to generic error handler
+    return false;
+  }
+
+  @Override
+  public boolean handleCollectionException(Exception ex, WsJob msjob,
+          WebserviceInfo wsInfo)
+  {
+    if (ex instanceof compbio.metadata.ResultNotAvailableException)
+    {
+      // job has failed for some reason - probably due to invalid
+      // parameters
+      Cache.log.debug(
+              "Results not available for finished job - marking as broken job.",
+              ex);
+      String status = msjob.getStatus();
+
+      msjob.setStatus(status
+              +
+              "\nResult not available. Probably due to invalid input or parameter settings. Server error message below:\n\n"
+                      + ex.getLocalizedMessage());
+      msjob.setState(WsJob.JobState.BROKEN);
+      return true;
+    }
+    return false;
+  }
+}
index 3bff20b..01c13a8 100644 (file)
@@ -239,4 +239,26 @@ public class Jws2Instance extends ServiceWithParameters
       paramStore = new JabaParamStore(this, userParameterStore);
     }
   }
+
+  /**
+   * an object that implements one or more interfaces in jalview.ws.api
+   * 
+   * @return
+   */
+  public Object getEndpoint()
+  {
+    if (aaui!=null) {
+      // TODO complete
+      return null;
+    } else {
+      if (service instanceof MsaWS<?>)
+      {
+      return new JabawsMsaInstance(this);
+    } else {
+        // TODO complete
+        // service is for sequence analysis
+        return null;
+    }
+  }
+}
 }