Java Process.waitFor()和Readline挂起

时间:2012-10-09 19:01:25

标签: java multithreading powershell vmware

首先,这是我的代码:

import java.io.*;
import java.util.Date;

import com.banctecmtl.ca.vlp.shared.exceptions.*;

public class PowershellTest implements Runnable {

public static final String PATH_TO_SCRIPT = "C:\\Scripts\\ScriptTest.ps1";
public static final String SERVER_IP = "XX.XX.XX.XXX";
public static final String MACHINE_TO_MOD = "MachineTest";

/**
 * @param args
 * @throws OperationException 
 */
public static void main(String[] args) throws OperationException {

    new PowershellTest().run();

}

public PowershellTest(){}

@Override
public synchronized void run() {
    String input = "";
    String error = "";

    boolean isHanging = false;

    try {

        Runtime runtime = Runtime.getRuntime();
        Process proc = runtime.exec("powershell -file " + PATH_TO_SCRIPT +" "+ SERVER_IP +" "+ MACHINE_TO_MOD);
        proc.getOutputStream().close();
        InputStream inputstream = proc.getInputStream();
        InputStreamReader inputstreamreader = new InputStreamReader(inputstream);
        BufferedReader bufferedreader = new BufferedReader(inputstreamreader);

        proc.waitFor();

        String line;
        while (!isHanging && (line = bufferedreader.readLine()) != null) {
            input += (line + "\n");
            Date date = new Date();
            while(!bufferedreader.ready()){

                this.wait(1000);
                //if its been more then 1 minute since a line has been read, its hanging. 
                if(new Date().getTime() - date.getTime() >= 60000){

                    isHanging = true;
                    break;

                }
            }
        }

        inputstream.close();
        inputstream = proc.getErrorStream();
        inputstreamreader = new InputStreamReader(inputstream);
        bufferedreader = new BufferedReader(inputstreamreader);
        isHanging = false;

        while (!isHanging && (line = bufferedreader.readLine()) != null) {
            error += (line + "\n");
            Date date = new Date();
            while(!bufferedreader.ready()){

                this.wait(1000);
                //if its been more then 1 minute since a line has been read, its hanging. 
                if(new Date().getTime() - date.getTime() >= 60000){

                    isHanging = true;
                    break;

                }
            }
        }
        inputstream.close();

        proc.destroy();

    } catch (IOException e) {
        //throw new OperationException("File IO problem.", e);
    } catch (InterruptedException e) {
        //throw new OperationException("Script thread problem.",e);
    }
    System.out.println("Error : " + error + "\nInput : " + input);
}

}

我目前正在尝试运行一个PowerShell脚本,它将启动/停止远程服务器上的虚拟机(VMWARE)。 脚本在命令行中运行,此代码也是如此。问题是,我讨厌如何使用一个线程(让它等待脚本响应,如进一步解释的那样)来完成这样的工作。我不得不这样做,因为BufferedReader.readline()和proc.waitFor()都会永远挂起。

从cmd运行时,脚本执行起来很长。从服务器验证验证并执行实际脚本,它停止30秒到1分钟。从我从调试中看到的,当readline开始从脚本中接收到这些延迟时,它就会挂起。

我也很确定这不是内存问题,因为我在任何调试会话中都没有任何OOM错误。

现在我明白Process.waitFor()要求我从错误流和常规流中刷新缓冲区才能工作,这就是为什么我不使用它(我需要输出来管理VM特定的错误) ,证书问题等。)。

我想知道是否有人可以向我解释为什么它会挂起,如果有办法只使用典型的readline()而没有让它如此难以挂起。即使脚本应该已经结束了一段时间,它仍然挂起(我试图同时运行java应用程序和cmd命令使用我在java应用程序中使用的完全相同的东西,让它运行1小时,没有任何工作)。它不只是在在while循环中,readline()就是悬挂的地方。

这也是一个测试版本,无处接近最终代码,所以请饶恕我:这应该是一个常数,这是无用的等等我稍后会清理代码。显然,我的代码中的IP不是XX.XX.XX.XXX。

对于如何修复的解释或建议将不胜感激。

这是我目前使用的脚本:

Add-PSSnapin vmware.vimautomation.core

Connect-VIServer -server $args[0]

Start-VM -VM "MachineTest"

如果您需要更多细节,我会尽量给予。

提前感谢您的帮助!

编辑:我之前也使用要求较低的脚本测试了代码,其中的工作是获取文件的内容并进行打印。由于无需等待获取信息,因此readline()运行良好。因此,我非常确定问题在于来自脚本执行的等待时间。

另外,原谅我的错误,英语不是我的主要语言。

提前感谢您的帮助!

EDIT2:因为我无法回答我自己的问题:

这是我使用线程后的“最终”代码:

import java.io.*;

public class PowershellTest implements Runnable {

public InputStream is;

public PowershellTest(InputStream newIs){

    this.is = newIs;
}

@Override
public synchronized void run() {
    String input = "";
    String error = "";

    try {


        InputStreamReader inputstreamreader = new InputStreamReader(is);
        BufferedReader bufferedreader = new BufferedReader(inputstreamreader);

        String line;
        while ((line = bufferedreader.readLine()) != null) {
            input += (line + "\n");

        }

        is.close();

    } catch (IOException e) {
        //throw new OperationException("File IO problem.", e);
    }
    System.out.println("Error : " + error + "\nInput : " + input);
}

}

主要简单地创建和启动2个线程(PowerShellTest实例),1个带有errorStream,1个带有inputStream。

我相信当我第一次编写应用程序并在某种程度上修复它时我犯了一个愚蠢的错误,因为我一遍又一遍地重写了代码。它仍然需要5-6分钟才能运行,这在某种程度上是相似的,如果不长于我之前的代码(这是合乎逻辑的,因为errorStream和inputStream在我的情况下按顺序获取它们的信息)。

无论如何,多亏了你的所有答案,尤其是Miserable Variable,因为他提到了线程。

2 个答案:

答案 0 :(得分:5)

首先,在您完成阅读流之前,请不要致电waitFor()。我强烈建议您查看ProcessBuilder而不是简单地使用Runtime.exec,然后自己拆分命令,而不是依靠Java为您执行此操作:

ProcessBuilder pb = new ProcessBuilder("powershell", "-file", PATH_TO_SCRIPT,
        SERVER_IP, MACHINE_TO_MOD);
pb.redirectErrorStream(true); // merge stdout and stderr
Process proc = pb.start();

redirectErrorStream将错误输出合并到正常输出中,因此您只需阅读proc.getInputStream()。然后,您应该只能读取该流,直到EOF,然后调用proc.waitFor()

答案 1 :(得分:4)

在开始阅读inputStream之前,您目前正在等待从errorStream完成阅读。如果进程在stderr之前写入stdout,则可能会陷入死锁状态。

尝试从并发运行的线程读取两个流。当您在此时,也请删除proc.getOutputStream().close();。它不应该影响行为,但也不需要。