Run SSH from Java

Filed under: Java, Linux, — Tags: Automation, ssh — Thomas Sundberg — 2014-08-27

Suppose that you need to do something from a Java program on a remote Linux server? How can you do that?

One thing we know is that Linux servers usually supports ssh and that you can do everything you need from a command line. In other words, you need a Java implementation of ssh so you can execute whatever you need on the remote host. Next problem is to either implement ssh yourself or find an implementation that you can use. If you decide not to implement ssh yourself, you will probably prefer a self contained implementation so you don't have to include more dependencies than necessary. This is a good use case for Ganymed SSH-2 for Java. The only thing left is to:

  1. Include a dependency in your Maven, Gradle or Ant/Ivy project
  2. Implement whatever you want to do

I will show you one solution to 1 and 2 above.

Let me start with the dependency I found at The Central Repository:

<dependency>
    <groupId>ch.ethz.ganymed</groupId>
    <artifactId>ganymed-ssh2</artifactId>
    <version>262</version>
</dependency>

The dependency above is a self contained ssh implementation for Java. Neat. Next step is to use it. The example I will implement has a pom that looks like this:

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project>
    <modelVersion>4.0.0</modelVersion>
    <groupId>se.thinkcode.blog</groupId>
    <artifactId>ssh-from-java</artifactId>
    <version>1.0-SNAPSHOT</version>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.1</version>
                <configuration>
                    <source>1.7</source>
                    <target>1.7</target>
                </configuration>
            </plugin>
        </plugins>
    </build>
    <dependencies>
        <dependency>
            <groupId>ch.ethz.ganymed</groupId>
            <artifactId>ganymed-ssh2</artifactId>
            <version>262</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.11</version>
            <scope>test</scope>
        </dependency>
    </dependencies>
</project>

My Maven installation default to an old language version so I set it to use Java 7 both for source code and the target class files.

With the Maven project above, it is time to implement something that actually will use ssh to communicate with a remote server. Let me implement an ssh client. Since I want to test stuff when I implement them, I will start with a test class.

src/test/java/se/thinkcode/SshClientTest.java

package se.thinkcode;

import org.junit.Test;

import java.io.IOException;
import java.util.List;

import static junit.framework.TestCase.assertFalse;

public class SshClientTest {
    @Test
    public void shouldSeeFileListing() throws IOException {
        // Replace userName, password and host with your specific values
        String userName = "aUserName";
        String password = "password";
        String host = "hostName";
        String path = "/";

        SshClient sshClient = new SshClient();
        List<String> actual = sshClient.listFiles(userName, password, host, path);

        for (String s: actual) {
            System.out.println(s);
        }

        assertFalse("The list should contain a few files", actual.isEmpty());
    }
}

It doesn't do a lot, it just runs my client. I print whatever I get back to the console and I assert that the result isn't empty. This test isn't portable since it will only run in the environment where I have access to a specific server. But it will be sufficient for me to iterate through my implementation as I write it.

The implementation of my ssh client looks like this:

src/main/java/se/thinkcode/SshClient.java

package se.thinkcode;

import ch.ethz.ssh2.Connection;
import ch.ethz.ssh2.Session;
import ch.ethz.ssh2.StreamGobbler;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.LinkedList;
import java.util.List;

public class SshClient {

    public List<String> listFiles(String userName, String password, String host, String path) throws IOException {
        Connection connection = null;
        try {
            connection = connectTo(host, userName, password);
            return listFiles(path, connection);
        } finally {
            if (connection != null) {
                connection.close();
            }
        }
    }

    private Connection connectTo(String host, String userName, String password) throws IOException {
        Connection connection = new Connection(host);
        connection.connect();
        connection.authenticateWithPassword(userName, password);

        return connection;
    }

    private List<String> listFiles(String path, Connection connection) throws IOException {
        String command = "ls -la " + path;
        List<String> result = new LinkedList<>();
        Session session = null;

        try {
            session = connection.openSession();
            session.execCommand(command);
            InputStream stdout = new StreamGobbler(session.getStdout());

            try (BufferedReader br = new BufferedReader(new InputStreamReader(stdout))) {
                String line = br.readLine();
                while (line != null) {
                    result.add(line);
                    line = br.readLine();
                }
            }
        } finally {
            if (session != null) {
                session.close();
            }
        }

        return result;
    }
}

I expect that the user will supply me with a user name, password, host and a path so I can connect to the proper host with correct credentials and list all files at the path the user is interested in.

User name, password and host is enough for me to connect to the interesting host.

When I have a connection, I want to use it. Listing the files is just a matter of executing the command ls -la for the desired path. The result is then converted to a list of strings. The last thing I do is being a good citizen and close the session.

The three files that are needed are the files above. They should be located like this:

example
|-- pom.xml
`-- src
    |-- main
    |   `-- java
    |       `-- se
    |           `-- thinkcode
    |               `-- SshClient.java
    `-- test
        `-- java
            `-- se
                `-- thinkcode
                    `-- SshClientTest.java

I use Maven to build this.

mvn clean test

This will build and test my implementation.

Conclusion

Communicating with ssh from java is possible to do. It is not very complicated and there are self contained libraries that you can use.

Resources



(less...)

Pages

About
Events
Why

Categories

Agile
Automation
BDD
Clean code
Continuous delivery
Continuous deployment
Continuous integration
Cucumber
Culture
Design
DevOps
Executable specification
Git
Gradle
Guice
J2EE
JUnit
Java
Javascript
Kubernetes
Linux
Load testing
Maven
Mockito
New developers
Pair programming
PicoContainer
Presentation
Programming
Public speaking
Quality
React
Recruiting
Requirements
Scala
Selenium
Software craftsmanship
Software development
Spring
TDD
Teaching
Technical debt
Test automation
Tools
Web
Windows
eXtreme Programming

Authors

Thomas Sundberg
Adrian Bolboaca

Archives

Meta

rss RSS