从Java程序执行ssh命令时超时

从Java程序执行ssh命令时超时,java,process,ssh,runtime,command,Java,Process,Ssh,Runtime,Command,我有一个在Tomcat中运行的Java程序,它需要在本地机器上执行几个ssh和scp命令,以及一些简单的命令,如ls。我目前的方法有问题,因为每次执行ssh命令时都会超时。我可以在命令行上毫无问题地运行ssh命令,但是当从Java程序执行它时,它会超时。我正在运行一个web应用程序,其中ssh命令以root用户身份执行(即,我以root用户身份启动Tomcat,web应用程序代码部署为WAR文件),据我所知,在本地和远程机器上都配置了正确的认证密钥,至少我可以在命令行以root用户身份执行ssh

我有一个在Tomcat中运行的Java程序,它需要在本地机器上执行几个ssh和scp命令,以及一些简单的命令,如ls。我目前的方法有问题,因为每次执行ssh命令时都会超时。我可以在命令行上毫无问题地运行ssh命令,但是当从Java程序执行它时,它会超时。我正在运行一个web应用程序,其中ssh命令以root用户身份执行(即,我以root用户身份启动Tomcat,web应用程序代码部署为WAR文件),据我所知,在本地和远程机器上都配置了正确的认证密钥,至少我可以在命令行以root用户身份执行ssh命令,而不必输入用户名或密码。我没有在我的Java程序正在执行的ssh命令中指定用户名或密码,因为我假设我可以在Java代码中运行与在命令行中执行相同的ssh命令,但这可能是错误的假设,也是我出现问题的原因

我为执行命令而开发的Java代码如下:

public class ProcessUtility
{

    static Log log = LogFactory.getLog(ProcessUtility.class);

    /**
     * Thread class to be used as a worker
     */
    private static class Worker
        extends Thread
    {
        private final Process process;
        private volatile Integer exitValue;

        Worker(final Process process)
        {
            this.process = process;
        }

        public Integer getExitValue()
        {
            return exitValue;
        }

        @Override
        public void run()
        {
            try
            {
                exitValue = process.waitFor();
            }
            catch (InterruptedException ignore)
            {
                return;
            }
        }
    }

    /**
     * Executes a command.
     * 
     * @param args command + arguments
     */
    public static void execCommand(final String[] args)
    {
        try
        {
            Runtime.getRuntime().exec(args);
        }
        catch (IOException e)
        {
            // swallow it
        }

    }

    /**
     * Executes a command.
     * 
     * @param command
     * @param printOutput
     * @param printError
     * @param timeOut
     * @return
     * @throws java.io.IOException
     * @throws java.lang.InterruptedException
     */
    public static int executeCommand(final String command,
                                     final boolean printOutput,
                                     final boolean printError,
                                     final long timeOut)
    {
        return executeCommandWithWorker(command, printOutput, printError, timeOut);
    }

    /**
     * Executes a command and returns its output or error stream.
     * 
     * @param command
     * @return the command's resulting output or error stream
     */
    public static String executeCommandReceiveOutput(final String command)
    {
        try
        {
            // create the process which will run the command
            Runtime runtime = Runtime.getRuntime();
            final Process process = runtime.exec(command);

            try
            {
                // consume the error and output streams
                StreamGobbler outputGobbler = new StreamGobbler(process.getInputStream(), "OUTPUT", false);
                StreamGobbler errorGobbler = new StreamGobbler(process.getErrorStream(), "ERROR", false);
                outputGobbler.start();
                errorGobbler.start();

                // execute the command
                if (process.waitFor() == 0)
                {
                    return outputGobbler.getInput();
                }
                return errorGobbler.getInput();
            }
            finally
            {
                process.destroy();
            }
        }
        catch (InterruptedException ex)
        {
            String errorMessage = "The command [" + command + "] did not complete due to an unexpected interruption.";
            log.error(errorMessage, ex);
            throw new RuntimeException(errorMessage, ex);
        }
        catch (IOException ex)
        {
            String errorMessage = "The command [" + command + "] did not complete due to an IO error.";
            log.error(errorMessage, ex);
            throw new RuntimeException(errorMessage, ex);
        }
    }

    /**
     * Executes a command.
     * 
     * @param command
     * @param printOutput
     * @param printError
     * @param timeOut
     * @return
     * @throws java.io.IOException
     * @throws java.lang.InterruptedException
     */
    @SuppressWarnings("unused")
    private static int executeCommandWithExecutors(final String command,
                                                   final boolean printOutput,
                                                   final boolean printError,
                                                   final long timeOut)
    {
        // validate the system and command line and get a system-appropriate command line 
        String massagedCommand = validateSystemAndMassageCommand(command);

        try
        {
            // create the process which will run the command
            Runtime runtime = Runtime.getRuntime();
            final Process process = runtime.exec(massagedCommand);

            // consume and display the error and output streams
            StreamGobbler outputGobbler = new StreamGobbler(process.getInputStream(), "OUTPUT", printOutput);
            StreamGobbler errorGobbler = new StreamGobbler(process.getErrorStream(), "ERROR", printError);
            outputGobbler.start();
            errorGobbler.start();

            // create a Callable for the command's Process which can be called by an Executor 
            Callable<Integer> call = new Callable<Integer>()
            {
                public Integer call()
                    throws Exception
                {
                    process.waitFor();
                    return process.exitValue();
                }
            };

            // submit the command's call via an Executor and get the result from a Future
            ExecutorService executorService = Executors.newSingleThreadExecutor();
            try
            {
                Future<Integer> futureResultOfCall = executorService.submit(call);
                int exitValue = futureResultOfCall.get(timeOut, TimeUnit.MILLISECONDS);
                return exitValue;
            }
            catch (TimeoutException ex)
            {
                String errorMessage = "The command [" + command + "] timed out.";
                log.error(errorMessage, ex);
                throw new RuntimeException(errorMessage, ex);
            }
            catch (ExecutionException ex)
            {
                String errorMessage = "The command [" + command + "] did not complete due to an execution error.";
                log.error(errorMessage, ex);
                throw new RuntimeException(errorMessage, ex);
            }
            finally
            {
                executorService.shutdown();
                process.destroy();
            }
        }
        catch (InterruptedException ex)
        {
            String errorMessage = "The command [" + command + "] did not complete due to an unexpected interruption.";
            log.error(errorMessage, ex);
            throw new RuntimeException(errorMessage, ex);
        }
        catch (IOException ex)
        {
            String errorMessage = "The command [" + command + "] did not complete due to an IO error.";
            log.error(errorMessage, ex);
            throw new RuntimeException(errorMessage, ex);
        }
    }

    /**
     * Executes a command.
     * 
     * @param command
     * @param printOutput
     * @param printError
     * @param timeOut
     * @return
     * @throws java.io.IOException
     * @throws java.lang.InterruptedException
     */
    private static int executeCommandWithWorker(final String command,
                                                final boolean printOutput,
                                                final boolean printError,
                                                final long timeOut)
    {
        // validate the system and command line and get a system-appropriate command line 
        String massagedCommand = validateSystemAndMassageCommand(command);

        try
        {
            // create the process which will run the command
            Runtime runtime = Runtime.getRuntime();
            Process process = runtime.exec(massagedCommand);

            // consume and display the error and output streams
            StreamGobbler outputGobbler = new StreamGobbler(process.getInputStream(), "OUTPUT", printOutput);
            StreamGobbler errorGobbler = new StreamGobbler(process.getErrorStream(), "ERROR", printError);
            outputGobbler.start();
            errorGobbler.start();

            // create and start a Worker thread which this thread will join for the timeout period 
            Worker worker = new Worker(process);
            worker.start();
            try
            {
                worker.join(timeOut);
                Integer exitValue = worker.getExitValue();
                if (exitValue != null)
                {
                    // the worker thread completed within the timeout period

                    // stop the output and error stream gobblers
                    outputGobbler.stopGobbling();
                    errorGobbler.stopGobbling();

                    return exitValue;
                }

                // if we get this far then we never got an exit value from the worker thread as a result of a timeout 
                String errorMessage = "The command [" + command + "] timed out.";
                log.error(errorMessage);
                throw new RuntimeException(errorMessage);
            }
            catch (InterruptedException ex)
            {
                worker.interrupt();
                Thread.currentThread().interrupt();
                throw ex;
            }
            finally
            {
                process.destroy();
            }
        }
        catch (InterruptedException ex)
        {
            String errorMessage = "The command [" + command + "] did not complete due to an unexpected interruption.";
            log.error(errorMessage, ex);
            throw new RuntimeException(errorMessage, ex);
        }
        catch (IOException ex)
        {
            String errorMessage = "The command [" + command + "] did not complete due to an IO error.";
            log.error(errorMessage, ex);
            throw new RuntimeException(errorMessage, ex);
        }
    }

    /**
     * Validates that the system is running a supported OS and returns a system-appropriate command line.
     * 
     * @param originalCommand
     * @return
     */
    private static String validateSystemAndMassageCommand(final String originalCommand)
    {
        // make sure that we have a command
        if (originalCommand.isEmpty() || (originalCommand.length() < 1))
        {
            String errorMessage = "Missing or empty command line parameter.";
            log.error(errorMessage);
            throw new RuntimeException(errorMessage);
        }

        // make sure that we are running on a supported system, and if so set the command line appropriately
        String massagedCommand;
        String osName = System.getProperty("os.name");
        if (osName.equals("Windows XP"))
        {
            massagedCommand = "cmd.exe /C " + originalCommand;
        }
        else if (osName.equals("Solaris") || osName.equals("SunOS") || osName.equals("Linux"))
        {
            massagedCommand = originalCommand;
        }
        else
        {
            String errorMessage = "Unable to run on this system which is not Solaris, Linux, or Windows XP (actual OS type: \'" +
                                  osName + "\').";
            log.error(errorMessage);
            throw new RuntimeException(errorMessage);
        }

        return massagedCommand;
    }
}

class StreamGobbler
    extends Thread
{
    static private Log log = LogFactory.getLog(StreamGobbler.class);
    private InputStream inputStream;
    private String streamType;
    private boolean displayStreamOutput;
    private final StringBuffer inputBuffer = new StringBuffer();
    private boolean keepGobbling = true;

    /**
     * Constructor.
     * 
     * @param inputStream the InputStream to be consumed
     * @param streamType the stream type (should be OUTPUT or ERROR)
     * @param displayStreamOutput whether or not to display the output of the stream being consumed
     */
    StreamGobbler(final InputStream inputStream,
                  final String streamType,
                  final boolean displayStreamOutput)
    {
        this.inputStream = inputStream;
        this.streamType = streamType;
        this.displayStreamOutput = displayStreamOutput;
    }

    /**
     * Returns the output stream of the
     * 
     * @return
     */
    public String getInput()
    {
        return inputBuffer.toString();
    }

    /**
     * Consumes the output from the input stream and displays the lines consumed if configured to do so.
     */
    @Override
    public void run()
    {
        InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
        BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
        try
        {
            String line = null;
            while (keepGobbling && inputStreamReader.ready() && ((line = bufferedReader.readLine()) != null))
            {
                inputBuffer.append(line);
                if (displayStreamOutput)
                {
                    System.out.println(streamType + ">" + line);
                }
            }
        }
        catch (IOException ex)
        {
            log.error("Failed to successfully consume and display the input stream of type " + streamType + ".", ex);
            ex.printStackTrace();
        }
        finally
        {
            try
            {
                bufferedReader.close();
                inputStreamReader.close();
            }
            catch (IOException e)
            {
                // swallow it
            }
        }
    }

    public void stopGobbling()
    {
        keepGobbling = false;
    }
}
公共类ProcessUtility
{
静态日志Log=LogFactory.getLog(ProcessUtility.class);
/**
*要用作辅助线程的线程类
*/
私有静态类工作者
延长线程
{
私人最终过程;
私有可变整数exitValue;
工人(最终工艺)
{
这个过程=过程;
}
公共整数getExitValue()
{
返回现有值;
}
@凌驾
公开募捐
{
尝试
{
exitValue=process.waitFor();
}
捕获(中断异常忽略)
{
返回;
}
}
}
/**
*执行命令。
* 
*@param args命令+参数
*/
公共静态void execCommand(最终字符串[]args)
{
尝试
{
Runtime.getRuntime().exec(args);
}
捕获(IOE异常)
{
//吞下去
}
}
/**
*执行命令。
* 
*@param命令
*@param打印输出
*@param打印错误
*@param超时
*@返回
*@抛出java.io.IOException
*@java.lang.InterruptedException
*/
公共静态int executeCommand(最终字符串命令,
最终布尔打印输出,
最终布尔打印错误,
最终长超时)
{
返回executeCommandWithWorker(命令、打印输出、打印错误、超时);
}
/**
*执行命令并返回其输出或错误流。
* 
*@param命令
*@返回命令的结果输出或错误流
*/
公共静态字符串executeCommandReceiveOutput(最终字符串命令)
{
尝试
{
//创建将运行该命令的进程
Runtime=Runtime.getRuntime();
最终进程=runtime.exec(命令);
尝试
{
//使用错误和输出流
StreamGobbler outputGobbler=新的StreamGobbler(process.getInputStream(),“OUTPUT”,false);
StreamGobbler errorGobbler=新的StreamGobbler(process.getErrorStream(),“ERROR”,false);
outputGobbler.start();
errorGobbler.start();
//执行命令
if(process.waitFor()==0)
{
返回outputGobbler.getInput();
}
返回errorGobbler.getInput();
}
最后
{
process.destroy();
}
}
捕获(中断异常例外)
{
String errorMessage=“由于意外中断,命令[“+命令+”]未完成。”;
日志错误(errorMessage,ex);
抛出新的运行时异常(errorMessage,ex);
}
捕获(IOEX异常)
{
String errorMessage=“由于IO错误,命令[“+命令+”]未完成。”;
日志错误(errorMessage,ex);
抛出新的运行时异常(errorMessage,ex);
}
}
/**
*执行命令。
* 
*@param命令
*@param打印输出
*@param打印错误
*@param超时
*@返回
*@抛出java.io.IOException
*@java.lang.InterruptedException
*/
@抑制警告(“未使用”)
私有静态int executeCommandWithExecutors(最终字符串命令,
最终布尔打印输出,
最终布尔打印错误,
最终长超时)
{
//验证系统和命令行,并获得系统适当的命令行
String massagedCommand=validateSystemAndMassageCommand(命令);
尝试
{
//创建将运行该命令的进程
Runtime=Runtime.getRuntime();
最终进程=runtime.exec(massagedCommand);
//使用并显示错误和输出流
StreamGobbler outputGobbler=新的StreamGobbler(process.getInputStream(),“OUTPUT”,printOutput);
StreamGobbler errorGobbler=新的StreamGobbler(process.getErrorStream(),“ERROR”,printError);
outputGobbler.start();
errorGobbler.start();
//为可由执行者调用的命令进程创建可调用的
Callable call=new Callable()
{
public class SecureCommandUtility
{
    static Log log = LogFactory.getLog(SecureCommandUtility.class);

    /**
     * Performs a secure copy of a single file (using scp).
     * 
     * @param localFilePathName
     * @param username
     * @param password
     * @param remoteHost
     * @param remoteFilePathName
     * @param timeout
     */
    public static void secureCopySingleFile(final String localFilePathName,
                                            final String username,
                                            final String password,
                                            final String remoteHost,
                                            final String remoteFilePathName,
                                            final int timeout)
    {
        // basic validation of the parameters
        if ((localFilePathName == null) || localFilePathName.isEmpty())
        {
            // log the error and throw an exception
            String errorMessage = "Error executing the secure copy -- the supplied local file path name parameter is null or empty.";
            log.error(errorMessage);
            throw new LifecycleException(errorMessage);
        }
        if ((username == null) || username.isEmpty())
        {
            // log the error and throw an exception
            String errorMessage = "Error executing the secure copy -- the supplied user name parameter is null or empty.";
            log.error(errorMessage);
            throw new LifecycleException(errorMessage);
        }
        if ((password == null) || password.isEmpty())
        {
            // log the error and throw an exception
            String errorMessage = "Error executing the secure copy -- the supplied password parameter is null or empty.";
            log.error(errorMessage);
            throw new LifecycleException(errorMessage);
        }
        if ((remoteHost == null) || remoteHost.isEmpty())
        {
            // log the error and throw an exception
            String errorMessage = "Error executing the secure copy -- the supplied remote host parameter is null or empty.";
            log.error(errorMessage);
            throw new LifecycleException(errorMessage);
        }
        if ((remoteFilePathName == null) || remoteFilePathName.isEmpty())
        {
            // log the error and throw an exception
            String errorMessage = "Error executing the secure copy -- the supplied remote file path name parameter is null or empty.";
            log.error(errorMessage);
            throw new LifecycleException(errorMessage);
        }
        if (timeout < 1000)
        {
            // log the error and throw an exception
            String errorMessage = "Error executing the secure copy -- the supplied timeout parameter is less than one second.";
            log.error(errorMessage);
            throw new LifecycleException(errorMessage);
        }

        //secureCopySingleFileJSch(localFilePathName, username, password, remoteHost, remoteFilePathName);
        secureCopySingleFileJ2Ssh(localFilePathName, username, password, remoteHost, remoteFilePathName, timeout);
    }

    /**
     * 
     * @param user
     * @param password
     * @param remoteHost
     * @param command
     * @return exit status of the command
     */
    public static int secureShellCommand(final String user,
                                         final String password,
                                         final String remoteHost,
                                         final String command,
                                         final int timeout)
    {
        // basic validation of the parameters
        if ((user == null) || user.isEmpty())
        {
            // log the error and throw an exception
            String errorMessage = "Error executing the ssh command \'" + command +
                                  "\': the supplied user name parameter is null or empty.";
            log.error(errorMessage);
            throw new LifecycleException(errorMessage);
        }
        if ((password == null) || password.isEmpty())
        {
            // log the error and throw an exception
            String errorMessage = "Error executing the ssh command \'" + command +
                                  "\': the supplied password parameter is null or empty.";
            log.error(errorMessage);
            throw new LifecycleException(errorMessage);
        }
        if ((remoteHost == null) || remoteHost.isEmpty())
        {
            // log the error and throw an exception
            String errorMessage = "Error executing the ssh command \'" + command +
                                  "\': the supplied remote host parameter is null or empty.";
            log.error(errorMessage);
            throw new LifecycleException(errorMessage);
        }
        if ((command == null) || command.isEmpty())
        {
            // log the error and throw an exception
            String errorMessage = "Error executing the ssh command: the supplied command parameter is null or empty.";
            log.error(errorMessage);
            throw new LifecycleException(errorMessage);
        }
        if (timeout < 1000)
        {
            // log the error and throw an exception
            String errorMessage = "Error executing the ssh command \'" + command +
                                  "\': the supplied timeout parameter is less than one second.";
            log.error(errorMessage);
            throw new LifecycleException(errorMessage);
        }

        //return secureShellCommandJsch(user, password, remoteHost, command, timeout);
        return secureShellCommandJ2Ssh(user, password, remoteHost, command, timeout);
    }

    /**
     * Performs a secure copy of a single file (using scp).
     * 
     * @param localFilePathName
     * @param username
     * @param password
     * @param remoteHost
     * @param remoteFilePathName
     * @param timeout
     */
    private static void secureCopySingleFileJ2Ssh(final String localFilePathName,
                                                  final String username,
                                                  final String password,
                                                  final String remoteHost,
                                                  final String remoteFilePathName,
                                                  final int timeout)
    {
        SshClient sshClient = null;
        try
        {
            // create and connect client
            sshClient = new SshClient();
            sshClient.setSocketTimeout(timeout);
            sshClient.connect(remoteHost, 22, new IgnoreHostKeyVerification());

            // perform password-based authentication
            PasswordAuthenticationClient passwordAuthenticationClient = new PasswordAuthenticationClient();
            passwordAuthenticationClient.setUsername(username);
            passwordAuthenticationClient.setPassword(password);
            if (sshClient.authenticate(passwordAuthenticationClient) != AuthenticationProtocolState.COMPLETE)
            {
                // log the error and throw an exception
                String errorMessage = "Failed to copy \'" + localFilePathName + "\' to \'" + remoteHost + ":" +
                                      remoteFilePathName + "\' -- failed to authenticate using username/password \'" +
                                      username + "\'/\'" + password + "\'.";
                log.error(errorMessage);
                throw new LifecycleException(errorMessage);
            }

            // perform the copy
            sshClient.openScpClient().put(localFilePathName, remoteFilePathName, false);
        }
        catch (Exception ex)
        {
            // log the error and throw an exception
            String errorMessage = "Failed to copy \'" + localFilePathName + "\' to \'" + remoteHost + ":" +
                                  remoteFilePathName + "\'.";
            log.error(errorMessage, ex);
            throw new LifecycleException(errorMessage, ex);
        }
        finally
        {
            if ((sshClient != null) && sshClient.isConnected())
            {
                sshClient.disconnect();
            }
        }
    }

    /**
     * Performs a secure copy of a single file (using scp).
     * 
     * @param localFilePathName
     * @param user
     * @param password
     * @param remoteHost
     * @param remoteFilePathName
     */
    @SuppressWarnings("unused")
    private static void secureCopySingleFileJSch(final String localFilePathName,
                                                 final String user,
                                                 final String password,
                                                 final String remoteHost,
                                                 final String remoteFilePathName)
    {
        Session session = null;
        Channel channel = null;
        FileInputStream fileInputStream = null;
        try
        {
            // create and connect Jsch session
            JSch jsch = new JSch();
            session = jsch.getSession(user, remoteHost, 22);
            session.setPassword(password);
            session.connect();

            // exec 'scp -p -t remoteFilePathName' remotely
            String command = "scp -p -t " + remoteFilePathName;
            channel = session.openChannel("exec");
            ((ChannelExec) channel).setCommand(command);

            // get the I/O streams for the remote scp
            OutputStream outputStream = channel.getOutputStream();
            InputStream inputStream = channel.getInputStream();

            // connect the channel
            channel.connect();

            int ackCheck = checkAck(inputStream);
            if (checkAck(inputStream) != 0)
            {
                // log the error and throw an exception
                String errorMessage = "The scp command failed -- input stream ACK check failed with the following result: " +
                                      ackCheck;
                log.error(errorMessage);
                throw new LifecycleException(errorMessage);
            }

            // send "C0644 filesize filename", where filename should not include '/'
            long filesize = (new File(localFilePathName)).length();
            command = "C0644 " + filesize + " ";
            if (localFilePathName.lastIndexOf('/') > 0)
            {
                command += localFilePathName.substring(localFilePathName.lastInde
 File dir = new File("directory");
 String[] children = dir.list();
SshClient ssh = = new SshClient();
ssh.connect(server, port, new IgnoreHostKeyVerification());
PasswordAuthenticationClient pwd = new PasswordAuthenticationClient();
pwd.setUsername(uid);
pwd.setPassword(password);
int result = ssh.authenticate(pwd);
if (result == AuthenticationProtocolState.COMPLETE) {
    SessionChannelClient session = ssh.openSessionChannel();
    session.executeCommand("sh test.sh");
}
public static void executeSecureCommand(final String user,
                                        final String remoteHost,
                                        final String command)
{
    // basic validation of the parameters
    if ((user == null) || user.isEmpty())
    {
        // log the error and throw an exception
        String errorMessage = "Error executing the ssh command \'" + command +
                              "\': the supplied user name parameter is null or empty.";
        log.error(errorMessage);
        throw new LifecycleException(errorMessage);
    }
    if ((remoteHost == null) || remoteHost.isEmpty())
    {
        // log the error and throw an exception
        String errorMessage = "Error executing the ssh command \'" + command +
                              "\': the supplied remote host parameter is null or empty.";
        log.error(errorMessage);
        throw new LifecycleException(errorMessage);
    }
    if ((command == null) || command.isEmpty())
    {
        // log the error and throw an exception
        String errorMessage = "Error executing the ssh command: the supplied command parameter is null or empty.";
        log.error(errorMessage);
        throw new LifecycleException(errorMessage);
    }

    // create and execute a corresponding ssh command
    String sshCommand = "ssh " + user + "@" + remoteHost + " " + command;
    try
    {
        executeShellCommand(sshCommand);
    }
    catch (Exception ex)
    {
        // log the error and throw an exception
        String errorMessage = "Error executing the secure shell command \'" + sshCommand + "\'";
        log.error(errorMessage, ex);
        throw new LifecycleException(errorMessage, ex);
    }
}

public static void executeSecureFileCopy(final String localFilePathName,
                                         final String user,
                                         final String remoteHost,
                                         final String remoteFilePathName)
{
    // basic validation of the parameters
    if ((localFilePathName == null) || localFilePathName.isEmpty())
    {
        // log the error and throw an exception
        String errorMessage = "Error executing the secure copy -- the supplied local file path name parameter is null or empty.";
        log.error(errorMessage);
        throw new LifecycleException(errorMessage);
    }
    if ((user == null) || user.isEmpty())
    {
        // log the error and throw an exception
        String errorMessage = "Error executing the secure copy -- the supplied user name parameter is null or empty.";
        log.error(errorMessage);
        throw new LifecycleException(errorMessage);
    }
    if ((remoteHost == null) || remoteHost.isEmpty())
    {
        // log the error and throw an exception
        String errorMessage = "Error executing the secure copy -- the supplied remote host parameter is null or empty.";
        log.error(errorMessage);
        throw new LifecycleException(errorMessage);
    }
    if ((remoteFilePathName == null) || remoteFilePathName.isEmpty())
    {
        // log the error and throw an exception
        String errorMessage = "Error executing the secure copy -- the supplied remote file path name parameter is null or empty.";
        log.error(errorMessage);
        throw new LifecycleException(errorMessage);
    }

    try
    {
        // create an scp command we'll use to perform the secure file copy
        String scpCommand = "scp -B -C -q " + localFilePathName + " " + user + "@" + remoteHost + ":" +
                            remoteFilePathName;

        // execute the scp command
        executeShellCommand(scpCommand);
    }
    catch (Exception ex)
    {
        // log the error and throw an exception
        String errorMessage = "Failed to copy local file \'" + localFilePathName + "\' to remote host:file \'" +
                              remoteHost + ":" + remoteFilePathName + "\'.";
        log.error(errorMessage, ex);
        throw new LifecycleException(errorMessage, ex);
    }
}

public static void executeShellCommand(final String command)
{
    try
    {
        // create and execute a runtime process which runs the command
        Process process = Runtime.getRuntime().exec(new String[] { "/bin/sh", "-c", command });

        // gobble the input stream
        InputStream processInputStream = process.getInputStream();
        BufferedReader processInputStreamReader = new BufferedReader(new InputStreamReader(processInputStream));
        String inputStreamLine = processInputStreamReader.readLine();
        while (inputStreamLine != null)
        {
            inputStreamLine = processInputStreamReader.readLine();
        }

        // capture the error stream
        InputStream processErrorStream = process.getErrorStream();
        BufferedReader processErrorStreamReader = new BufferedReader(new InputStreamReader(processErrorStream));
        String errorStreamLine = processErrorStreamReader.readLine();
        StringBuffer errorBuffer = new StringBuffer();
        while (errorStreamLine != null)
        {
            errorBuffer.append(errorStreamLine);
            errorStreamLine = processErrorStreamReader.readLine();
        }

        // close the streams
        processInputStream.close();
        processErrorStream.close();

        // wait for the process to finish and return the exit code
        process.waitFor();
        if (process.exitValue() != 0)
        {
            // log the error and throw an exception
            String errorMessage = "Failed to execute the shell command \'" + command + "\' -- Error: \'" +
                                  errorBuffer.toString() + "\'";
            log.error(errorMessage);
            throw new LifecycleException(errorMessage);
        }
    }
    catch (Exception ex)
    {
        // log the error and throw an exception
        String errorMessage = "Failed to execute the shell command \'" + command + "\'.";
        log.error(errorMessage, ex);
        throw new LifecycleException(errorMessage, ex);
    }
}