JAL-1379 refactor Jws2 service client so dynamic services can be of any type of JABA...
authorJim Procter <jprocter@dundee.ac.uk>
Tue, 10 Jun 2014 16:20:49 +0000 (17:20 +0100)
committerJim Procter <jprocter@dundee.ac.uk>
Tue, 10 Jun 2014 16:20:49 +0000 (17:20 +0100)
src/jalview/ws/jws2/AAConClient.java
src/jalview/ws/jws2/AADisorderClient.java
src/jalview/ws/jws2/AbstractJabaCalcWorker.java [new file with mode: 0644]
src/jalview/ws/jws2/JabawsAlignCalcWorker.java [deleted file]
src/jalview/ws/jws2/JabawsCalcWorker.java
src/jalview/ws/jws2/Jws2Client.java
src/jalview/ws/jws2/RNAalifoldClient.java
src/jalview/ws/jws2/SequenceAnnotationWSClient.java

index 676b3ba..66338b1 100644 (file)
@@ -34,10 +34,11 @@ import java.util.Map;
 import java.util.Set;
 import java.util.TreeSet;
 
+import compbio.data.sequence.FastaSequence;
 import compbio.data.sequence.Score;
 import compbio.metadata.Argument;
 
-public class AAConClient extends JabawsAlignCalcWorker
+public class AAConClient extends JabawsCalcWorker
 {
 
   public AAConClient(Jws2Instance service, AlignFrame alignFrame,
@@ -102,6 +103,12 @@ public class AAConClient extends JabawsAlignCalcWorker
   }
 
   @Override
+  boolean checkValidInputSeqs(boolean dynamic, List<FastaSequence> seqs)
+  {
+    return (seqs.size() > 1);
+  }
+
+  @Override
   public String getCalcId()
   {
     return CALC_ID;
index 34969d1..83eebdf 100644 (file)
@@ -40,6 +40,7 @@ import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 
+import compbio.data.sequence.FastaSequence;
 import compbio.data.sequence.Range;
 import compbio.data.sequence.Score;
 import compbio.data.sequence.ScoreManager.ScoreHolder;
@@ -82,6 +83,12 @@ public class AADisorderClient extends JabawsCalcWorker implements
     return "Submitting amino acid sequences for disorder prediction.";
   }
 
+  @Override
+  boolean checkValidInputSeqs(boolean dynamic, List<FastaSequence> seqs)
+  {
+    return (seqs.size() > 0);
+  }
+
   private static Map<String, Map<String, String[]>> featureMap;
 
   private static Map<String, Map<String, Map<String, Object>>> annotMap;
@@ -374,4 +381,11 @@ public class AADisorderClient extends JabawsCalcWorker implements
     }
   }
 
+  @Override
+  public String getCalcId()
+  {
+    // Disorder predictions are not dynamically updated so we return null
+    return null;
+  }
+
 }
diff --git a/src/jalview/ws/jws2/AbstractJabaCalcWorker.java b/src/jalview/ws/jws2/AbstractJabaCalcWorker.java
new file mode 100644 (file)
index 0000000..a7cc5b6
--- /dev/null
@@ -0,0 +1,599 @@
+package jalview.ws.jws2;
+
+import jalview.analysis.AlignSeq;
+import jalview.analysis.SeqsetUtils;
+import jalview.api.AlignViewportI;
+import jalview.api.AlignmentViewPanel;
+import jalview.datamodel.AlignmentAnnotation;
+import jalview.datamodel.AlignmentI;
+import jalview.datamodel.AnnotatedCollectionI;
+import jalview.datamodel.SequenceI;
+import jalview.gui.AlignFrame;
+import jalview.gui.IProgressIndicator;
+import jalview.workers.AlignCalcWorker;
+import jalview.ws.jws2.dm.AAConSettings;
+import jalview.ws.jws2.dm.JabaWsParamSet;
+import jalview.ws.jws2.jabaws2.Jws2Instance;
+import jalview.ws.params.WsParamSetI;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import compbio.data.sequence.FastaSequence;
+import compbio.metadata.Argument;
+import compbio.metadata.ChunkHolder;
+import compbio.metadata.JobStatus;
+import compbio.metadata.JobSubmissionException;
+import compbio.metadata.Option;
+import compbio.metadata.ResultNotAvailableException;
+
+public abstract class AbstractJabaCalcWorker extends AlignCalcWorker
+{
+
+  protected Jws2Instance service;
+
+  protected WsParamSetI preset;
+
+  protected List<Argument> arguments;
+
+  protected IProgressIndicator guiProgress;
+
+  protected boolean submitGaps = true;
+
+  /**
+   * Recover any existing parameters for this service
+   */
+  protected void initViewportParams()
+  {
+    if (getCalcId() != null)
+    {
+      ((jalview.gui.AlignViewport) alignViewport).setCalcIdSettingsFor(
+              getCalcId(),
+              new AAConSettings(true, service, this.preset,
+                      (arguments != null) ? JabaParamStore
+                              .getJwsArgsfromJaba(arguments) : null), true);
+    }
+  }
+
+  /**
+   * 
+   * @return null or a string used to recover all annotation generated by this
+   *         worker
+   */
+  public abstract String getCalcId();
+
+  public WsParamSetI getPreset()
+  {
+    return preset;
+  }
+
+  public List<Argument> getArguments()
+  {
+    return arguments;
+  }
+
+  /**
+   * reconfigure and restart the AAConClient. This method will spawn a new
+   * thread that will wait until any current jobs are finished, modify the
+   * parameters and restart the conservation calculation with the new values.
+   * 
+   * @param newpreset
+   * @param newarguments
+   */
+  public void updateParameters(final WsParamSetI newpreset,
+          final List<Argument> newarguments)
+  {
+    preset = newpreset;
+    arguments = newarguments;
+    calcMan.startWorker(this);
+    initViewportParams();
+  }
+
+  public List<Option> getJabaArguments()
+  {
+    List<Option> newargs = new ArrayList<Option>();
+    if (preset != null && preset instanceof JabaWsParamSet)
+    {
+      newargs.addAll(((JabaWsParamSet) preset).getjabaArguments());
+    }
+    if (arguments != null && arguments.size() > 0)
+    {
+      for (Argument rg : arguments)
+      {
+        if (Option.class.isAssignableFrom(rg.getClass()))
+        {
+          newargs.add((Option) rg);
+        }
+      }
+    }
+    return newargs;
+  }
+
+  protected boolean alignedSeqs = true;
+
+  protected boolean nucleotidesAllowed = false;
+
+  protected boolean proteinAllowed = false;
+
+  /**
+   * record sequences for mapping result back to afterwards
+   */
+  protected boolean bySequence = false;
+
+  protected Map<String, SequenceI> seqNames;
+
+  protected boolean[] gapMap;
+
+  int realw;
+
+  protected int start;
+
+  int end;
+
+  public AbstractJabaCalcWorker(AlignViewportI alignViewport,
+          AlignmentViewPanel alignPanel)
+  {
+    super(alignViewport, alignPanel);
+  }
+
+  public AbstractJabaCalcWorker(Jws2Instance service,
+          AlignFrame alignFrame, WsParamSetI preset, List<Argument> paramset)
+  {
+    this(alignFrame.getCurrentView(), alignFrame.alignPanel);
+    this.guiProgress = alignFrame;
+    this.preset = preset;
+    this.arguments = paramset;
+    this.service = service;
+  }
+
+  /**
+   * 
+   * @return true if the submission thread should attempt to submit data
+   */
+  abstract boolean hasService();
+
+  volatile String rslt = "JOB NOT DEFINED";
+
+  @Override
+  public void run()
+  {
+    if (!hasService())
+    {
+      return;
+    }
+    long progressId = -1;
+
+    int serverErrorsLeft = 3;
+
+    StringBuffer msg = new StringBuffer();
+    try
+    {
+      if (checkDone())
+      {
+        return;
+      }
+      List<compbio.data.sequence.FastaSequence> seqs = getInputSequences(
+              alignViewport.getAlignment(),
+              bySequence ? alignViewport.getSelectionGroup() : null);
+
+      if (seqs == null || !checkValidInputSeqs(true, seqs))
+      {
+        calcMan.workerComplete(this);
+        return;
+      }
+
+      AlignmentAnnotation[] aa = alignViewport.getAlignment()
+              .getAlignmentAnnotation();
+      if (guiProgress != null)
+      {
+        guiProgress.setProgressBar("JABA " + getServiceActionText(),
+                progressId = System.currentTimeMillis());
+      }
+      rslt = submitToService(seqs);
+
+      boolean finished = false;
+      long rpos = 0;
+      do
+      {
+        JobStatus status = getJobStatus(rslt);
+        if (status.equals(JobStatus.FINISHED))
+        {
+          finished = true;
+        }
+        if (calcMan.isPending(this) && isInteractiveUpdate())
+        {
+          finished = true;
+          // cancel this job and yield to the new job
+          try
+          {
+            if (cancelJob(rslt))
+            {
+              System.err.println("Cancelled AACon job: " + rslt);
+            }
+            else
+            {
+              System.err.println("FAILED TO CANCEL AACon job: " + rslt);
+            }
+
+          } catch (Exception x)
+          {
+
+          }
+          rslt = "CANCELLED JOB";
+          return;
+        }
+        long cpos;
+        ChunkHolder stats = null;
+        do
+        {
+          cpos = rpos;
+          boolean retry = false;
+          do
+          {
+            try
+            {
+              stats = pullExecStatistics(rslt, rpos);
+            } catch (Exception x)
+            {
+
+              if (x.getMessage().contains(
+                      "Position in a file could not be negative!"))
+              {
+                // squash index out of bounds exception- seems to happen for
+                // disorder predictors which don't (apparently) produce any
+                // progress information and JABA server throws an exception
+                // because progress length is -1.
+                stats = null;
+              }
+              else
+              {
+                if (--serverErrorsLeft > 0)
+                {
+                  retry = true;
+                  try
+                  {
+                    Thread.sleep(200);
+                  } catch (InterruptedException q)
+                  {
+                  }
+                  ;
+                }
+                else
+                {
+                  throw x;
+                }
+              }
+            }
+          } while (retry);
+          if (stats != null)
+          {
+            System.out.print(stats.getChunk());
+            msg.append(stats);
+            rpos = stats.getNextPosition();
+          }
+        } while (stats != null && rpos > cpos);
+
+        if (!finished && status.equals(JobStatus.FAILED))
+        {
+          try
+          {
+            Thread.sleep(200);
+          } catch (InterruptedException x)
+          {
+          }
+          ;
+        }
+      } while (!finished);
+      if (serverErrorsLeft > 0)
+      {
+        try
+        {
+          Thread.sleep(200);
+        } catch (InterruptedException x)
+        {
+        }
+        if (collectAnnotationResultsFor(rslt))
+        {
+          jalview.bin.Cache.log
+                  .debug("Updating result annotation from Job " + rslt
+                          + " at " + service.getUri());
+          updateResultAnnotation(true);
+          ap.adjustAnnotationHeight();
+        }
+      }
+    }
+
+    catch (JobSubmissionException x)
+    {
+
+      System.err.println("submission error with " + getServiceActionText()
+              + " :");
+      x.printStackTrace();
+      calcMan.workerCannotRun(this);
+    } catch (ResultNotAvailableException x)
+    {
+      System.err.println("collection error:\nJob ID: " + rslt);
+      x.printStackTrace();
+      calcMan.workerCannotRun(this);
+
+    } catch (OutOfMemoryError error)
+    {
+      calcMan.workerCannotRun(this);
+
+      // consensus = null;
+      // hconsensus = null;
+      ap.raiseOOMWarning(getServiceActionText(), error);
+    } catch (Exception x)
+    {
+      calcMan.workerCannotRun(this);
+
+      // consensus = null;
+      // hconsensus = null;
+      System.err
+              .println("Blacklisting worker due to unexpected exception:");
+      x.printStackTrace();
+    } finally
+    {
+
+      calcMan.workerComplete(this);
+      if (ap != null)
+      {
+        calcMan.workerComplete(this);
+        if (guiProgress != null && progressId != -1)
+        {
+          guiProgress.setProgressBar("", progressId);
+        }
+        ap.paintAlignment(true);
+      }
+      if (msg.length() > 0)
+      {
+        // TODO: stash message somewhere in annotation or alignment view.
+        // code below shows result in a text box popup
+        /*
+         * jalview.gui.CutAndPasteTransfer cap = new
+         * jalview.gui.CutAndPasteTransfer(); cap.setText(msg.toString());
+         * jalview.gui.Desktop.addInternalFrame(cap,
+         * "Job Status for "+getServiceActionText(), 600, 400);
+         */
+      }
+    }
+
+  }
+
+  /**
+   * validate input for dynamic/non-dynamic update context
+   * @param dynamic
+   * @param seqs
+   * @return true if input is valid
+   */
+  abstract boolean checkValidInputSeqs(boolean dynamic, List<FastaSequence> seqs);
+
+  abstract String submitToService(
+          List<compbio.data.sequence.FastaSequence> seqs)
+          throws JobSubmissionException;
+
+  abstract boolean cancelJob(String rslt) throws Exception;
+
+  abstract JobStatus getJobStatus(String rslt) throws Exception;
+
+  abstract ChunkHolder pullExecStatistics(String rslt, long rpos);
+
+  abstract boolean collectAnnotationResultsFor(String rslt)
+          throws ResultNotAvailableException;
+
+  public void cancelCurrentJob()
+  {
+    try
+    {
+      String id = rslt;
+      if (cancelJob(rslt))
+      {
+        System.err.println("Cancelled job "+id);
+      }
+      else 
+      {
+        System.err.println("Job "+id+" couldn't be cancelled.");
+      }
+    } catch (Exception q)
+    {
+      q.printStackTrace();
+    }
+  }
+
+  /**
+   * Interactive updating. Analysis calculations that work on the currently
+   * displayed alignment data should cancel existing jobs when the input data
+   * has changed.
+   * 
+   * @return true if a running job should be cancelled because new input data is
+   *         available for analysis
+   */
+  abstract boolean isInteractiveUpdate();
+
+  public List<FastaSequence> getInputSequences(AlignmentI alignment,
+          AnnotatedCollectionI inputSeqs)
+  {
+    if (alignment == null || alignment.getWidth() <= 0
+            || alignment.getSequences() == null || alignment.isNucleotide() ? !nucleotidesAllowed
+            : !proteinAllowed)
+    {
+      return null;
+    }
+    if (inputSeqs == null || inputSeqs.getWidth() <= 0
+            || inputSeqs.getSequences() == null
+            || inputSeqs.getSequences().size() < 1)
+    {
+      inputSeqs = alignment;
+    }
+
+    List<compbio.data.sequence.FastaSequence> seqs = new ArrayList<compbio.data.sequence.FastaSequence>();
+
+    int minlen = 10;
+    int ln = -1;
+    if (bySequence)
+    {
+      seqNames = new HashMap<String, SequenceI>();
+    }
+    gapMap = new boolean[0];
+    start = inputSeqs.getStartRes();
+    end = inputSeqs.getEndRes();
+
+    for (SequenceI sq : ((List<SequenceI>) inputSeqs.getSequences()))
+    {
+      if (bySequence ? sq.findPosition(end + 1)
+              - sq.findPosition(start + 1) > minlen - 1 : sq.getEnd()
+              - sq.getStart() > minlen - 1)
+      {
+        String newname = SeqsetUtils.unique_name(seqs.size() + 1);
+        // make new input sequence with or without gaps
+        if (seqNames != null)
+        {
+          seqNames.put(newname, sq);
+        }
+        FastaSequence seq;
+        if (submitGaps)
+        {
+          seqs.add(seq = new compbio.data.sequence.FastaSequence(newname,
+                  sq.getSequenceAsString()));
+          if (gapMap == null || gapMap.length < seq.getSequence().length())
+          {
+            boolean[] tg = gapMap;
+            gapMap = new boolean[seq.getLength()];
+            System.arraycopy(tg, 0, gapMap, 0, tg.length);
+            for (int p = tg.length; p < gapMap.length; p++)
+            {
+              gapMap[p] = false; // init as a gap
+            }
+          }
+          for (int apos : sq.gapMap())
+          {
+            gapMap[apos] = true; // aligned.
+          }
+        }
+        else
+        {
+          seqs.add(seq = new compbio.data.sequence.FastaSequence(newname,
+                  AlignSeq.extractGaps(jalview.util.Comparison.GapChars,
+                          sq.getSequenceAsString(start, end + 1))));
+        }
+        if (seq.getSequence().length() > ln)
+        {
+          ln = seq.getSequence().length();
+        }
+      }
+    }
+    if (alignedSeqs && submitGaps)
+    {
+      realw = 0;
+      for (int i = 0; i < gapMap.length; i++)
+      {
+        if (gapMap[i])
+        {
+          realw++;
+        }
+      }
+      // try real hard to return something submittable
+      // TODO: some of AAcon measures need a minimum of two or three amino
+      // acids at each position, and AAcon doesn't gracefully degrade.
+      for (int p = 0; p < seqs.size(); p++)
+      {
+        FastaSequence sq = seqs.get(p);
+        int l = sq.getSequence().length();
+        // strip gapped columns
+        char[] padded = new char[realw], orig = sq.getSequence()
+                .toCharArray();
+        for (int i = 0, pp = 0; i < realw; pp++)
+        {
+          if (gapMap[pp])
+          {
+            if (orig.length > pp)
+            {
+              padded[i++] = orig[pp];
+            }
+            else
+            {
+              padded[i++] = '-';
+            }
+          }
+        }
+        seqs.set(p, new compbio.data.sequence.FastaSequence(sq.getId(),
+                new String(padded)));
+      }
+    }
+    return seqs;
+  }
+
+  @Override
+  public void updateAnnotation()
+  {
+    updateResultAnnotation(false);
+  }
+
+  public abstract void updateResultAnnotation(boolean immediate);
+
+  public abstract String getServiceActionText();
+
+  /**
+   * notify manager that we have started, and wait for a free calculation slot
+   * 
+   * @return true if slot is obtained and work still valid, false if another
+   *         thread has done our work for us.
+   */
+  protected boolean checkDone()
+  {
+    calcMan.notifyStart(this);
+    ap.paintAlignment(false);
+    while (!calcMan.notifyWorking(this))
+    {
+      if (calcMan.isWorking(this))
+      {
+        return true;
+      }
+      try
+      {
+        if (ap != null)
+        {
+          ap.paintAlignment(false);
+        }
+
+        Thread.sleep(200);
+      } catch (Exception ex)
+      {
+        ex.printStackTrace();
+      }
+    }
+    if (alignViewport.isClosed())
+    {
+      abortAndDestroy();
+      return true;
+    }
+    return false;
+  }
+
+  protected void updateOurAnnots(List<AlignmentAnnotation> ourAnnot)
+  {
+    List<AlignmentAnnotation> our = ourAnnots;
+    ourAnnots = ourAnnot;
+    AlignmentI alignment = alignViewport.getAlignment();
+    if (our != null)
+    {
+      if (our.size() > 0)
+      {
+        for (AlignmentAnnotation an : our)
+        {
+          if (!ourAnnots.contains(an))
+          {
+            // remove the old annotation
+            alignment.deleteAnnotation(an);
+          }
+        }
+      }
+      our.clear();
+
+      ap.adjustAnnotationHeight();
+    }
+  }
+
+}
diff --git a/src/jalview/ws/jws2/JabawsAlignCalcWorker.java b/src/jalview/ws/jws2/JabawsAlignCalcWorker.java
deleted file mode 100644 (file)
index 43d7c63..0000000
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.8.2)
- * Copyright (C) 2014 The Jalview Authors
- * 
- * This file is part of Jalview.
- * 
- * Jalview is free software: you can redistribute it and/or
- * modify it under the terms of the GNU General Public License 
- * as published by the Free Software Foundation, either version 3
- * of the License, or (at your option) any later version.
- *  
- * Jalview is distributed in the hope that it will be useful, but 
- * WITHOUT ANY WARRANTY; without even the implied warranty 
- * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
- * PURPOSE.  See the GNU General Public License for more details.
- * 
- * You should have received a copy of the GNU General Public License
- * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
- * The Jalview Authors are detailed in the 'AUTHORS' file.
- */
-package jalview.ws.jws2;
-
-import jalview.api.AlignViewportI;
-import jalview.api.AlignmentViewPanel;
-import jalview.gui.AlignFrame;
-import jalview.ws.jws2.dm.AAConSettings;
-import jalview.ws.jws2.jabaws2.Jws2Instance;
-import jalview.ws.params.WsParamSetI;
-
-import java.util.List;
-
-import compbio.metadata.Argument;
-
-public abstract class JabawsAlignCalcWorker extends JabawsCalcWorker
-{
-
-  public JabawsAlignCalcWorker(AlignViewportI alignViewport,
-          AlignmentViewPanel alignPanel)
-  {
-    super(alignViewport, alignPanel);
-  }
-
-  public JabawsAlignCalcWorker(Jws2Instance service, AlignFrame alignFrame,
-          WsParamSetI preset, List<Argument> paramset)
-  {
-    super(service, alignFrame, preset, paramset);
-  }
-
-  /**
-   * Recover any existing parameters for this service
-   */
-  protected void initViewportParams()
-  {
-    ((jalview.gui.AlignViewport) alignViewport).setCalcIdSettingsFor(
-            getCalcId(),
-            new AAConSettings(true, service, this.preset,
-                    (arguments != null) ? JabaParamStore
-                            .getJwsArgsfromJaba(arguments) : null), true);
-  }
-
-  /**
-   * 
-   * @return
-   */
-  public abstract String getCalcId();
-
-  @Override
-  public void updateParameters(WsParamSetI newpreset,
-          java.util.List<Argument> newarguments)
-  {
-    super.updateParameters(newpreset, newarguments);
-    initViewportParams();
-  }
-}
index 5223c52..afb46e9 100644 (file)
  */
 package jalview.ws.jws2;
 
-import java.util.ArrayList;
-import java.util.HashMap;
+import jalview.datamodel.AlignmentAnnotation;
+import jalview.datamodel.Annotation;
+import jalview.datamodel.SequenceI;
+import jalview.gui.AlignFrame;
+import jalview.ws.jws2.jabaws2.Jws2Instance;
+import jalview.ws.params.WsParamSetI;
+
 import java.util.Iterator;
 import java.util.List;
-import java.util.Map;
 
 import compbio.data.msa.SequenceAnnotation;
-import compbio.data.sequence.FastaSequence;
 import compbio.data.sequence.Score;
 import compbio.data.sequence.ScoreManager;
 import compbio.metadata.Argument;
 import compbio.metadata.ChunkHolder;
 import compbio.metadata.JobStatus;
 import compbio.metadata.JobSubmissionException;
-import compbio.metadata.Option;
 import compbio.metadata.ResultNotAvailableException;
 import compbio.metadata.WrongParameterException;
-import jalview.analysis.AlignSeq;
-import jalview.analysis.SeqsetUtils;
-import jalview.api.AlignViewportI;
-import jalview.api.AlignmentViewPanel;
-import jalview.datamodel.AlignmentAnnotation;
-import jalview.datamodel.AlignmentI;
-import jalview.datamodel.AnnotatedCollectionI;
-import jalview.datamodel.Annotation;
-import jalview.datamodel.SequenceI;
-import jalview.gui.AlignFrame;
-import jalview.gui.IProgressIndicator;
-import jalview.workers.AlignCalcWorker;
-import jalview.ws.jws2.dm.JabaWsParamSet;
-import jalview.ws.jws2.jabaws2.Jws2Instance;
-import jalview.ws.params.WsParamSetI;
 
-public abstract class JabawsCalcWorker extends AlignCalcWorker
+public abstract class JabawsCalcWorker extends AbstractJabaCalcWorker
 {
 
-  protected Jws2Instance service;
-
   @SuppressWarnings("unchecked")
   protected SequenceAnnotation aaservice;
 
   protected ScoreManager scoremanager;
 
-  protected WsParamSetI preset;
-
-  protected List<Argument> arguments;
-
-  protected IProgressIndicator guiProgress;
-
-  public JabawsCalcWorker(AlignViewportI alignViewport,
-          AlignmentViewPanel alignPanel)
-  {
-    super(alignViewport, alignPanel);
-  }
-
   public JabawsCalcWorker(Jws2Instance service, AlignFrame alignFrame,
           WsParamSetI preset, List<Argument> paramset)
   {
-    this(alignFrame.getCurrentView(), alignFrame.alignPanel);
-    this.guiProgress = alignFrame;
-    this.preset = preset;
-    this.arguments = paramset;
-    this.service = service;
+    super(service, alignFrame, preset, paramset);
     aaservice = (SequenceAnnotation) service.service;
-
   }
 
-  public WsParamSetI getPreset()
+  @Override
+  ChunkHolder pullExecStatistics(String rslt, long rpos)
   {
-    return preset;
+    return aaservice.pullExecStatistics(rslt, rpos);
   }
 
-  public List<Argument> getArguments()
+  @Override
+  boolean collectAnnotationResultsFor(String rslt)
+          throws ResultNotAvailableException
   {
-    return arguments;
+    scoremanager = aaservice.getAnnotation(rslt);
+    if (scoremanager != null)
+    {
+      return true;
+    }
+    return false;
   }
 
-  /**
-   * reconfigure and restart the AAConClient. This method will spawn a new
-   * thread that will wait until any current jobs are finished, modify the
-   * parameters and restart the conservation calculation with the new values.
-   * 
-   * @param newpreset
-   * @param newarguments
-   */
-  public void updateParameters(final WsParamSetI newpreset,
-          final List<Argument> newarguments)
+  @Override
+  boolean cancelJob(String rslt) throws Exception
   {
-    preset = newpreset;
-    arguments = newarguments;
-    calcMan.startWorker(this);
+    return aaservice.cancelJob(rslt);
   }
 
-  public List<Option> getJabaArguments()
+  @Override
+  protected JobStatus getJobStatus(String rslt) throws Exception
   {
-    List<Option> newargs = new ArrayList<Option>();
-    if (preset != null && preset instanceof JabaWsParamSet)
-    {
-      newargs.addAll(((JabaWsParamSet) preset).getjabaArguments());
-    }
-    if (arguments != null && arguments.size() > 0)
-    {
-      for (Argument rg : arguments)
-      {
-        if (Option.class.isAssignableFrom(rg.getClass()))
-        {
-          newargs.add((Option) rg);
-        }
-      }
-    }
-    return newargs;
+    return aaservice.getJobStatus(rslt);
   }
 
   @Override
-  public void run()
+  boolean hasService()
   {
-    if (aaservice == null)
-    {
-      return;
-    }
-    long progressId = -1;
-
-    int serverErrorsLeft = 3;
-
-    String rslt = "JOB NOT DEFINED";
-    StringBuffer msg = new StringBuffer();
-    try
-    {
-      if (checkDone())
-      {
-        return;
-      }
-      List<compbio.data.sequence.FastaSequence> seqs = getInputSequences(
-              alignViewport.getAlignment(),
-              bySequence ? alignViewport.getSelectionGroup() : null);
-
-      if (seqs == null)
-      {
-        calcMan.workerComplete(this);
-        return;
-      }
-
-      AlignmentAnnotation[] aa = alignViewport.getAlignment()
-              .getAlignmentAnnotation();
-      if (guiProgress != null)
-      {
-        guiProgress.setProgressBar("JABA " + getServiceActionText(),
-                progressId = System.currentTimeMillis());
-      }
-      if (preset == null && arguments == null)
-      {
-        rslt = aaservice.analize(seqs);
-      }
-      else
-      {
-        try
-        {
-          rslt = aaservice.customAnalize(seqs, getJabaArguments());
-        } catch (WrongParameterException x)
-        {
-          throw new JobSubmissionException(
-                  "Invalid parameter set. Check Jalview implementation.", x);
-
-        }
-      }
-      boolean finished = false;
-      long rpos = 0;
-      do
-      {
-        JobStatus status = aaservice.getJobStatus(rslt);
-        if (status.equals(JobStatus.FINISHED))
-        {
-          finished = true;
-        }
-        if (calcMan.isPending(this) && this instanceof AAConClient)
-        {
-          finished = true;
-          // cancel this job and yield to the new job
-          try
-          {
-            if (aaservice.cancelJob(rslt))
-            {
-              System.err.println("Cancelled AACon job: " + rslt);
-            }
-            else
-            {
-              System.err.println("FAILED TO CANCEL AACon job: " + rslt);
-            }
-
-          } catch (Exception x)
-          {
-
-          }
-
-          return;
-        }
-        long cpos;
-        ChunkHolder stats = null;
-        do
-        {
-          cpos = rpos;
-          boolean retry = false;
-          do
-          {
-            try
-            {
-              stats = aaservice.pullExecStatistics(rslt, rpos);
-            } catch (Exception x)
-            {
-
-              if (x.getMessage().contains(
-                      "Position in a file could not be negative!"))
-              {
-                // squash index out of bounds exception- seems to happen for
-                // disorder predictors which don't (apparently) produce any
-                // progress information and JABA server throws an exception
-                // because progress length is -1.
-                stats = null;
-              }
-              else
-              {
-                if (--serverErrorsLeft > 0)
-                {
-                  retry = true;
-                  try
-                  {
-                    Thread.sleep(200);
-                  } catch (InterruptedException q)
-                  {
-                  }
-                  ;
-                }
-                else
-                {
-                  throw x;
-                }
-              }
-            }
-          } while (retry);
-          if (stats != null)
-          {
-            System.out.print(stats.getChunk());
-            msg.append(stats);
-            rpos = stats.getNextPosition();
-          }
-        } while (stats != null && rpos > cpos);
-
-        if (!finished && status.equals(JobStatus.FAILED))
-        {
-          try
-          {
-            Thread.sleep(200);
-          } catch (InterruptedException x)
-          {
-          }
-          ;
-        }
-      } while (!finished);
-      if (serverErrorsLeft > 0)
-      {
-        try
-        {
-          Thread.sleep(200);
-        } catch (InterruptedException x)
-        {
-        }
-        ;
-        scoremanager = aaservice.getAnnotation(rslt);
-        if (scoremanager != null)
-        {
-          jalview.bin.Cache.log
-                  .debug("Updating result annotation from Job " + rslt
-                          + " at " + service.getUri());
-          updateResultAnnotation(true);
-          ap.adjustAnnotationHeight();
-        }
-      }
-    }
-
-    catch (JobSubmissionException x)
-    {
-
-      System.err.println("submission error with " + getServiceActionText()
-              + " :");
-      x.printStackTrace();
-      calcMan.workerCannotRun(this);
-    } catch (ResultNotAvailableException x)
-    {
-      System.err.println("collection error:\nJob ID: " + rslt);
-      x.printStackTrace();
-      calcMan.workerCannotRun(this);
-
-    } catch (OutOfMemoryError error)
-    {
-      calcMan.workerCannotRun(this);
-
-      // consensus = null;
-      // hconsensus = null;
-      ap.raiseOOMWarning(getServiceActionText(), error);
-    } catch (Exception x)
-    {
-      calcMan.workerCannotRun(this);
-
-      // consensus = null;
-      // hconsensus = null;
-      System.err
-              .println("Blacklisting worker due to unexpected exception:");
-      x.printStackTrace();
-    } finally
-    {
-
-      calcMan.workerComplete(this);
-      if (ap != null)
-      {
-        calcMan.workerComplete(this);
-        if (guiProgress != null && progressId != -1)
-        {
-          guiProgress.setProgressBar("", progressId);
-        }
-        ap.paintAlignment(true);
-      }
-      if (msg.length() > 0)
-      {
-        // TODO: stash message somewhere in annotation or alignment view.
-        // code below shows result in a text box popup
-        /*
-         * jalview.gui.CutAndPasteTransfer cap = new
-         * jalview.gui.CutAndPasteTransfer(); cap.setText(msg.toString());
-         * jalview.gui.Desktop.addInternalFrame(cap,
-         * "Job Status for "+getServiceActionText(), 600, 400);
-         */
-      }
-    }
-
+    return aaservice != null;
   }
 
   @Override
-  public void updateAnnotation()
+  protected boolean isInteractiveUpdate()
   {
-    updateResultAnnotation(false);
+    return this instanceof AAConClient;
   }
 
-  public abstract void updateResultAnnotation(boolean immediate);
-
-  public abstract String getServiceActionText();
-
-  protected boolean submitGaps = true;
-
-  protected boolean alignedSeqs = true;
-
-  protected boolean nucleotidesAllowed = false;
-
-  protected boolean proteinAllowed = false;
-
-  /**
-   * record sequences for mapping result back to afterwards
-   */
-  protected boolean bySequence = false;
-
-  protected Map<String, SequenceI> seqNames;
-
-  protected boolean[] gapMap;
-
-  int realw;
-
-  int start, end;
-
-  public List<FastaSequence> getInputSequences(AlignmentI alignment,
-          AnnotatedCollectionI inputSeqs)
+  @Override
+  protected String submitToService(
+          List<compbio.data.sequence.FastaSequence> seqs)
+          throws JobSubmissionException
   {
-    if (alignment == null || alignment.getWidth() <= 0
-            || alignment.getSequences() == null || alignment.isNucleotide() ? !nucleotidesAllowed
-            : !proteinAllowed)
-    {
-      return null;
-    }
-    if (inputSeqs == null || inputSeqs.getWidth() <= 0
-            || inputSeqs.getSequences() == null
-            || inputSeqs.getSequences().size() < 1)
-    {
-      inputSeqs = alignment;
-    }
-
-    List<compbio.data.sequence.FastaSequence> seqs = new ArrayList<compbio.data.sequence.FastaSequence>();
-
-    int minlen = 10;
-    int ln = -1;
-    if (bySequence)
-    {
-      seqNames = new HashMap<String, SequenceI>();
-    }
-    gapMap = new boolean[0];
-    start = inputSeqs.getStartRes();
-    end = inputSeqs.getEndRes();
-
-    for (SequenceI sq : ((List<SequenceI>) inputSeqs.getSequences()))
-    {
-      if (bySequence ? sq.findPosition(end + 1)
-              - sq.findPosition(start + 1) > minlen - 1 : sq.getEnd()
-              - sq.getStart() > minlen - 1)
-      {
-        String newname = SeqsetUtils.unique_name(seqs.size() + 1);
-        // make new input sequence with or without gaps
-        if (seqNames != null)
-        {
-          seqNames.put(newname, sq);
-        }
-        FastaSequence seq;
-        if (submitGaps)
-        {
-          seqs.add(seq = new compbio.data.sequence.FastaSequence(newname,
-                  sq.getSequenceAsString()));
-          if (gapMap == null || gapMap.length < seq.getSequence().length())
-          {
-            boolean[] tg = gapMap;
-            gapMap = new boolean[seq.getLength()];
-            System.arraycopy(tg, 0, gapMap, 0, tg.length);
-            for (int p = tg.length; p < gapMap.length; p++)
-            {
-              gapMap[p] = false; // init as a gap
-            }
-          }
-          for (int apos : sq.gapMap())
-          {
-            gapMap[apos] = true; // aligned.
-          }
-        }
-        else
-        {
-          seqs.add(seq = new compbio.data.sequence.FastaSequence(newname,
-                  AlignSeq.extractGaps(jalview.util.Comparison.GapChars,
-                          sq.getSequenceAsString(start, end + 1))));
-        }
-        if (seq.getSequence().length() > ln)
-        {
-          ln = seq.getSequence().length();
-        }
-      }
-    }
-    if (alignedSeqs && submitGaps)
+    String rslt;
+    if (preset == null && arguments == null)
     {
-      realw = 0;
-      for (int i = 0; i < gapMap.length; i++)
-      {
-        if (gapMap[i])
-        {
-          realw++;
-        }
-      }
-      // try real hard to return something submittable
-      // TODO: some of AAcon measures need a minimum of two or three amino
-      // acids at each position, and AAcon doesn't gracefully degrade.
-      for (int p = 0; p < seqs.size(); p++)
-      {
-        FastaSequence sq = seqs.get(p);
-        int l = sq.getSequence().length();
-        // strip gapped columns
-        char[] padded = new char[realw], orig = sq.getSequence()
-                .toCharArray();
-        for (int i = 0, pp = 0; i < realw; pp++)
-        {
-          if (gapMap[pp])
-          {
-            if (orig.length > pp)
-            {
-              padded[i++] = orig[pp];
-            }
-            else
-            {
-              padded[i++] = '-';
-            }
-          }
-        }
-        seqs.set(p, new compbio.data.sequence.FastaSequence(sq.getId(),
-                new String(padded)));
-      }
+      rslt = aaservice.analize(seqs);
     }
-    return seqs;
-  }
-
-  /**
-   * notify manager that we have started, and wait for a free calculation slot
-   * 
-   * @return true if slot is obtained and work still valid, false if another
-   *         thread has done our work for us.
-   */
-  boolean checkDone()
-  {
-    calcMan.notifyStart(this);
-    ap.paintAlignment(false);
-    while (!calcMan.notifyWorking(this))
+    else
     {
-      if (calcMan.isWorking(this))
-      {
-        return true;
-      }
       try
       {
-        if (ap != null)
-        {
-          ap.paintAlignment(false);
-        }
-
-        Thread.sleep(200);
-      } catch (Exception ex)
+        rslt = aaservice.customAnalize(seqs, getJabaArguments());
+      } catch (WrongParameterException x)
       {
-        ex.printStackTrace();
+        throw new JobSubmissionException(
+                "Invalid parameter set. Check Jalview implementation.", x);
+
       }
     }
-    if (alignViewport.isClosed())
-    {
-      abortAndDestroy();
-      return true;
-    }
-    return false;
+    return rslt;
   }
 
   protected void createAnnotationRowsForScores(
@@ -622,28 +207,4 @@ public abstract class JabawsCalcWorker extends AlignCalcWorker
     annotation.validateRangeAndDisplay();
   }
 
-  protected void updateOurAnnots(List<AlignmentAnnotation> ourAnnot)
-  {
-    List<AlignmentAnnotation> our = ourAnnots;
-    ourAnnots = ourAnnot;
-    AlignmentI alignment = alignViewport.getAlignment();
-    if (our != null)
-    {
-      if (our.size() > 0)
-      {
-        for (AlignmentAnnotation an : our)
-        {
-          if (!ourAnnots.contains(an))
-          {
-            // remove the old annotation
-            alignment.deleteAnnotation(an);
-          }
-        }
-      }
-      our.clear();
-
-      ap.adjustAnnotationHeight();
-    }
-  }
-
 }
index 2c96009..b2b676f 100644 (file)
  */
 package jalview.ws.jws2;
 
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
 import java.util.List;
 
+import javax.swing.JCheckBoxMenuItem;
 import javax.swing.JMenu;
+import javax.swing.JMenuItem;
+import javax.swing.event.MenuEvent;
+import javax.swing.event.MenuListener;
 
 import compbio.metadata.Argument;
-
+import jalview.api.AlignCalcWorkerI;
+import jalview.bin.Cache;
 import jalview.gui.AlignFrame;
 import jalview.gui.Desktop;
+import jalview.gui.JvSwingUtils;
 import jalview.gui.WebserviceInfo;
 import jalview.gui.WsJobParameters;
 import jalview.util.MessageManager;
+import jalview.ws.jws2.dm.AAConSettings;
 import jalview.ws.jws2.dm.JabaWsParamSet;
 import jalview.ws.jws2.jabaws2.Jws2Instance;
 import jalview.ws.params.WsParamSetI;
+import jalview.ws.uimodel.AlignAnalysisUIText;
 
 /**
  * provides metadata for a jabaws2 service instance - resolves names, etc.
@@ -164,5 +174,252 @@ public abstract class Jws2Client extends jalview.ws.WSClient
    */
   abstract void attachWSMenuEntry(JMenu wsmenu, final Jws2Instance service,
           final AlignFrame alignFrame);
+  
+
+  protected boolean registerAAConWSInstance(final JMenu wsmenu,
+          final Jws2Instance service, final AlignFrame alignFrame)
+  {
+    final AlignAnalysisUIText aaui = service.getAlignAnalysisUI(); // null ; //
+                                                                   // AlignAnalysisUIText.aaConGUI.get(service.serviceType.toString());
+    if (aaui == null)
+    {
+      // not an instantaneous calculation GUI type service
+      return false;
+    }
+    // create the instaneous calculation GUI bits and update state if existing
+    // GUI elements already present
+
+    JCheckBoxMenuItem _aaConEnabled = null;
+    for (int i = 0; i < wsmenu.getItemCount(); i++)
+    {
+      JMenuItem item = wsmenu.getItem(i);
+      if (item instanceof JCheckBoxMenuItem
+              && item.getText().equals(aaui.getAAconToggle()))
+      {
+        _aaConEnabled = (JCheckBoxMenuItem) item;
+      }
+    }
+    // is there an aaCon worker already present - if so, set it to use the
+    // given service handle
+    {
+      List<AlignCalcWorkerI> aaconClient = alignFrame.getViewport()
+              .getCalcManager()
+              .getRegisteredWorkersOfClass(aaui.getClient());
+      if (aaconClient != null && aaconClient.size() > 0)
+      {
+        AbstractJabaCalcWorker worker = (AbstractJabaCalcWorker) aaconClient
+                .get(0);
+        if (!worker.service.hosturl.equals(service.hosturl))
+        {
+          // javax.swing.SwingUtilities.invokeLater(new Runnable()
+          {
+            // @Override
+            // public void run()
+            {
+              removeCurrentAAConWorkerFor(aaui, alignFrame);
+              buildCurrentAAConWorkerFor(aaui, alignFrame, service);
+            }
+          }// );
+        }
+      }
+    }
+
+    // is there a service already registered ? there shouldn't be if we are
+    // being called correctly
+    if (_aaConEnabled == null)
+    {
+      final JCheckBoxMenuItem aaConEnabled = new JCheckBoxMenuItem(
+              aaui.getAAconToggle());
+
+      aaConEnabled.setToolTipText("<html><p>"
+              + JvSwingUtils.wrapTooltip(aaui.getAAconToggleTooltip()
+                      + "</p>") + "</html>");
+      aaConEnabled.addActionListener(new ActionListener()
+      {
+        @Override
+        public void actionPerformed(ActionEvent arg0)
+        {
+          List<AlignCalcWorkerI> aaconClient = alignFrame.getViewport()
+                  .getCalcManager()
+                  .getRegisteredWorkersOfClass(aaui.getClient());
+          if (aaconClient != null && aaconClient.size() > 0)
+          {
+            removeCurrentAAConWorkerFor(aaui, alignFrame);
+          }
+          else
+          {
+            buildCurrentAAConWorkerFor(aaui, alignFrame);
+
+          }
+        }
+
+      });
+      wsmenu.add(aaConEnabled);
+      final JMenuItem modifyParams = new JMenuItem(aaui.getAAeditSettings());
+      modifyParams.setToolTipText("<html><p>"
+              + JvSwingUtils.wrapTooltip(aaui.getAAeditSettingsTooltip()
+                      + "</p>") + "</html>");
+      modifyParams.addActionListener(new ActionListener()
+      {
+
+        @Override
+        public void actionPerformed(ActionEvent arg0)
+        {
+          showAAConAnnotationSettingsFor(aaui, alignFrame);
+        }
+      });
+      wsmenu.add(modifyParams);
+      wsmenu.addMenuListener(new MenuListener()
+      {
+
+        @Override
+        public void menuSelected(MenuEvent arg0)
+        {
+          // TODO: refactor to the implementing class.
+          if (alignFrame.getViewport().getAlignment().isNucleotide() ? aaui
+                  .isNa() : aaui.isPr())
+          {
+            aaConEnabled.setEnabled(true);
+            modifyParams.setEnabled(true);
+          }
+          else
+          {
+            aaConEnabled.setEnabled(false);
+            modifyParams.setEnabled(false);
+          }
+          List<AlignCalcWorkerI> aaconClient = alignFrame.getViewport()
+                  .getCalcManager()
+                  .getRegisteredWorkersOfClass(aaui.getClient());
+          if (aaconClient != null && aaconClient.size() > 0)
+          {
+            aaConEnabled.setSelected(true);
+          }
+          else
+          {
+            aaConEnabled.setSelected(false);
+          }
+        }
+
+        @Override
+        public void menuDeselected(MenuEvent arg0)
+        {
+          // TODO Auto-generated method stub
+
+        }
+
+        @Override
+        public void menuCanceled(MenuEvent arg0)
+        {
+          // TODO Auto-generated method stub
+
+        }
+      });
+
+    }
+    return true;
+  }
+
+  private static void showAAConAnnotationSettingsFor(
+          final AlignAnalysisUIText aaui, AlignFrame alignFrame)
+  {
+    /*
+     * preferred settings Whether AACon is automatically recalculated Which
+     * AACon server to use What parameters to use
+     */
+    // could actually do a class search for this too
+    AAConSettings fave = (AAConSettings) alignFrame.getViewport()
+            .getCalcIdSettingsFor(aaui.getCalcId());
+    if (fave == null)
+    {
+      fave = createDefaultAAConSettings(aaui);
+    }
+    new SequenceAnnotationWSClient(fave, alignFrame, true);
+
+  }
+
+  private static void buildCurrentAAConWorkerFor(
+          final AlignAnalysisUIText aaui, AlignFrame alignFrame)
+  {
+    buildCurrentAAConWorkerFor(aaui, alignFrame, null);
+  }
+
+  private static void buildCurrentAAConWorkerFor(
+          final AlignAnalysisUIText aaui, AlignFrame alignFrame,
+          Jws2Instance service)
+  {
+    /*
+     * preferred settings Whether AACon is automatically recalculated Which
+     * AACon server to use What parameters to use
+     */
+    AAConSettings fave = (AAConSettings) alignFrame.getViewport()
+            .getCalcIdSettingsFor(aaui.getCalcId());
+    if (fave == null)
+    {
+      fave = createDefaultAAConSettings(aaui, service);
+    }
+    else
+    {
+      if (service != null
+              && !fave.getService().hosturl.equals(service.hosturl))
+      {
+        Cache.log.debug("Changing AACon service to " + service.hosturl
+                + " from " + fave.getService().hosturl);
+        fave.setService(service);
+      }
+    }
+    new SequenceAnnotationWSClient(fave, alignFrame, false);
+  }
+
+  private static AAConSettings createDefaultAAConSettings(
+          AlignAnalysisUIText aaui)
+  {
+    return createDefaultAAConSettings(aaui, null);
+  }
+
+  private static AAConSettings createDefaultAAConSettings(
+          AlignAnalysisUIText aaui, Jws2Instance service)
+  {
+    if (service != null)
+    {
+      if (!service.serviceType.toString().equals(
+              compbio.ws.client.Services.AAConWS.toString()))
+      {
+        Cache.log
+                .warn("Ignoring invalid preferred service for AACon calculations (service type was "
+                        + service.serviceType + ")");
+        service = null;
+      }
+      else
+      {
+        // check service is actually in the list of currently avaialable
+        // services
+        if (!Jws2Discoverer.getDiscoverer().getServices().contains(service))
+        {
+          // it isn't ..
+          service = null;
+        }
+      }
+    }
+    if (service == null)
+    {
+      // get the default service for AACon
+      service = Jws2Discoverer.getDiscoverer().getPreferredServiceFor(null,
+              aaui.getServiceType());
+    }
+    if (service == null)
+    {
+      // TODO raise dialog box explaining error, and/or open the JABA
+      // preferences menu.
+      throw new Error("No AACon service found.");
+    }
+    return new AAConSettings(true, service, null, null);
+  }
+
+  private static void removeCurrentAAConWorkerFor(AlignAnalysisUIText aaui,
+          AlignFrame alignFrame)
+  {
+    alignFrame.getViewport().getCalcManager()
+            .removeRegisteredWorkersOfClass(aaui.getClient());
+  }
 
 }
index 5d0df8a..fac0857 100644 (file)
@@ -37,6 +37,7 @@ import java.util.TreeSet;
 import java.util.regex.Pattern;
 
 import compbio.data.sequence.RNAStructReader.AlifoldResult;
+import compbio.data.sequence.FastaSequence;
 import compbio.data.sequence.RNAStructScoreManager;
 import compbio.data.sequence.Range;
 import compbio.data.sequence.Score;
@@ -49,7 +50,7 @@ import compbio.metadata.Argument;
  * 
  */
 
-public class RNAalifoldClient extends JabawsAlignCalcWorker implements
+public class RNAalifoldClient extends JabawsCalcWorker implements
         AlignCalcWorkerI
 {
 
@@ -109,6 +110,12 @@ public class RNAalifoldClient extends JabawsAlignCalcWorker implements
   }
 
   @Override
+  boolean checkValidInputSeqs(boolean dynamic, List<FastaSequence> seqs)
+  {
+    return (seqs.size() > 1);
+  }
+
+  @Override
   public void updateResultAnnotation(boolean immediate)
   {
 
index 77ba300..13568ea 100644 (file)
@@ -88,7 +88,7 @@ public class SequenceAnnotationWSClient extends Jws2Client
 
       List<AlignCalcWorkerI> clnts = alignFrame.getViewport()
               .getCalcManager().getRegisteredWorkersOfClass(clientClass);
-      JabawsAlignCalcWorker worker;
+      AbstractJabaCalcWorker worker;
       if (clnts == null || clnts.size() == 0)
       {
         if (!processParams(sh, editParams))
@@ -97,7 +97,7 @@ public class SequenceAnnotationWSClient extends Jws2Client
         }
         try
         {
-          worker = (JabawsAlignCalcWorker) (clientClass
+          worker = (AbstractJabaCalcWorker) (clientClass
                   .getConstructor(new Class[]
                   { Jws2Instance.class, AlignFrame.class,
                       WsParamSetI.class, List.class })
@@ -114,7 +114,7 @@ public class SequenceAnnotationWSClient extends Jws2Client
       }
       else
       {
-        worker = (JabawsAlignCalcWorker) clnts.get(0);
+        worker = (AbstractJabaCalcWorker) clnts.get(0);
         if (editParams)
         {
           paramset = worker.getArguments();
@@ -257,250 +257,4 @@ public class SequenceAnnotationWSClient extends Jws2Client
       }
     }
   }
-
-  private boolean registerAAConWSInstance(final JMenu wsmenu,
-          final Jws2Instance service, final AlignFrame alignFrame)
-  {
-    final AlignAnalysisUIText aaui = service.getAlignAnalysisUI(); // null ; //
-                                                                   // AlignAnalysisUIText.aaConGUI.get(service.serviceType.toString());
-    if (aaui == null)
-    {
-      // not an instantaneous calculation GUI type service
-      return false;
-    }
-    // create the instaneous calculation GUI bits and update state if existing
-    // GUI elements already present
-
-    JCheckBoxMenuItem _aaConEnabled = null;
-    for (int i = 0; i < wsmenu.getItemCount(); i++)
-    {
-      JMenuItem item = wsmenu.getItem(i);
-      if (item instanceof JCheckBoxMenuItem
-              && item.getText().equals(aaui.getAAconToggle()))
-      {
-        _aaConEnabled = (JCheckBoxMenuItem) item;
-      }
-    }
-    // is there an aaCon worker already present - if so, set it to use the
-    // given service handle
-    {
-      List<AlignCalcWorkerI> aaconClient = alignFrame.getViewport()
-              .getCalcManager()
-              .getRegisteredWorkersOfClass(aaui.getClient());
-      if (aaconClient != null && aaconClient.size() > 0)
-      {
-        JabawsAlignCalcWorker worker = (JabawsAlignCalcWorker) aaconClient
-                .get(0);
-        if (!worker.service.hosturl.equals(service.hosturl))
-        {
-          // javax.swing.SwingUtilities.invokeLater(new Runnable()
-          {
-            // @Override
-            // public void run()
-            {
-              removeCurrentAAConWorkerFor(aaui, alignFrame);
-              buildCurrentAAConWorkerFor(aaui, alignFrame, service);
-            }
-          }// );
-        }
-      }
-    }
-
-    // is there a service already registered ? there shouldn't be if we are
-    // being called correctly
-    if (_aaConEnabled == null)
-    {
-      final JCheckBoxMenuItem aaConEnabled = new JCheckBoxMenuItem(
-              aaui.getAAconToggle());
-
-      aaConEnabled.setToolTipText("<html><p>"
-              + JvSwingUtils.wrapTooltip(aaui.getAAconToggleTooltip()
-                      + "</p>") + "</html>");
-      aaConEnabled.addActionListener(new ActionListener()
-      {
-        @Override
-        public void actionPerformed(ActionEvent arg0)
-        {
-          List<AlignCalcWorkerI> aaconClient = alignFrame.getViewport()
-                  .getCalcManager()
-                  .getRegisteredWorkersOfClass(aaui.getClient());
-          if (aaconClient != null && aaconClient.size() > 0)
-          {
-            removeCurrentAAConWorkerFor(aaui, alignFrame);
-          }
-          else
-          {
-            buildCurrentAAConWorkerFor(aaui, alignFrame);
-
-          }
-        }
-
-      });
-      wsmenu.add(aaConEnabled);
-      final JMenuItem modifyParams = new JMenuItem(aaui.getAAeditSettings());
-      modifyParams.setToolTipText("<html><p>"
-              + JvSwingUtils.wrapTooltip(aaui.getAAeditSettingsTooltip()
-                      + "</p>") + "</html>");
-      modifyParams.addActionListener(new ActionListener()
-      {
-
-        @Override
-        public void actionPerformed(ActionEvent arg0)
-        {
-          showAAConAnnotationSettingsFor(aaui, alignFrame);
-        }
-      });
-      wsmenu.add(modifyParams);
-      wsmenu.addMenuListener(new MenuListener()
-      {
-
-        @Override
-        public void menuSelected(MenuEvent arg0)
-        {
-          // TODO: refactor to the implementing class.
-          if (alignFrame.getViewport().getAlignment().isNucleotide() ? aaui
-                  .isNa() : aaui.isPr())
-          {
-            aaConEnabled.setEnabled(true);
-            modifyParams.setEnabled(true);
-          }
-          else
-          {
-            aaConEnabled.setEnabled(false);
-            modifyParams.setEnabled(false);
-          }
-          List<AlignCalcWorkerI> aaconClient = alignFrame.getViewport()
-                  .getCalcManager()
-                  .getRegisteredWorkersOfClass(aaui.getClient());
-          if (aaconClient != null && aaconClient.size() > 0)
-          {
-            aaConEnabled.setSelected(true);
-          }
-          else
-          {
-            aaConEnabled.setSelected(false);
-          }
-        }
-
-        @Override
-        public void menuDeselected(MenuEvent arg0)
-        {
-          // TODO Auto-generated method stub
-
-        }
-
-        @Override
-        public void menuCanceled(MenuEvent arg0)
-        {
-          // TODO Auto-generated method stub
-
-        }
-      });
-
-    }
-    return true;
-  }
-
-  private static void showAAConAnnotationSettingsFor(
-          final AlignAnalysisUIText aaui, AlignFrame alignFrame)
-  {
-    /*
-     * preferred settings Whether AACon is automatically recalculated Which
-     * AACon server to use What parameters to use
-     */
-    // could actually do a class search for this too
-    AAConSettings fave = (AAConSettings) alignFrame.getViewport()
-            .getCalcIdSettingsFor(aaui.getCalcId());
-    if (fave == null)
-    {
-      fave = createDefaultAAConSettings(aaui);
-    }
-    new SequenceAnnotationWSClient(fave, alignFrame, true);
-
-  }
-
-  private static void buildCurrentAAConWorkerFor(
-          final AlignAnalysisUIText aaui, AlignFrame alignFrame)
-  {
-    buildCurrentAAConWorkerFor(aaui, alignFrame, null);
-  }
-
-  private static void buildCurrentAAConWorkerFor(
-          final AlignAnalysisUIText aaui, AlignFrame alignFrame,
-          Jws2Instance service)
-  {
-    /*
-     * preferred settings Whether AACon is automatically recalculated Which
-     * AACon server to use What parameters to use
-     */
-    AAConSettings fave = (AAConSettings) alignFrame.getViewport()
-            .getCalcIdSettingsFor(aaui.getCalcId());
-    if (fave == null)
-    {
-      fave = createDefaultAAConSettings(aaui, service);
-    }
-    else
-    {
-      if (service != null
-              && !fave.getService().hosturl.equals(service.hosturl))
-      {
-        Cache.log.debug("Changing AACon service to " + service.hosturl
-                + " from " + fave.getService().hosturl);
-        fave.setService(service);
-      }
-    }
-    new SequenceAnnotationWSClient(fave, alignFrame, false);
-  }
-
-  private static AAConSettings createDefaultAAConSettings(
-          AlignAnalysisUIText aaui)
-  {
-    return createDefaultAAConSettings(aaui, null);
-  }
-
-  private static AAConSettings createDefaultAAConSettings(
-          AlignAnalysisUIText aaui, Jws2Instance service)
-  {
-    if (service != null)
-    {
-      if (!service.serviceType.toString().equals(
-              compbio.ws.client.Services.AAConWS.toString()))
-      {
-        Cache.log
-                .warn("Ignoring invalid preferred service for AACon calculations (service type was "
-                        + service.serviceType + ")");
-        service = null;
-      }
-      else
-      {
-        // check service is actually in the list of currently avaialable
-        // services
-        if (!Jws2Discoverer.getDiscoverer().getServices().contains(service))
-        {
-          // it isn't ..
-          service = null;
-        }
-      }
-    }
-    if (service == null)
-    {
-      // get the default service for AACon
-      service = Jws2Discoverer.getDiscoverer().getPreferredServiceFor(null,
-              aaui.getServiceType());
-    }
-    if (service == null)
-    {
-      // TODO raise dialog box explaining error, and/or open the JABA
-      // preferences menu.
-      throw new Error("No AACon service found.");
-    }
-    return new AAConSettings(true, service, null, null);
-  }
-
-  private static void removeCurrentAAConWorkerFor(AlignAnalysisUIText aaui,
-          AlignFrame alignFrame)
-  {
-    alignFrame.getViewport().getCalcManager()
-            .removeRegisteredWorkersOfClass(aaui.getClient());
-  }
 }