2008-09-24 46 views
4

我正在嘗試運行一個進程並使用其輸入,輸出和錯誤流進行操作。最顯而易見的方法做,這是使用類似select(),但我能找到在Java中,做的唯一的事情是Selector.select(),這需要一個Channel。這似乎並不可能得到來自InputStreamOutputStreamFileStreamgetChannel()方法,但是,這並不在這裏幫助)如何處理Java中的多個流?

所以一個Channel,而不是我寫了一些代碼來查詢所有流:

while(!out_eof || !err_eof) 
{ 
    while(out_str.available()) 
    { 
     if((bytes = out_str.read(buf)) != -1) 
     { 
      // Do something with output stream 
     } 
     else 
      out_eof = true; 
    } 
    while(err_str.available()) 
    { 
     if((bytes = err_str.read(buf)) != -1) 
     { 
      // Do something with error stream 
     } 
     else 
      err_eof = true; 
    } 
    sleep(100); 
} 

它的工作原理,但它永遠不會終止。當一個流到達文件的末尾,available()返回零所以read()不叫,我們從來沒有得到回報-1將指示EOF。

一個解決方案是,以檢測EOF無阻塞方式。我無法在任何地方看到文檔。或者,有沒有更好的方式來做我想做的事情?

我看到這個問題在這裏: link text ,雖然它並不完全做我想做的,我也許可以使用這個想法,產卵單獨的線程爲每個數據流,爲特定的問題,我現在有。但當然,這不是唯一的方法嗎?當然,必須有一種方法可以從多個流中讀取,而無需爲每個流使用線程?

回答

4

正如您所說,解決方案outlined in this Answer是從Process讀取stdout和stderr的傳統方式。每個線程都是一條線路,儘管它有點煩人。

2

事實上,你將不得不去產卵要監視每個流線程的路線。如果你的用例允許把有問題的進程的stdout和stderr結合起來,你只需要一個線程,否則需要兩個線程。

我花了相當一段時間在我們的一個項目中得到正確的結果,我必須啓動一個外部過程,接受它的輸出並對它做某些事情,同時尋找錯誤和進程終止,能夠在Java應用程序的用戶取消操作時終止它。

我創建了一個相當簡單的類來封裝鑑賞部分,其run()方法看起來是這樣的:

public void run() { 
    BufferedReader tStreamReader = null; 
    try { 
     while (externalCommand == null && !shouldHalt) { 
      logger.warning("ExtProcMonitor(" 
          + (watchStdErr ? "err" : "out") 
          + ") Sleeping until external command is found"); 
      Thread.sleep(500); 
     } 
     if (externalCommand == null) { 
      return; 
     } 
     tStreamReader = 
       new BufferedReader(new InputStreamReader(watchStdErr ? externalCommand.getErrorStream() 
         : externalCommand.getInputStream())); 
     String tLine; 
     while ((tLine = tStreamReader.readLine()) != null) { 
      logger.severe(tLine); 
      if (filter != null) { 
       if (filter.matches(tLine)) { 
        informFilterListeners(tLine); 
        return; 
       } 
      } 
     } 
    } catch (IOException e) { 
     logger.logExceptionMessage(e, "IOException stderr"); 
    } catch (InterruptedException e) { 
     logger.logExceptionMessage(e, "InterruptedException waiting for external process"); 
    } finally { 
     if (tStreamReader != null) { 
      try { 
       tStreamReader.close(); 
      } catch (IOException e) { 
       // ignore 
      } 
     } 
    } 
} 

在主叫方面,它看起來是這樣的:

Thread tExtMonitorThread = new Thread(new Runnable() { 

     public void run() { 
      try { 
       while (externalCommand == null) { 
        getLogger().warning("Monitor: Sleeping until external command is found"); 
        Thread.sleep(500); 
        if (isStopRequested()) { 
         getLogger() 
           .warning("Terminating external process on user request"); 
         if (externalCommand != null) { 
          externalCommand.destroy(); 
         } 
         return; 
        } 
       } 
       int tReturnCode = externalCommand.waitFor(); 
       getLogger().warning("External command exited with code " + tReturnCode); 
      } catch (InterruptedException e) { 
       getLogger().logExceptionMessage(e, "Interrupted while waiting for external command to exit"); 
      } 
     } 
    }, "ExtCommandWaiter"); 

    ExternalProcessOutputHandlerThread tExtErrThread = 
      new ExternalProcessOutputHandlerThread("ExtCommandStdErr", getLogger(), true); 
    ExternalProcessOutputHandlerThread tExtOutThread = 
      new ExternalProcessOutputHandlerThread("ExtCommandStdOut", getLogger(), true); 
    tExtMonitorThread.start(); 
    tExtOutThread.start(); 
    tExtErrThread.start(); 
    tExtErrThread.setFilter(new FilterFunctor() { 

     public boolean matches(Object o) { 
      String tLine = (String)o; 
      return tLine.indexOf("Error") > -1; 
     } 
    }); 

    FilterListener tListener = new FilterListener() { 
     private boolean abortFlag = false; 

     public boolean shouldAbort() { 
      return abortFlag; 
     } 

     public void matched(String aLine) { 
      abortFlag = abortFlag || (aLine.indexOf("Error") > -1); 
     } 

    }; 

    tExtErrThread.addFilterListener(tListener); 
    externalCommand = new ProcessBuilder(aCommand).start(); 
    tExtErrThread.setProcess(externalCommand); 
    try { 
     tExtMonitorThread.join(); 
     tExtErrThread.join(); 
     tExtOutThread.join(); 
    } catch (InterruptedException e) { 
     // when this happens try to bring the external process down 
     getLogger().severe("Aborted because auf InterruptedException."); 
     getLogger().severe("Killing external command..."); 
     externalCommand.destroy(); 
     getLogger().severe("External command killed."); 
     externalCommand = null; 
     return -42; 
    } 
    int tRetVal = tListener.shouldAbort() ? -44 : externalCommand.exitValue(); 

    externalCommand = null; 
    try { 
     getLogger().warning("command exit code: " + tRetVal); 
    } catch (IllegalThreadStateException ex) { 
     getLogger().warning("command exit code: unknown"); 
    } 
    return tRetVal; 

不幸的是我不必爲自給自足的可運行示例,但也許這有助於。 如果我不得不再次這樣做,我會再看看使用Thread.interrupt()方法而不是自制的停止標誌(頭腦聲明它不穩定!),但我留下了另一次。 :)