Merge branch 'features/JAL-1596ChimeraREST' into develop
authorgmungoc <g.m.carstairs@dundee.ac.uk>
Sun, 28 Dec 2014 10:34:27 +0000 (10:34 +0000)
committergmungoc <g.m.carstairs@dundee.ac.uk>
Sun, 28 Dec 2014 10:34:27 +0000 (10:34 +0000)
1  2 
.classpath
src/ext/edu/ucsf/rbvi/strucviz2/ChimeraManager.java
src/jalview/ext/rbvi/chimera/JalviewChimeraBinding.java

diff --combined .classpath
        <classpathentry kind="lib" path="lib/min-jabaws-client-2.1.0.jar" sourcepath="/clustengine"/>
        <classpathentry kind="lib" path="lib/json_simple-1.1.jar" sourcepath="/Users/jimp/Downloads/json_simple-1.1-all.zip"/>
        <classpathentry kind="lib" path="lib/slf4j-api-1.7.7.jar"/>
 +      <classpathentry kind="lib" path="lib/jsoup-1.8.1.jar"/>
        <classpathentry kind="lib" path="lib/log4j-to-slf4j-2.0-rc2.jar"/>
        <classpathentry kind="lib" path="lib/slf4j-log4j12-1.7.7.jar"/>
        <classpathentry kind="lib" path="lib/VARNAv3-91.jar"/>
 -
 -      <classpathentry kind="lib" path="lib/jfreesvg-2.1.jar"/>
        <classpathentry kind="con" path="org.eclipse.jdt.USER_LIBRARY/plugin.jar"/>
        <classpathentry kind="lib" path="lib/xml-apis.jar"/>
        <classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/4"/>
        <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
        <classpathentry kind="con" path="org.eclipse.jdt.USER_LIBRARY/Plugin.jar"/>
 +      <classpathentry kind="lib" path="lib/jfreesvg-2.1.jar"/>
-       <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.launching.macosx.MacOSXType/Java SE 6 [1.6.0_65-b14-462]"/>
        <classpathentry kind="output" path="classes"/>
  </classpath>
@@@ -1,14 -1,21 +1,21 @@@
  package ext.edu.ucsf.rbvi.strucviz2;
  
+ import jalview.ws.HttpClientUtils;
  import java.awt.Color;
+ import java.io.BufferedReader;
  import java.io.File;
  import java.io.IOException;
+ import java.io.InputStream;
+ import java.io.InputStreamReader;
  import java.util.ArrayList;
  import java.util.Collection;
  import java.util.HashMap;
  import java.util.List;
  import java.util.Map;
  
+ import org.apache.http.NameValuePair;
+ import org.apache.http.message.BasicNameValuePair;
  import org.slf4j.Logger;
  import org.slf4j.LoggerFactory;
  
@@@ -20,10 -27,19 +27,19 @@@ import ext.edu.ucsf.rbvi.strucviz2.port
   */
  public class ChimeraManager
  {
 -  private static final boolean debug = true;
++  private static final boolean debug = false;
+   /*
+    * true: use REST API (recommended), false: use stdout/stdin (deprecated)
+    */
++  // TODO remove once definitely happy with using REST
+   private static final boolean USE_REST = true;
 -  // Port number for Chimera REST service
 -  private int restPort;
++  private int chimeraRestPort;
  
    private Process chimera;
  
-   private ListenerThreads chimeraListenerThreads;
+   private ListenerThreads chimeraListenerThread;
  
    private Map<Integer, ChimeraModel> currentModelsMap;
  
@@@ -36,7 -52,7 +52,7 @@@
    {
      this.structureManager = structureManager;
      chimera = null;
-     chimeraListenerThreads = null;
+     chimeraListenerThread = null;
      currentModelsMap = new HashMap<Integer, ChimeraModel>();
  
    }
    {
      chimera = null;
      currentModelsMap.clear();
-     chimeraListenerThreads = null;
 -    if (!USE_REST)
++    if (USE_REST)
++    {
++      this.chimeraRestPort = 0;
++    }
++    else
+     {
+       chimeraListenerThread.requestStop();
+       chimeraListenerThread = null;
+     }
      structureManager.clearOnChimeraExit();
    }
  
          List<String> args = new ArrayList<String>();
          args.add(chimeraPath);
          args.add("--start");
-         args.add("ReadStdin");
+         args.add(USE_REST ? "RESTServer" : "ReadStdin");
          ProcessBuilder pb = new ProcessBuilder(args);
          chimera = pb.start();
          error = "";
          workingPath = chimeraPath;
-         logger.info("Strarting " + chimeraPath);
+         logger.info("Starting " + chimeraPath + " with "
+                 + (USE_REST ? "REST API" : "stdin/stdout"));
          break;
        } catch (Exception e)
        {
      // If no error, then Chimera was launched successfully
      if (error.length() == 0)
      {
-       // Initialize the listener threads
-       chimeraListenerThreads = new ListenerThreads(chimera,
-               structureManager);
-       chimeraListenerThreads.start();
+       if (USE_REST)
+       {
 -        this.restPort = getPortNumber();
++        this.chimeraRestPort = getPortNumber();
++        System.out.println("Chimera REST API on port " + chimeraRestPort);
+       }
+       else
+       {
+         // Initialize the listener threads
+         chimeraListenerThread = new ListenerThreads(chimera,
+                 structureManager);
+         chimeraListenerThread.start();
+       }
        // structureManager.initChimTable();
        structureManager.setChimeraPathProperty(workingPath);
        // TODO: [Optional] Check Chimera version and show a warning if below 1.8
    }
  
    /**
+    * Read and return the port number returned in the reply to --start RESTServer
+    */
+   private int getPortNumber()
+   {
+     int port = 0;
+     InputStream readChan = chimera.getInputStream();
+     BufferedReader lineReader = new BufferedReader(new InputStreamReader(
+             readChan));
+     String response = null;
+     try
+     {
+       // expect: REST server on host 127.0.0.1 port port_number
+       response = lineReader.readLine();
+       String [] tokens = response.split(" ");
+       if (tokens.length == 7 && "port".equals(tokens[5])) {
+         port = Integer.parseInt(tokens[6]);
 -        logger.info("Chimera REST service listening on port " + restPort);
++        logger.info("Chimera REST service listening on port "
++                + chimeraRestPort);
+       }
+     } catch (Exception e)
+     {
+       logger.error("Failed to get REST port number from " + response + ": "
+               + e.getMessage());
+     } finally
+     {
+       try
+       {
+         lineReader.close();
+       } catch (IOException e2)
+       {
+       }
+     }
+     return port;
+   }
+   /**
     * Determine the color that Chimera is using for this model.
     * 
     * @param model
     */
    public List<String> sendChimeraCommand(String command, boolean reply)
    {
-     if (!isChimeraLaunched())
+     if (!isChimeraLaunched() || command == null
+             || "".equals(command.trim()))
      {
        return null;
      }
        ;
      }
      busy = true;
+     long startTime = System.currentTimeMillis();
      try
      {
-       chimeraListenerThreads.clearResponse(command);
-       String text = command.concat("\n");
-       // System.out.println("send command to chimera: " + text);
-       try
+       if (USE_REST)
        {
-         // send the command
-         chimera.getOutputStream().write(text.getBytes());
-         chimera.getOutputStream().flush();
-       } catch (IOException e)
-       {
-         // logger.info("Unable to execute command: " + text);
-         // logger.info("Exiting...");
-         logger.warn("Unable to execute command: " + text);
-         logger.warn("Exiting...");
-         clearOnChimeraExit();
-         // busy = false;
-         return null;
+         return sendRestCommand(command);
        }
-       if (!reply)
+       else
        {
-         // busy = false;
-         return null;
+         return sendStdinCommand(command, reply);
        }
      } finally
      {
        busy = false;
+       if (debug)
+       {
+         System.out.println("Chimera command took "
+                 + (System.currentTimeMillis() - startTime) + "ms: "
+                 + command);
+       }
+     }
+   }
+   /**
+    * Sends the command to Chimera's REST API, and returns any response lines.
+    * 
+    * @param command
+    * @return
+    */
+   protected List<String> sendRestCommand(String command)
+   {
+     // TODO start a separate thread to do this so we don't block?
 -    String restUrl = "http://127.0.0.1:" + this.restPort + "/run";
++    String restUrl = "http://127.0.0.1:" + this.chimeraRestPort + "/run";
+     List<NameValuePair> commands = new ArrayList<NameValuePair>(1);
+     commands.add(new BasicNameValuePair("command", command));
+     List<String> reply = new ArrayList<String>();
+     BufferedReader response = null;
+     try {
+       response = HttpClientUtils.doHttpUrlPost(restUrl,
+               commands);
+       String line = "";
+       while ((line = response.readLine()) != null) {
+         reply.add(line);
+       }
+     } catch (Exception e)
+     {
+       logger.error("REST call " + command + " failed: " + e.getMessage());
+     } finally
+     {
+       if (response != null)
+       {
+         try
+         {
+           response.close();
+         } catch (IOException e)
+         {
+         }
+       }
+     }
+     return reply;
+   }
+   /**
+    * Send a command to stdin of Chimera process, and optionally read any
+    * responses.
+    * 
+    * @param command
+    * @param readReply
+    * @return
+    */
+   protected List<String> sendStdinCommand(String command, boolean readReply)
+   {
+     chimeraListenerThread.clearResponse(command);
+     String text = command.concat("\n");
+     try
+     {
+       // send the command
+       chimera.getOutputStream().write(text.getBytes());
+       chimera.getOutputStream().flush();
+     } catch (IOException e)
+     {
+       // logger.info("Unable to execute command: " + text);
+       // logger.info("Exiting...");
+       logger.warn("Unable to execute command: " + text);
+       logger.warn("Exiting...");
+       clearOnChimeraExit();
+       return null;
+     }
+     if (!readReply)
+     {
+       return null;
      }
+     List<String> rsp = chimeraListenerThread.getResponse(command);
+     return rsp;
    }
  
    public StructureManager getStructureManager()
@@@ -58,6 -58,6 +58,9 @@@ public abstract class JalviewChimeraBin
          SequenceStructureBinding, StructureSelectionManagerProvider
  
  {
++
++  private static final boolean debug = false;
++
    private static final String PHOSPHORUS = "P";
  
    private static final String ALPHACARBON = "CA";
        }
        if (selectioncom.length() > 0)
        {
--        // TODO remove debug output
--        System.out.println("Select regions:\n" + selectioncom.toString());
--        System.out
--                .println("Superimpose command(s):\n" + command.toString());
++        if (debug)
++        {
++          System.out.println("Select regions:\n" + selectioncom.toString());
++          System.out.println("Superimpose command(s):\n"
++                  + command.toString());
++        }
          allComs.append("~display all; chain @CA|P; ribbon "
                  + selectioncom.toString() + ";"+command.toString());
          // selcom.append("; ribbons; ");
        {
          selectioncom.setLength(selectioncom.length() - 1);
        }
--      System.out.println("Select regions:\n" + selectioncom.toString());
++      if (debug)
++      {
++        System.out.println("Select regions:\n" + selectioncom.toString());
++      }
        allComs.append("; ~display all; chain @CA|P; ribbon "
                + selectioncom.toString() + "; focus");
        // evalStateCommand("select *; backbone; select "+selcom.toString()+"; cartoons; center "+selcom.toString());
      }
    }
  
--  boolean debug = false;
--
    private void log(String message)
    {
      System.err.println("## Chimera log: " + message);