Commons VFS, SSHJ and JSch in Comparison

Some weeks ago I evaluated some SSH libraries for Java. The main requirements to them are file transferring and file operations on a remote machine. Therefore, it exists a network protocol based on SSH, SSH File Transfer Protocol (or SFTP). So I needed a SSH library that supports SFTP. A research shows that it exits many SSH libraries for Java. I reduce the number of libraries to three for the comparison. I choose JSch, SSHJ and Apache's Commons VFS for a deeper look. All of them support SFTP. JSch seems to be the de-facto standard for Java. SSHJ is a newer library. Its goal is to have a clear Java API for SSH. The goal of Commons VFS is to have a clear API for virtual file systems and SFTP is one of the supported protocol. Under the hood it uses JSch for the SFTP protocol. The libraries should cover following requirements:

  • client authentication over password
  • client authentication over public key
  • server authentication
  • upload files from local host over SFTP
  • download files to local host over SFTP
  • file operations on the remote host like move, delete, list all children of a given folder (filtering after type like file or folder) over SFTP
  • execute plain shell commands

Lets have a deeper look how the three libraries cover the requirements.

Client Authentication

All three libraries supports both required authentication methods. SSHJ has the clearest API for authentication (SSHClient.authUserPass(), SSHClient.authUserPublicKey()).

1SSHClient sshClient= new SSHClient();
2sshClient.connect(host);
3
4// only for public key authentication
5sshClient.authPublickey("user", "location to private key file");
6
7// only for password authentication
8sshClient.authPassword("user", "password");

In Commons VFS the authentication configuration depends which kind of authentication should be used. For the public key authentication, the private key has to set in the FileSystemOption and the user name is a part of the connection url. For the password authentication, user name and password is a part of the connection url.

 1StandardFileSystemManager fileSystemManager = new StandardFileSystemManager();
 2fileSystemManager.init();
 3
 4// only for public key authentication
 5SftpFileSystemConfigBuilder sftpConfigBuilder = SftpFileSystemConfigBuilder.getInstance();
 6FileSystemOptions opts = new FileSystemOptions();
 7sftpConfigBuilder.setIdentities(opts, new File[]{privateKey.toFile()});
 8String connectionUrl = String.format("sftp://%s@%s", user, host);
 9
10// only for password authentication
11String connectionUrl = String.format("sftp://%s:%s@%s", user, password, host);
12
13// Connection set-up
14FileObject remoteRootDirectory = fileSystemManager.resolveFile(connectionUrl, connectionOptions);

The authentication configuration in JSch is similar to Commons VFS. It depends which kind of authentication should be used. The private key for the public key authentication has to be configured in the JSch object and the password for the password authentication has to be set in the Session object. For both, the user name is set, when the JSch object gets the Session object.

 1JSch sshClient = new JSch();
 2
 3// only for public key authentication
 4sshClient.addIdentity("location to private key file");
 5
 6session = sshClient.getSession(user, host);
 7
 8// only for password authentication
 9session.setPassword(password);
10
11session.connect();

Server Authentication

All three libraries supports server authentication. In SSHJ the server authentication can be enabled with SSHClient.loadKnownHost(). It is possible to  add an own location of known_host file or it is used the default location that depends on the using platform.

1SSHClient sshClient = new SSHClient();
2sshClient.loadKnownHosts(); // or sshClient.loadKnownHosts(knownHosts.toFile());
3sshClient.connect(host);

In Commons VFS the server authentication configuration is also a part of the FileSystemOption like the public key authentication. There, the location of the known_hosts file can be set.

1SftpFileSystemConfigBuilder sftpConfigBuilder = SftpFileSystemConfigBuilder.getInstance();
2FileSystemOptions opts = new FileSystemOptions();
3sftpConfigBuilder.setKnownHosts(opts, new File("location of the known_hosts file"));

In JSch it exists two possibilities to configure the server authentication. One possibility is to use the OpenSSHConfig (see JSch example for OpenSSHConfig). The another possibility is easier. The location of the known_hosts file can be set directly in JSch object.

1JSch sshClient = new JSch();
2sshClient.setKnownHosts("location of known-hosts file");

Upload/download Files Over SFTP

All three libraries supports uploads and downloads files over SFTP. SSHJ has very clear API for these operations. The SSHClient object creates a SFTPClient object. This object is responsible for the upload (SFTPClient.put()) and for the download (SFTPClient.get()).

1SSHClient sshClient = new SSHClient();
2// ... connection
3
4try (SFTPClient sftpClient = sshClient.newSFTPClient()) {
5  // download
6  sftpClient.get(remotePath, new FileSystemFile(local.toFile()));
7  // upload
8  sftpClient.put(new FileSystemFile(local.toFile()), remotePath);
9}

In Commons VFS the upload and download files is abstracted as operation on a file system. So both are represented by the copyFrom method of a FileObject object. Upload is a copyFrom operation on a RemoteFile object and download is a copyFrom operation on a LocalFile.

 1StandardFileSystemManager fileSystemManager = new StandardFileSystemManager();
 2// ... configuration
 3remoteRootDirectory = fileSystemManager.resolveFile(connectionUrl, connectionOptions);
 4
 5LocalFile localFileObject = (LocalFile) fileSystemManager.resolveFile(local.toUri().toString());
 6FileObject remoteFileObject = remoteRootDirectory.resolveFile(remotePath);
 7try {
 8  // download
 9  localFileObject.copyFrom(remoteFileObject, new AllFileSelector());
10
11  // upload
12  remoteFileObject.copyFrom(localFileObject, new AllFileSelector());
13} finally {
14  localFileObject.close();
15  remoteFileObject.close();
16}

JSch also supports a SFTPClient. In JSch it is called ChannelSFTP. It has two method for download (ChannelSFTP.get()) and upload (ChannelSFTP.put()).

 1// here: creation and configuration of session
 2
 3ChannelSftp sftpChannel = null;
 4try {
 5  sftpChannel = (ChannelSftp) session.openChannel("sftp");
 6  sftpChannel.connect();
 7
 8  // download
 9  InputStream inputStream = sftpChannel.get(remotePath);
10  Files.copy(inputStream, localPath);
11
12  // upload
13  OutputStream outputStream = sftpChannel.put(remotePath);
14  Files.copy(locaPathl, outputStream);
15} catch (SftpException | JSchException ex) {
16  throw new IOException(ex);
17} finally {
18  if (sftpChannel != null) {
19    sftpChannel.disconnect();
20  }
21}

Execute Shell Commands

Only Commons VFS doesn't support executing plain shell commands. In SSHJ it is a two-liner. The SshClient starts a new Session object. This object executes the shell command. It is very intuitive.

1// creation and configuration of sshClient
2
3try (Session session = sshClient.startSession()) {
4  session.exec("ls");
5}

In Jsch the ChannelExec is responsible for executing shell commands over SSH. At first the command is set in the channel and then the channel has to be started. It isn't so intuitive than in SSHJ.

 1// here: creation and configuration of session object
 2
 3ChannelExec execChannel = null;
 4try {
 5  execChannel = (ChannelExec) session.openChannel("exec");
 6  execChannel.connect();
 7  execChannel.setCommand(command);
 8  execChannel.start();
 9} catch (JSchException ex) {
10  throw new IOException(ex);
11} finally {
12  if (execChannel != null) {
13    execChannel.disconnect();
14  }
15}

File Operations On the Remote Hosts

All libraries supports more or less ideal file operations over SFTP on remote machines. In SSHJ SFTPClient has also methods for file operations. The names of the methods are the same as the file operations on a Linux system. The following code snippet shows how to delete a file.

1//here: creation and configuration of sshClient
2
3try (SFTPClient sftpClient = sshClient.newSFTPClient()) {
4  sftpClient.rm(remotePath);
5}

Commons VFS's core functionality is file operations. The usage takes getting used to. A file object has to be resolve and the file operations can be done on it.

1// here: creation and configuration of remoteRootDirectory
2
3FileObject remoteFileObject = remoteRootDirectory.resolveFile(remotePath);
4try {
5  remoteFileObject.delete();
6} finally {
7  remoteFileObject.close();
8}

JSch's SFTPClient ChannelSFTP has also method for file operations. The mostly file operations are supported by this channel. For e.g. the file copy operation on the remote machine has to be done by plain shell commands over the ChannelExec.

 1// here: creation and configuration of session
 2ChannelSftp sftpChannel = null;
 3try {
 4  sftpChannel = (ChannelSftp) session.openChannel("sftp");
 5  sftpChannel.connect();
 6  sftpChannel.rm(remotePath);
 7} catch (SftpException | JSchException ex) {
 8  throw new IOException(ex);
 9} finally {
10  if (sftpChannel != null) {
11    sftpChannel.disconnect();
12  }
13}

Conclusion

After this comparison I have two favourites, SSHJ and Commons VFS. SSHJ has a very clear API and I would choose it if I need a common SSH client or file operation support over SFTP is sufficient. I would choose Commons VFS if I have file operation over many file system protocols or a common SSH client is not needed. For the case, that I need both, I could use JSch directly to execute commands over SSH. The API of Commons VFS takes getting used to. But after understanding the concept behind, the usage of the API is straightforward. The whole source code examples of this comparison are hosted on Github.

  1. SSHJ homepage
  2. JSch homepage
  3. Commons-vfs homepage
  4. Wikipedia page about SFTP
  5. Source Code of this comparison on Github