/* * Copyright (C) 2006-2016 DLR, Germany * * All rights reserved * * http://www.rcenvironment.de/ */ package de.rcenvironment.core.utils.ssh.jsch.executor; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.util.Map; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import com.jcraft.jsch.ChannelExec; import com.jcraft.jsch.JSchException; import com.jcraft.jsch.Session; import de.rcenvironment.core.utils.executor.AbstractCommandLineExecutor; import de.rcenvironment.core.utils.executor.CommandLineExecutor; import de.rcenvironment.core.utils.ssh.jsch.JschFileTransfer; /** * A {@link CommandLineExecutor} that delegates execution over an established JSch connection. The * current implementation expects the remote system to provide a bash-like shell, and will therefore * not work with a standard Windows host. * * Note that this class is not thread-safe. * * @author Robert Mischke */ public class JSchCommandLineExecutor extends AbstractCommandLineExecutor implements CommandLineExecutor { private static final String SLASH = "/"; private static final int TERMINATION_POLLING_INTERVAL_MSEC = 1000; private static final String EXCEPTION_MESSAGE_NOT_RUNNING = "Not running"; private static final String EXCEPTION_MESSAGE_ALREADY_RUNNING = "Already running"; private Session jschSession; private String remoteWorkDir; private ChannelExec executionChannel; private InputStream stdOutStream; private InputStream stdErrStream; private Log log = LogFactory.getLog(getClass()); /** * @param jschSession an established JSch session * @param remoteWorkDir the path of the remote work directory; this can be either absolute, or * relative to the default SSH "home" directory */ public JSchCommandLineExecutor(Session jschSession, String remoteWorkDir) { this.jschSession = jschSession; this.remoteWorkDir = remoteWorkDir; } @Override public InputStream getStderr() throws IOException { if (executionChannel == null) { throw new IllegalStateException(EXCEPTION_MESSAGE_NOT_RUNNING); } return stdErrStream; } @Override public InputStream getStdout() throws IOException { if (executionChannel == null) { throw new IllegalStateException(EXCEPTION_MESSAGE_NOT_RUNNING); } return stdOutStream; } @Override public String getWorkDirPath() { return remoteWorkDir; } @Override public void start(String commandString) throws IOException { start(commandString, null); } @Override public void start(String commandString, InputStream stdinStream) throws IOException { if (executionChannel != null) { throw new IllegalStateException(EXCEPTION_MESSAGE_ALREADY_RUNNING); } StringBuilder command = new StringBuilder(); for (Map.Entry<String, String> entry : env.entrySet()) { command.append("export "); command.append(entry.getKey()); command.append("="); command.append(entry.getValue()); command.append(" && "); } command.append("cd "); command.append(remoteWorkDir); command.append(" && "); command.append(commandString); try { executionChannel = (ChannelExec) jschSession.openChannel("exec"); String fullCommand = command.toString(); log.debug("Full invocation command: " + fullCommand); executionChannel.setCommand(fullCommand); if (stdinStream != null) { executionChannel.setInputStream(stdinStream); } // as stated in the JavaDoc of ChannelExec, 'getInputStream()' and 'getErrStream()' should be called before 'connect()' stdOutStream = executionChannel.getInputStream(); stdErrStream = executionChannel.getExtInputStream(); executionChannel.connect(); } catch (JSchException e) { throw new IOException(e); } } @Override public int waitForTermination() throws IOException, InterruptedException { if (executionChannel == null) { throw new IllegalStateException(EXCEPTION_MESSAGE_NOT_RUNNING); } try { while (!executionChannel.isClosed()) { Thread.sleep(TERMINATION_POLLING_INTERVAL_MSEC); } return executionChannel.getExitStatus(); } finally { // note: this is called AFTER getExitStatus during normal execution flow executionChannel.disconnect(); executionChannel = null; } } @Override public void downloadWorkdir(File localDir) throws IOException { try { JschFileTransfer.downloadDirectory(jschSession, remoteWorkDir, localDir); } catch (JSchException e) { throw new IOException(e); } } @Override public void uploadFileToWorkdir(File localFile, String remoteLocation) throws IOException { try { JschFileTransfer.uploadFile(jschSession, localFile, remoteWorkDir + SLASH + remoteLocation); } catch (JSchException e) { throw new IOException(e); } } @Override public void downloadFileFromWorkdir(String remoteLocation, File localFile) throws IOException { try { JschFileTransfer.downloadFile(jschSession, remoteWorkDir + SLASH + remoteLocation, localFile); } catch (JSchException e) { throw new IOException(e); } } @Override public void uploadDirectoryToWorkdir(File localDirectory, String remoteLocation) throws IOException { try { JschFileTransfer.uploadDirectory(jschSession, localDirectory, remoteWorkDir + SLASH + remoteLocation); } catch (JSchException e) { throw new IOException(e); } catch (InterruptedException e) { throw new IOException(e); } } @Override public void downloadDirectoryFromWorkdir(String remoteLocation, File localDirectory) throws IOException { try { JschFileTransfer.downloadDirectory(jschSession, remoteWorkDir + SLASH + remoteLocation, localDirectory); } catch (JSchException e) { throw new IOException(e); } } @Override public void downloadFile(String remoteSource, File remoteTarget) throws IOException { try { JschFileTransfer.downloadFile(jschSession, remoteSource, remoteTarget); } catch (JSchException e) { throw new IOException(e); } } @Override public void downloadDirectory(String remoteLocation, File localDirectory) throws IOException { try { JschFileTransfer.downloadDirectory(jschSession, remoteLocation, localDirectory); } catch (JSchException e) { throw new IOException(e); } } @Override public void uploadFile(File localFile, String remoteLocation) throws IOException { try { JschFileTransfer.uploadFile(jschSession, localFile, remoteLocation); } catch (JSchException e) { throw new IOException(e); } } @Override public void uploadDirectory(File localDirectory, String remoteLocation) throws IOException { try { JschFileTransfer.uploadDirectory(jschSession, localDirectory, remoteLocation); } catch (JSchException e) { throw new IOException(e); } catch (InterruptedException e) { throw new IOException(e); } } @Override public void remoteCopy(String remoteSource, String remoteTarget) throws IOException { try { JschFileTransfer.remoteToRemoteCopy(jschSession, remoteSource, remoteTarget); } catch (JSchException e) { throw new IOException(e); } catch (InterruptedException e) { throw new IOException(e); } } }