1. 程式人生 > >使用Apache Commons Exec管理程序

使用Apache Commons Exec管理程序

Java管理程序,API級別是使用:Runtime.getRuntime().exec(“shell”);這個方法。

Java在執行命令時輸出到某個Buffer裡,這個Buffer是有容量限制的,如果滿了一直沒讀取,就會一直等待,造成程序鎖死的現象。想要避免這種阻塞有一種簡單的方法,重定向標準輸入流以及標準錯誤流來接收執行命令後的輸出。另一種方法就是使用Apache Commons Exec,可以避免很多類似的坑。 它提供一些常用的方法用來執行外部程序,另外,它提供了監視狗Watchdog來設監視程序的執行超時,同時也還實現了同步和非同步功能, Apache Commons Exec涉及到多執行緒,比如新啟動一個程序,Java中需要再開三個執行緒來處理程序的三個資料流,分別是標準輸入,標準輸出和錯誤輸出。

基本用法
1、建立命令列:CommandLine 
2、建立執行器:DefaultExecutor 
3、用執行器執行命令:executor.execute(cmdLine);
String command= "ping www.baidu.com -t";
final CommandLine commandLine = CommandLine.parse(command);
DefaultExecutor executor = new DefaultExecutor();
int exitValue = executor.execute(commandLine );
關於這個exitValue
: 
execute(String command)方法的返回值,int型別的返回值,一般的程序,其執行結束後,預設值為0。 
對於某些有特殊退出值的程式,需要確定知道其值是什麼,然後用executor.setExitValue(退出值);進行明確設定,否則,會出現ExecuteException。 比如,寫一個JAVA程式,在main函式中,用System.exit(10);退出程式,用Exec管理這個程序就必須設定:executor.setExitValue(10); 如果程式有多個退出值,可使用executor.setExitValues(int[]);函式進行處理。 1、構建命令,官方推薦通過新增引數的方法構建命令,上面的程式碼可以修改如下:
final CommmandLine commandLine = new CommandLine("ping");
conmmandLine.addArgument("www.baidu.com");
conmmandLine.addArgument("-t");
DefaultExecutor executor = new DefaultExecutor();
int exitValue = executor.execute(commandLine);
超時管理-管理程序執行的時間
設定外部命令執行等待的時間,如果外部命令在指定時間內沒有完成,則中斷執行。
final CommandLine commandLine = CommandLine.parse("ping www.baidu.com -t");
ExecuteWatchdog watchdog = new ExecuteWatchdog(5000);//設定超時時間:5秒
DefaultExecutor executor = new DefaultExecutor();
executor.setWatchdog(watchdog);
int exitValue = executor.execute(cmdLine);
非阻塞方式執行程序
上面的執行外部命令都是阻塞式,也就是在執行外部命令時,當前執行緒是阻塞的。 
比如執行ping -t,當前的JAVA程式
會一直等著不會停止。 
解決辦法:使用DefaultExecuteResultHandler處理外部命令執行的結果,釋放當前執行緒。
final CommandLine commandLine = CommandLine.parse("ping www.baidu.com -t");
final DefaultExecuteResultHandler resultHandler = new DefaultExecuteResultHandler();

DefaultExecutor executor = new DefaultExecutor();
executor.execute(commandLine , resultHandler);

//這裡開始的程式碼會被立即執行下去,因為上面的語句不會被阻塞。

resultHandler.waitFor(5000);//等待5秒。
可以使用waitFor來阻塞處理邏輯,比如上面的程式碼,在執行到waitFor時,會等待5秒再繼續執行。 之後,可以通過resultHandler.hasResult()、resultHandler.getExitValue()、resultHandler.getException()獲得需要資訊。 注意:getException();得到的是Exec自己的異常,不是應用程式(比如JAVA)程式碼裡面丟擲的異常。
終止程序
通過Watchdog,可以終止正在執行的程序。
final CommandLine commandLine = CommandLine.parse("ping www.baidu.com -t");
final ExecuteWatchdog watchdog = new ExecuteWatchdog(Integer.MAX_VALUE);
final DefaultExecuteResultHandler resultHandler = new DefaultExecuteResultHandler();
DefaultExecutor executor = new DefaultExecutor();

executor.setWatchdog(watchdog);
executor.execute(commandLine , resultHandler);

Thread.sleep(10000);//等程序執行一會,再終止它
System.out.println("--> Watchdog is watching ? " + watchdog.isWatching());
watchdog.destroyProcess();//終止程序
System.out.println("--> destroyProcess done.");
System.out.println("--> Watchdog is watching ? " + watchdog.isWatching());
System.out.println("--> Watchdog should have killed the process : " + watchdog.killedProcess());
System.out.println("--> wait result is : " + resultHandler.hasResult());
System.out.println("--> exit value is : " + resultHandler.getExitValue());
System.out.println("--> exception is : " + resultHandler.getException());

resultHandler.waitFor(5000);//等待5秒。下面加上上面的幾個System.out,看看程序狀態是什麼。
獲得程序的輸出資訊
可以在程式中,通過PumpStreamHandler,截獲程序的各種輸出,包括output 和 error stream。
String command = "ping www.baidu.com";
final CommandLine commandLine = CommandLine.parse(command );

DefaultExecutor executor = new DefaultExecutor();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
executor.setStreamHandler(new PumpStreamHandler(baos, baos));

executor.setExitValue(1);
int exitValue = executor.execute(commandLine );
final String result = baos.toString().trim();
System.out.println(result);//這個result就是ping輸出的結果。如果是JAVA程式,丟擲了異常,也被它獲取。