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.