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:
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.
Communicating with ssh from java is possible to do. It is not very complicated and there are self contained libraries that you can use.