【问题标题】:Timeout when ssh command is executed from my Java program从我的 Java 程序执行 ssh 命令时超时
【发布时间】:2010-12-03 09:41:19
【问题描述】:

我有一个在 Tomcat 中运行的 Java 程序,它需要在本地机器上执行几个 ssh 和 scp 命令,以及一些简单的命令,例如 ls。我目前的方法遇到问题,因为每次执行 ssh 命令时都会超时。我可以在命令行上毫无问题地运行 ssh 命令,但是当它从我的 Java 程序执行时,它会超时。我正在运行以 root 身份执行 ssh 命令的 Web 应用程序(即,我以 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。我不是并发编程方面的专家,所以也许我正在做一些愚蠢的事情——如果是,请随时指出。

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

--詹姆斯

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

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

【问题讨论】:

  • 您究竟在哪里获得了时间? 'process.waitFor();' ??指定它。

标签: java process ssh runtime command


【解决方案1】:

我已经放弃了为此使用 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() 的基本解决方案,一切看起来都很好。

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

【讨论】:

    【解决方案2】:

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

    【讨论】:

    • 谢谢,丝滑。我以 root 身份运行 Tomcat。我认为这并不重要,因为在我的代码中我获得了用户和密码并使用它们进行身份验证,而且我什至从未进入该步骤,因为代码在两个实现中的 connect() 步骤中都引发了异常(使用 sshtools/j2ssh 和 jsch),这发生在身份验证步骤之前(请参阅我使用 sshtools/j2ssh 和 jsch 提出的两种实现的原始消息的更新)。
    【解决方案3】:

    这并不能回答您的问题,但您可以尝试 sshtools 很好地处理 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");
    }
    

    【讨论】:

    • 使用此库建立连接时出现 IOException:java.io.IOException: The socket is EOF 这发生在 connect() 调用中:SshClient sshClient = new SshClient(); sshClient.setSocketTimeout(超时); sshClient.connect(remoteHost, 22);任何人都可以评论此异常的含义吗?
    • 我添加了快速示例。再次不确定它与您的问题有何关系,抱歉。
    • 感谢 serg555。请查看我对原始问题的更新,其中列出了我用来制作 ssh 命令的代码。我的代码与您在上面演示的完全一样,只是我最初在连接步骤中没有使用 IgnoreHostKeyVerification,但现在我添加了它,但它仍然无法正常工作,在 connect() 调用中失败并出现以下错误:

      严重:传输协议线程失败
      java.io.IOException:套接字为 EOF
    • 对不起,我不知道。也许可以尝试在 serverfault.com 上询问,他们是那里的服务器端专家,也许他们会有一些线索......
    【解决方案4】:

    如果您使用jsch 而不是尝试解决问题,您可能会更好地处理超时和错误。有here的用法示例。

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

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

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

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

    【讨论】:

    • 与主机建立连接时出现以下异常: JSchException: 算法协商失败 这发生在连接步骤: JSch jsch = new JSch();会话 = jsch.getSession(用户,远程主机,22); session.setPassword(密码); session.connect();任何人都可以评论可能导致此错误的原因吗? (顺便说一句,在使用 j2ssh 时,我在连接步骤中也得到了我认为的类似错误,IOException: socket is EOF)
    • 服务器上运行的是什么版本的 sshd/openssl?算法协商失败可能是由于配置错误或非常旧的服务器堆栈。在连接开始时,客户端和服务器列表支持加密算法并决定使用哪个。客户端和服务器上必须有相同的版本。
    • 我使用的是 jsch 版本 0.1.31 和 j2ssh-core 0.2.9。我正在运行我的代码的两台 Solaris 机器由于“ssh -V”命令而显示以下内容:Sun_SSH_1.3、SSH 协议 1.5/2.0、OpenSSL 0x0090801f 如何确定此版本的 OpenSSL 是否兼容这些 ssh Java 库中的任何一个?
    • 你能通过 putty 或其他协议 ssh 进入机器吗?您可能想检查 sshd 使用的是 ssh1 还是 ssh2。您可以通过执行 sshd -d 来检查这一点,并检查调试日志中的协议版本。确保服务器默认为 ssh2
    猜你喜欢
    • 2015-07-16
    • 1970-01-01
    • 1970-01-01
    • 2014-08-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-08
    相关资源
    最近更新 更多