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

发布于 2024-08-06 01:01:08 字数 25498 浏览 7 评论 0原文

我有一个在 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;
    }
}

我在 Java 程序中执行 ssh 命令,如下所示:

ProcessUtility.executeCommand("ssh " +physicalHostIpAddress + " virsh list \| grep " + newDomUName, false, false, 3600000)

任何人都可以看到我做错了什么吗?顺便说一句,上述代码是使用本文作为指导开发的: http://www.javaworld.com/javaworld/jw-12-2000/jw-1229-traps.html。我对并发编程不是很专家,所以也许我正在做一些愚蠢的事情——如果是这样,请随意指出。

提前非常感谢您的任何建议、想法等。

--James

更新:我现在已经采纳了那些回答我最初问题的乐于助人的人的建议,并编写了一个类,该类提供了以下方法:进行 ssh 和 scp 调用,使用两个 Java ssh 库 jsch (jsch-0.1.31) 和 sshtools (j2ssh-core-0.2.9) 实现。然而,这些实现都不起作用,因为在我有机会执行身份验证之前,它们都在连接步骤中失败了。我预计在运行代码的服务器上会遇到某种配置问题,尽管这并不明显,因为当我在这些服务器上发出 ssh 或 scp 命令时,我可以毫无问题地在这些服务器上执行 ssh 和 scp 命令。命令行。我正在测试代码的 Solaris 服务器由于 ssh -V 而显示以下结果:

Sun_SSH_1.3、SSH 协议 1.5/2.0、OpenSSL 0x0090801f

下面是我为此目的编写的 Java 代码 -- 如果有人可以的话看看我在 Java 代码级别做错了什么,然后请告诉我,如果是这样,请提前感谢您的帮助。

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

I have a Java program which runs in Tomcat and which needs to execute several ssh and scp commands, as well as a few simple commands such as ls on the local machine. I am having trouble with my current approach in that I am getting a time out every time I execute an ssh command. I can run the ssh command on the command line with no problems, but when it's executed from my Java program it times out. I am running the web application in which the ssh commands are executed as root (i.e. I start Tomcat as root user, with my web application code deployed as a WAR file), and as far as I know the correct certification keys are configured on both the local and remote machines, at least I can perform the ssh commands at command line as root without having to enter a user name or password. I do not specify the user name or password in the ssh command that is being executed by my Java program since I assume that I can run the same ssh command in my Java code as I can execute at command line, but maybe this is a false assumption and is the cause of my trouble.

The Java code I have developed to perform command execution is as follows:

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;
    }
}

I execute the ssh commands in my Java program like so:

ProcessUtility.executeCommand("ssh " + physicalHostIpAddress + " virsh list \| grep " +
newDomUName, false, false, 3600000)

Can anyone see what I am doing wrong? BTW the above code was developed using this article as a guide: http://www.javaworld.com/javaworld/jw-12-2000/jw-1229-traps.html. I am not very expert with concurrent programming so perhaps I am doing something boneheaded -- feel free to point it out if so.

Thanks a lot in advance for any suggestions, ideas, etc.

--James

Update: I have now taken the advice of the helpful folks who responded to my original question and have written a class which provides methods for making ssh and scp calls, implemented using two Java ssh libraries jsch (jsch-0.1.31) and sshtools (j2ssh-core-0.2.9). However neither of these implementations are working in that they are both failing at the connect step, before I even get the chance to perform authentication. I expect that I am facing some sort of configuration issue on the servers where I am running the codes, although this is not obvious since I can perform ssh and scp commands on these servers with no problems when I issue the ssh or scp commands on the command line. The Solaris servers that I am testing my code on are showing the following as a result of ssh -V:

Sun_SSH_1.3, SSH protocols 1.5/2.0, OpenSSL 0x0090801f

Below is the Java code I have written for this purpose -- if anyone can see what I am doing wrong at the Java code level then please let me know, and if so thanks a lot in advance for your help.

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

早乙女 2024-08-13 01:01:08

如果您使用 jsch 而不是尝试掏钱,那么您可能会在处理超时和错误方面有更好的运气。 此处有使用示例。

在大多数情况下,从 JSch 获得的错误对于诊断这是连接问题还是逻辑问题会更有帮助。

另外,不确定为什么需要以这种方式使用 ls 。您可以通过这种方式获取文件数组,

 File dir = new File("directory");
 String[] children = dir.list();

而无需解析 ls 的输出。这将更加便携。

You might have better luck dealing with timeout and errors if you use jsch rather than try to shell out. There are usage examples here.

In most cases, the errors you'll get from JSch will be much more helpful in diagnosing if this is a connection issue or a logic problem.

Also, not sure why you need to use ls this way. You can get an array of files this way

 File dir = new File("directory");
 String[] children = dir.list();

Without having to parse the output of ls. This will be much more portable.

桃扇骨 2024-08-13 01:01:08

我的猜测:tomcat 以什么用户身份运行? SSH 如何进行密钥管理?我怀疑用户正在运行,因为没有配置正确的密钥。

My guess: What user is tomcat running as? And how does SSH do key-management? I suspect the user that it's running as doesn't have proper keys configured.

忱杏 2024-08-13 01:01:08

我已经放弃了使用 ssh 库的尝试,而是使用简单的 Runtime.exec() 方法来发出 ssh 和 scp 命令。这是我现在使用的运行良好的代码:

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);
    }
}

如果有人发现此代码存在问题,即我没有发现的可能错误等,请指出。为了解决本文中指出的所有问题,我的原始代码非常复杂且令人费解(http://www.javaworld.com/javaworld/jw-12-2000/jw-1229-traps.html),但直到最近才向我指出这篇文章已经有近 8 年历史了,它警告的许多问题在当前的 Java 版本中不再适用。所以我回到了使用 Runtime.exec() 的基本解决方案,一切似乎都很好。

再次感谢所有试图帮助我解决这个问题的人。

I have abandoned my attempts at using ssh libraries for this and have instead used a bare-bones Runtime.exec() approach for issuing ssh and scp commands. Here's the code that I'm now using which is working well:

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);
    }
}

If anyone sees issues with this code, i.e. possible errors I'm not catching, etc. then please point them out. My original code was quite complicated and convoluted in order to address all of the issues pointed out in this article (http://www.javaworld.com/javaworld/jw-12-2000/jw-1229-traps.html) but only recently was it pointed out to me that that article is almost 8 years old and that many of the issues it warns about are no longer applicable in current releases of Java. So I'm back to a basic solution which uses Runtime.exec() and all seems well.

Thanks again to everyone who tried to help me solve this problem.

风透绣罗衣 2024-08-13 01:01:08

这并不能回答你的问题,但你可以尝试 sshtools lib 来很好地处理 ssh。

一些例子:

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");
}

This doesn't answer your question, but you can try sshtools lib that handles ssh nicely.

Some example:

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");
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文