Package org.molgenis.util

Source Code of org.molgenis.util.Ssh

package org.molgenis.util;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.charset.Charset;

import org.apache.log4j.Logger;

import ch.ethz.ssh2.ChannelCondition;
import ch.ethz.ssh2.Connection;
import ch.ethz.ssh2.InteractiveCallback;
import ch.ethz.ssh2.LocalPortForwarder;
import ch.ethz.ssh2.SCPClient;
import ch.ethz.ssh2.Session;

/**
* Wrapper arround ssh. Build on top of
*
* http://www.cleondris.ch/opensource/ssh2/javadoc/ (BSD type license)
* http://www.ganymed.ethz.ch/ssh2/FAQ.html
*/
public class Ssh
{
  // general ssh settings
  private String hostname;
  private String username;
  private String password;
  private int timeout = 10000;
  private int port = 22;

  Logger logger = Logger.getLogger(Ssh.class);

  // implementation specific
  Connection conn;
  Session sess;
  // for forwarding
  LocalPortForwarder lpf;

  public Ssh(String host, String user, String password) throws IOException
  {
    this(host, user, password, 22);
  }

  public Ssh(String host, String user, String password, int port) throws IOException
  {
    this.hostname = host;
    this.username = user;
    this.password = password;
    this.port = port;

    this.connect();
  }

  public Ssh(String host, String user, String password, int port, String forwardHost, String forwardUser,
      String forwardPassword, int forwardPort) throws IOException
  {
    // first create a tunnel, then connect to second host
    Ssh forwardSsh = new Ssh(forwardHost, forwardUser, forwardPassword, forwardPort);

    // setup the tunnel via the forwardHost to the port
    forwardSsh.forward(9999, host, port);

    // create the ssh to the forwarded port
    this.hostname = "127.0.0.1";
    this.username = user;
    this.password = password;
    this.port = 9999;

    this.connect();
  }

  public void forward(int local_port, String host_to_connect, int port_to_connect) throws IOException
  {
    logger.debug("creating a tunnel from L:" + local_port + "->" + host_to_connect + ":" + port_to_connect);
    lpf = conn.createLocalPortForwarder(local_port, host_to_connect, port_to_connect);

    // conn.requestRemotePortForwarding("127.0.0.1", local_port,
    // host_to_connect, port_to_connect);
  }

  /**
   * Connect to server as a session
   *
   * @throws IOException
   */
  private void connect() throws IOException
  {
    logger.debug("trying to connect to " + this.username + "@" + this.hostname + ":" + this.port);
    /* Create a connection instance */
    conn = new Connection(this.hostname, this.port);

    /* Now connect */
    conn.connect();

    /* Authenticate */
    try
    {
      boolean isAuthenticated = conn.authenticateWithPassword(username, password);

      if (isAuthenticated == false)
      {
        throw new IOException("Authentication failed.");
      }

    }
    catch (Exception e)
    { // authenticated method not supported
      // try to use keyboard interactive
      conn.authenticateWithKeyboardInteractive(username, new InteractiveCallback()
      {
        @Override
        public String[] replyToChallenge(String name, String instruction, int numPrompts, String[] prompt,
            boolean[] echo) throws Exception
        {
          String[] responses = new String[numPrompts];
          for (int x = 0; x < numPrompts; x++)
          {
            responses[x] = password;
          }
          return responses;
        }
      });
    }

    logger.debug("connected to " + this.username + "@" + this.hostname + ":" + this.port);
  }

  public SshResult executeCommand(String command) throws IOException
  {
    return this.executeCommand(command, timeout);
  }

  /** Execute one command and wait for the result to return */
  public SshResult executeCommand(String command, int timeout) throws IOException
  {
    logger.debug("executing command: " + command);

    try
    {
      /* Create a session */
      sess = conn.openSession();

      // code thanks to SingleThreadStdoutStderr example from ch.ethz.ssh2
      StringBuffer stdOutBuffer = new StringBuffer();
      StringBuffer stdErrBuffer = new StringBuffer();

      InputStream stdout = sess.getStdout();
      InputStream stderr = sess.getStderr();

      // sess.startShell()
      sess.execCommand(command);

      byte[] buffer = new byte[8192];

      while (true)
      {
        if ((stdout.available() == 0) && (stderr.available() == 0))
        {
          /*
           * Even though currently there is no data available, it may
           * be that new data arrives and the session's underlying
           * channel is closed before we call waitForCondition(). This
           * means that EOF and STDOUT_DATA (or STDERR_DATA, or both)
           * may be set together.
           */

          int conditions = sess.waitForCondition(ChannelCondition.STDOUT_DATA | ChannelCondition.STDERR_DATA
              | ChannelCondition.EOF, timeout);

          /* Wait no longer than 2 seconds (= 2000 milliseconds) */

          if ((conditions & ChannelCondition.TIMEOUT) != 0)
          {
            /* A timeout occured. */
            throw new IOException("Timeout while waiting for data from peer.");
          }

          /*
           * Here we do not need to check separately for CLOSED, since
           * CLOSED implies EOF
           */

          if ((conditions & ChannelCondition.EOF) != 0)
          {
            /* The remote side won't send us further data... */

            if ((conditions & (ChannelCondition.STDOUT_DATA | ChannelCondition.STDERR_DATA)) == 0)
            {
              /*
               * ... and we have consumed all data in the local
               * arrival window.
               */
              break;
            }
          }

          /* OK, either STDOUT_DATA or STDERR_DATA (or both) is set. */

          // You can be paranoid and check that the library is not
          // going
          // nuts:
          if ((conditions & (ChannelCondition.STDOUT_DATA | ChannelCondition.STDERR_DATA)) == 0) throw new IOException(
              "Unexpected condition result (" + conditions + ")");
        }

        /*
         * If you below replace "while" with "if", then the way the
         * output appears on the local stdout and stder streams is more
         * "balanced". Addtionally reducing the buffer size will also
         * improve the interleaving, but performance will slightly
         * suffer. OKOK, that all matters only if you get HUGE amounts
         * of stdout and stderr data =)
         */

        while (stdout.available() > 0)
        {
          int len = stdout.read(buffer);
          if (len > 0) // this check is somewhat paranoid
          {
            stdOutBuffer.append(new String(buffer, 0, len, Charset.forName("UTF-8")));
          }
        }

        while (stderr.available() > 0)
        {
          int len = stderr.read(buffer);
          if (len > 0) // this check is somewhat paranoid
          stdErrBuffer.append(new String(buffer, 0, len, Charset.forName("UTF-8")));
        }
      }

      return new SshResult(stdOutBuffer.toString(), stdErrBuffer.toString());
    }
    catch (IOException e)
    {
      throw e;
    }
    finally
    {
      /** Always close the session! */
      sess.close();
    }

  }

  public void close()
  {
    if (lpf != null) try
    {
      lpf.close();
    }
    catch (IOException e)
    {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }
    this.sess.close();
    this.conn.close();
  }

  /**
   * Download remote file to local file via scp
   */
  public void uploadFile(File localFile, String remoteFile) throws IOException
  {
    logger.debug("upload local file '" + localFile + "' to remote file '" + remoteFile + "'");

    SCPClient scp = conn.createSCPClient();

    OutputStream out = new FileOutputStream(localFile);
    try
    {
      // split remote file in directory
      if (remoteFile.contains("/"))
      {
        String dir = remoteFile.substring(0, remoteFile.lastIndexOf("/"));
        String file = remoteFile.substring(remoteFile.lastIndexOf("/") + 1);
        scp.put(localFile.getAbsolutePath(), file, dir, "0600");
      }
      else
      {
        scp.put(localFile.getAbsolutePath(), remoteFile, "", "0600");
      }
      out.flush();
    }
    finally
    {

      out.close();
    }
    logger.debug("upload file complete");
  }

  /**
   * Upload string to remote file.
   *
   * @param string
   *            to upload
   * @param remoteFile
   *            full path including directories
   * @throws IOException
   */
  public void uploadStringToFile(String string, String remoteFile) throws IOException
  {
    logger.debug("upload string to remote file '" + remoteFile + "'");

    SCPClient scp = conn.createSCPClient();

    if (remoteFile.contains("/"))
    {
      String dir = remoteFile.substring(0, remoteFile.lastIndexOf("/"));
      String file = remoteFile.substring(remoteFile.lastIndexOf("/") + 1);
      scp.put(string.getBytes("UTF-8"), file, dir, "0600");
    }
    else
    {
      scp.put(string.getBytes("UTF-8"), remoteFile, "", "0600");
    }
    logger.debug("upload file complete");

  }

  /**
   * Upload a string using scp, with seperate directory and file parameters.
   *
   * @param string
   *            to upload
   * @param remoteFile
   *            only the file name
   * @param remoteDir
   *            path to the directory
   * @throws IOException
   */
  public void uploadStringToFile(String string, String remoteFile, String remoteDir) throws IOException
  {
    if (!"".equals(remoteDir))
    {
      this.uploadStringToFile(string, remoteDir + "/" + remoteFile);
    }
    else
    {
      this.uploadStringToFile(string, remoteFile);
    }

  }

  /**
   * Download remote file to local file via scp
   *
   * @param remoteFile
   * @param localFile
   * @throws IOException
   */
  public void downloadFile(String remoteFile, File localFile) throws IOException
  {
    logger.debug("download remote file '" + remoteFile + "' to local file " + localFile);
    SCPClient scp = conn.createSCPClient();

    OutputStream out = new FileOutputStream(localFile);
    try
    {
      scp.get(remoteFile, out);

      out.flush();
    }
    finally
    {
      out.close();
    }

    logger.debug("download file complete");
  }

  public String downloadFileIntoString(String remoteFile) throws IOException
  {
    logger.debug("download remote file '" + remoteFile);
    SCPClient scp = conn.createSCPClient();

    ByteArrayOutputStream out = new ByteArrayOutputStream();

    scp.get(remoteFile, out);

    return out.toString("UTF-8");
  }

  @Override
  protected void finalize()
  {
    this.close();
  }

  // stupid getters and setters

  public String getHost()
  {
    return hostname;
  }

  public void setHost(String host)
  {
    this.hostname = host;
  }

  public String getUsername()
  {
    return username;
  }

  public void setUsername(String username)
  {
    this.username = username;
  }

  public String getPassword()
  {
    return password;
  }

  public void setPassword(String password)
  {
    this.password = password;
  }

  public int getPort()
  {
    return port;
  }

  public void setPort(int port)
  {
    this.port = port;
  }

  public String downloadFile(String remoteFile) throws IOException
  {
    logger.debug("download remoteFile file '" + remoteFile + "' as string");

    SCPClient scp = conn.createSCPClient();

    ByteArrayOutputStream out = new ByteArrayOutputStream();

    scp.get(remoteFile, out);

    logger.debug("download file complete");

    return out.toString("UTF-8");
  }

}
TOP

Related Classes of org.molgenis.util.Ssh

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.