First patch for * JAL-493
[jalview.git] / src / jalview / ws / MsaWSClient.java
index 36c3823..d58e2dc 100755 (executable)
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer (Version 2.5)
+ * Copyright (C) 2010 J Procter, AM Waterhouse, G Barton, M Clamp, S Searle
+ * 
+ * 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/>.
+ */
 package jalview.ws;
 
-import org.apache.axis.client.*;
-import javax.xml.namespace.QName;
-import java.util.*;
-import jalview.datamodel.*;
-import jalview.gui.*;
-import javax.swing.*;
-import java.util.*;
-import java.awt.*;
-import jalview.analysis.AlignSeq;
-import ext.vamsas.*;
-import vamsas.objects.*;
-
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
 
+import javax.swing.*;
 
+import ext.vamsas.*;
+import jalview.datamodel.*;
+import jalview.gui.*;
 
-public class MsaWSClient
-    extends WSClient
+/**
+ * DOCUMENT ME!
+ * 
+ * @author $author$
+ * @version $Revision$
+ */
+public class MsaWSClient extends WS1Client
 {
-    /**
-     * server is a WSDL2Java generated stub for an archetypal MsaWSI service.
-     */
-    ext.vamsas.MuscleWS server;
-
-  private boolean setWebService(String MsaWSName) {
-    if (MsaWServices.info.containsKey(MsaWSName)) {
-      WebServiceName = MsaWSName;
-      String[] wsinfo = (String[]) MsaWServices.info.get(MsaWSName);
-      WsURL = wsinfo[0];
-      WebServiceJobTitle = wsinfo[1];
-      WebServiceReference = wsinfo[2];
-      return true;
-    } else {
-      return false;
-    }
-  }
-
-
-  public MsaWSClient(String MsaWSName, String altitle, SequenceI[] msa, boolean submitGaps, boolean preserveOrder)
+  /**
+   * server is a WSDL2Java generated stub for an archetypal MsaWSI service.
+   */
+  ext.vamsas.MuscleWS server;
+
+  AlignFrame alignFrame;
+
+  /**
+   * Creates a new MsaWSClient object that uses a service given by an externally
+   * retrieved ServiceHandle
+   * 
+   * @param sh
+   *          service handle of type AbstractName(MsaWS)
+   * @param altitle
+   *          DOCUMENT ME!
+   * @param msa
+   *          DOCUMENT ME!
+   * @param submitGaps
+   *          DOCUMENT ME!
+   * @param preserveOrder
+   *          DOCUMENT ME!
+   */
+
+  public MsaWSClient(ext.vamsas.ServiceHandle sh, String altitle,
+          jalview.datamodel.AlignmentView msa, boolean submitGaps,
+          boolean preserveOrder, Alignment seqdataset,
+          AlignFrame _alignFrame)
   {
-    if (setWebService(MsaWSName)==false) {
-      JOptionPane.showMessageDialog(Desktop.desktop, "The Multiple Sequence Alignment Service named "+MsaWSName+" is unknown",
-                                    "Internal Jalview Error", JOptionPane.WARNING_MESSAGE);
+    super();
+    alignFrame = _alignFrame;
+    if (!sh.getAbstractName().equals("MsaWS"))
+    {
+      JOptionPane
+              .showMessageDialog(
+                      Desktop.desktop,
+                      "The Service called \n"
+                              + sh.getName()
+                              + "\nis not a \nMultiple Sequence Alignment Service !",
+                      "Internal Jalview Error", JOptionPane.WARNING_MESSAGE);
+
       return;
     }
 
-    wsInfo = new jalview.gui.WebserviceInfo(WebServiceJobTitle, WebServiceReference);
-
-    wsInfo.setProgressText("Alignment of "+altitle+"\nJob details\n");
+    if ((wsInfo = setWebService(sh)) == null)
+    {
+      JOptionPane.showMessageDialog(Desktop.desktop,
+              "The Multiple Sequence Alignment Service named "
+                      + sh.getName() + " is unknown",
+              "Internal Jalview Error", JOptionPane.WARNING_MESSAGE);
 
-    // TODO: MuscleWS transmuted to generic MsaWS client
-    MuscleWSServiceLocator loc = new MuscleWSServiceLocator(); // Default
-    try {
-      this.server = (MuscleWS) loc.getMuscleWS(new java.net.URL(WsURL));
-    }
-    catch (Exception ex) {
-      wsInfo.setProgressText("Serious! "+WebServiceName+" Service location failed\nfor URL :"
-                     +WsURL+"\n"+ex.getMessage());
-      wsInfo.setStatus(wsInfo.ERROR);
-      ex.printStackTrace();
+      return;
     }
+    startMsaWSClient(altitle, msa, submitGaps, preserveOrder, seqdataset);
 
-    MsaWSThread musclethread = new MsaWSThread(WebServiceName+" alignment of "+altitle, msa, submitGaps, preserveOrder);
-    wsInfo.setthisService(musclethread);
-    musclethread.start();
   }
 
-
-  protected class MsaWSThread extends Thread implements WSClientI
+  public MsaWSClient()
   {
-    String ServiceName = WebServiceName;
+    super();
+    // add a class reference to the list
+  }
 
-    public boolean isCancellable()
+  private void startMsaWSClient(String altitle, AlignmentView msa,
+          boolean submitGaps, boolean preserveOrder, Alignment seqdataset)
+  {
+    if (!locateWebService())
     {
-      return true;
+      return;
     }
 
-    String OutputHeader;
-    vamsas.objects.simple.MsaResult result = null;
-
-    vamsas.objects.simple.SequenceSet seqs = new vamsas.objects.simple.
-        SequenceSet();
-
-    Hashtable SeqNames = null;
-    boolean submitGaps = false,// default is to strip gaps from sequences
-        preserveOrder = true; // and always store and recover sequence order
-
-    String jobId;
-    String alTitle; // name which will be used to form new alignment window.
-    int allowedServerExceptions = 3; // thread dies if too many exceptions.
-    MsaWSThread(String title, SequenceI[] msa, boolean subgaps, boolean presorder)
+    wsInfo.setProgressText(((submitGaps) ? "Re-alignment" : "Alignment")
+            + " of " + altitle + "\nJob details\n");
+    String jobtitle = WebServiceName.toLowerCase();
+    if (jobtitle.endsWith("alignment"))
     {
-      alTitle = title;
-      submitGaps = subgaps;
-      preserveOrder = presorder;
-
-      OutputHeader = wsInfo.getProgressText();
-      SeqNames = new Hashtable();
-      vamsas.objects.simple.Sequence[] seqarray = new vamsas.objects.simple.
-          Sequence[msa.length];
-
-      for (int i = 0; i < msa.length; i++)
+      if (submitGaps
+              && (!jobtitle.endsWith("realignment") || jobtitle
+                      .indexOf("profile") == -1))
       {
-        String newname = jalview.analysis.SeqsetUtils.unique_name(i);
-        // uniquify as we go
-        // TODO: JBPNote: this is a ubiquitous transformation - set of jalview seq objects to vamsas sequences with name preservation
-        SeqNames.put(newname, jalview.analysis.SeqsetUtils.SeqCharacterHash(msa[i]));
-        seqarray[i] = new vamsas.objects.simple.Sequence();
-        seqarray[i].setId(newname);
-        seqarray[i].setSeq((submitGaps) ? msa[i].getSequence()
-                           : AlignSeq.extractGaps(jalview.util.Comparison.GapChars, msa[i].getSequence()));
+        int pos = jobtitle.indexOf("alignment");
+        jobtitle = WebServiceName.substring(0, pos) + "re-alignment of "
+                + altitle;
       }
-
-      this.seqs = new vamsas.objects.simple.SequenceSet();
-      this.seqs.setSeqs(seqarray);
-    }
-
-    boolean jobComplete = false;
-
-    public void cancelJob() {
-      if (!jobComplete) {
-        String cancelledMessage="";
-        try {
-          vamsas.objects.simple.WsJobId cancelledJob = server.cancel(jobId);
-          if (cancelledJob.getStatus() == 2)
-          {
-            // CANCELLED_JOB
-            cancelledMessage = "Job cancelled.";
-            wsInfo.setStatus(WebserviceInfo.STATE_CANCELLED_OK);
-            jobComplete = true;
-            jobsRunning--;
-            result = null;
-          }
-          else
-          if (cancelledJob.getStatus() == 3)
-          {
-            // VALID UNSTOPPABLE JOB
-            cancelledMessage +=
-                "Server cannot cancel this job. just close the window.\n";
-          }
-          if (cancelledJob.getJobId() != null)
-            cancelledMessage += "[" + cancelledJob.getJobId() + "]";
-          cancelledMessage +="\n";
-        } catch (Exception exc) {
-          cancelledMessage +="\nProblems cancelling the job : Exception received...\n"+exc+"\n";
-          exc.printStackTrace();
-        }
-        wsInfo.setProgressText(OutputHeader + cancelledMessage+"\n");
+      else
+      {
+        jobtitle = WebServiceName + " of " + altitle;
       }
     }
-
-    public void run()
+    else
     {
+      jobtitle = WebServiceName + (submitGaps ? " re" : " ")
+              + "alignment of " + altitle;
+    }
 
-      StartJob();
-
-      while (!jobComplete && (allowedServerExceptions > 0))
-      {
-        try
-        {
-          result = server.getResult(jobId);
+    MsaWSThread msathread = new MsaWSThread(server, WsURL, wsInfo,
+            alignFrame, WebServiceName, jobtitle, msa, submitGaps,
+            preserveOrder, seqdataset);
+    wsInfo.setthisService(msathread);
+    msathread.start();
+  }
 
-          if (result.isRunning())
-            wsInfo.setStatus(WebserviceInfo.STATE_RUNNING);
-          else if (result.isQueued())
-            wsInfo.setStatus(WebserviceInfo.STATE_QUEUING);
+  /**
+   * Initializes the server field with a valid service implementation.
+   * 
+   * @return true if service was located.
+   */
+  private boolean locateWebService()
+  {
+    // TODO: MuscleWS transmuted to generic MsaWS client
+    MuscleWSServiceLocator loc = new MuscleWSServiceLocator(); // Default
 
-          if (result.isFinished())
-          {
-            parseResult();
-            jobComplete = true;
-            jobsRunning--;
-          }
-          else
-          {
-            if (result.getStatus() != null)
-              wsInfo.setProgressText(OutputHeader + "\n" + result.getStatus());
-            if (! (result.isJobFailed() || result.isServerError()))
-            {
-              Thread.sleep(5000);
-              //  System.out.println("I'm alive "+seqid+" "+jobid);
-            }
-            else
-            {
-              break;
-            }
-          }
-        }
-        catch (Exception ex)
-        {
-          allowedServerExceptions--;
-          wsInfo.appendProgressText("\n" + ServiceName + " Server exception!\n" +
-                                    ex.getMessage());
-          ex.printStackTrace();
-        }
-      }
-      if (allowedServerExceptions == 0)
-      {
-        wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_SERVERERROR);
-      }
-      else
-      {
-        if (! (result != null && (result.isJobFailed() || result.isServerError())))
-          wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_OK);
-        else
-        {
-          if (result.isFailed())
-            wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_ERROR);
-          if (result.isServerError())
-            wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_SERVERERROR);
-        }
-      }
-    }
-    void StartJob()
+    try
     {
-      try
-      {
-        vamsas.objects.simple.WsJobId jobsubmit = server.align(seqs);
-        if (jobsubmit.getStatus()==1) {
-          jobId=jobsubmit.getJobId();
-          System.out.println(WsURL+" Job Id '"+jobId+"'");
-        } else {
-          throw new Exception(jobsubmit.getJobId());
-        }
-      }
-      catch (Exception e)
-      {
-        System.err.println(ServiceName + " Client: Failed to submit the prediction\n" +
-                           e.toString() + "\n");
-        e.printStackTrace();
-      }
+      this.server = (MuscleWS) loc.getMuscleWS(new java.net.URL(WsURL));
+      ((MuscleWSSoapBindingStub) this.server).setTimeout(60000); // One minute
+      // timeout
+    } catch (Exception ex)
+    {
+      wsInfo.setProgressText("Serious! " + WebServiceName
+              + " Service location failed\nfor URL :" + WsURL + "\n"
+              + ex.getMessage());
+      wsInfo.setStatus(WebserviceInfo.ERROR);
+      ex.printStackTrace();
+
+      return false;
     }
 
-    private void addFloatAnnotations(Alignment al, int[] gapmap, Vector values, String Symname, String Visname, float min, float max, int winLength) {
+    loc.getEngine().setOption("axis", "1");
 
-      Annotation[] annotations = new Annotation[al.getWidth()];
-      for (int j = 0; j < values.size(); j++)
-      {
-        float value = Float.parseFloat(values.get(j).toString());
-        annotations[gapmap[j]] = new Annotation("", value+"",' ',value);
-      }
-      al.addAnnotation(new AlignmentAnnotation(Symname, Visname, annotations, min, max, winLength));
-    }
-    private jalview.datamodel.Sequence[] getVamsasAlignment(vamsas.objects.simple.Alignment valign) {
-      vamsas.objects.simple.Sequence[] seqs = valign.getSeqs().getSeqs();
-      jalview.datamodel.Sequence[] msa = new jalview.datamodel.Sequence[seqs.length];
-      for (int i=0, j=seqs.length; i<j;i++)
-        msa[i] = new jalview.datamodel.Sequence(seqs[i].getId(), seqs[i].getSeq());
-      return msa;
-    }
-    void parseResult()
-    {
-      SequenceI[] seqs=null;
-      try {
-        // OutputHeader = output.getText();
-        if (result.isFailed()) {
-          OutputHeader +="Job failed.\n";
-        }
-        if (result.getStatus()!=null) {
-          OutputHeader += "\n"+result.getStatus();
-        }
-        if (result.getMsa()!=null) {
-          OutputHeader += "\nAlignment Object Method Notes\n";
-          String lines[] = result.getMsa().getMethod();
-          for (int line=0;line<lines.length; line++)
-            OutputHeader+=lines[line]+"\n";
+    return true;
+  }
 
-          // JBPNote The returned files from a webservice could be hidden behind icons in the monitor window that, when clicked, pop up their corresponding data
-          seqs = getVamsasAlignment(result.getMsa());
-        }
+  protected String getServiceActionKey()
+  {
+    return "MsaWS";
+  }
 
-        wsInfo.setProgressText(OutputHeader);
-        if (seqs!=null) {
-          AlignmentOrder msaorder = new AlignmentOrder(seqs);
+  protected String getServiceActionDescription()
+  {
+    return "Multiple Sequence Alignment";
+  }
 
-          if (preserveOrder) {
-            jalview.analysis.AlignmentSorter.recoverOrder(seqs);
-          }
+  /**
+   * look at ourselves and work out if we are a service that can take a profile
+   * and align to it
+   * 
+   * @return true if we can send gapped sequences to the alignment service
+   */
+  private boolean canSubmitGaps()
+  {
+    // TODO: query service or extract service handle props to check if we can
+    // realign
+    return (WebServiceName.indexOf("lustal") > -1); // cheat!
+  }
 
-          jalview.analysis.SeqsetUtils.deuniquify(SeqNames, seqs);
+  public void attachWSMenuEntry(JMenu msawsmenu,
+          final ServiceHandle serviceHandle, final AlignFrame alignFrame)
+  {
+    setWebService(serviceHandle, true); // headless
+    JMenuItem method = new JMenuItem(WebServiceName);
+    method.setToolTipText(WsURL);
+    method.addActionListener(new ActionListener()
+    {
+      public void actionPerformed(ActionEvent e)
+      {
+        AlignmentView msa = alignFrame.gatherSequencesForAlignment();
+        new jalview.ws.MsaWSClient(serviceHandle, alignFrame.getTitle(),
+                msa, false, true, alignFrame.getViewport().getAlignment()
+                        .getDataset(), alignFrame);
 
-          Alignment al = new Alignment(seqs);
+      }
 
-          // TODO: JBPNote Should also rename the query sequence sometime...
-          AlignFrame af = new AlignFrame(al);
-          af.addSortByOrderMenuItem(ServiceName+" Ordering", msaorder);
+    });
+    msawsmenu.add(method);
+    if (canSubmitGaps())
+    {
+      // We know that ClustalWS can accept partial alignments for refinement.
+      final JMenuItem methodR = new JMenuItem(serviceHandle.getName()
+              + " Realign");
+      methodR.setToolTipText(WsURL);
+      methodR.addActionListener(new ActionListener()
+      {
+        public void actionPerformed(ActionEvent e)
+        {
+          AlignmentView msa = alignFrame.gatherSequencesForAlignment();
+          new jalview.ws.MsaWSClient(serviceHandle, alignFrame.getTitle(),
+                  msa, true, true, alignFrame.getViewport().getAlignment()
+                          .getDataset(), alignFrame);
 
-          Desktop.addInternalFrame(af,
-                                   alTitle,
-                                   AlignFrame.NEW_WINDOW_WIDTH,
-                                   AlignFrame.NEW_WINDOW_HEIGHT);
         }
-      }catch(Exception ex){ex.printStackTrace();}
+
+      });
+      msawsmenu.add(methodR);
 
     }
 
   }
 }
-
-