2010-03-17 58 views
2

即時通訊嘗試爲包裝提供一個設計,以便在調用java中的命令行實用程序時使用。 runtime.exec()的問題在於你需要繼續讀取流出的錯誤流,或者在填充緩衝區時掛起。這使我下面的設計:圍繞命令行實用程序的包裝設計

public class CommandLineInterface { 
    private final Thread stdOutThread; 
    private final Thread stdErrThread; 
    private final OutputStreamWriter stdin; 
    private final History history; 


    public CommandLineInterface(String command) throws IOException { 
     this.history = new History(); 
     this.history.addEntry(new HistoryEntry(EntryTypeEnum.INPUT, command)); 
     Process process = Runtime.getRuntime().exec(command); 
     stdin = new OutputStreamWriter(process.getOutputStream()); 
     stdOutThread = new Thread(new Leech(process.getInputStream(), history, EntryTypeEnum.OUTPUT)); 
     stdOutThread.setDaemon(true); 
     stdOutThread.start(); 
     stdErrThread = new Thread(new Leech(process.getErrorStream(), history, EntryTypeEnum.ERROR)); 
     stdErrThread.setDaemon(true); 
     stdErrThread.start(); 
    } 

    public void write(String input) throws IOException { 
     this.history.addEntry(new HistoryEntry(EntryTypeEnum.INPUT, input)); 
     stdin.write(input); 
     stdin.write("\n"); 
     stdin.flush(); 
    } 
} 

而且

public class Leech implements Runnable{ 
    private final InputStream stream; 
    private final History history; 
    private final EntryTypeEnum type; 
    private volatile boolean alive = true; 


    public Leech(InputStream stream, History history, EntryTypeEnum type) { 
     this.stream = stream; 
     this.history = history; 
     this.type = type; 
    } 

    public void run() { 
     BufferedReader reader = new BufferedReader(new InputStreamReader(stream)); 
     String line; 
     try { 
      while(alive) { 
       line = reader.readLine(); 
       if (line==null) break; 
       history.addEntry(new HistoryEntry(type, line)); 
      } 
     } catch (Exception e) { 
      e.printStackTrace(); 
     } 
    } 
} 

我的問題是與水蛭類(用於「螞蟥」的過程出來,犯錯流,並將它們送入歷史 - 這就像一個日誌文件) - 一方面,讀取整行很好,很容易(以及目前正在做的),但這意味着我錯過了最後一行(通常是提示行)。我只在執行下一個命令時看到提示行(因爲在那之前沒有換行符)。另一方面,如果我自己閱讀角色,如何知道過程何時「完成」? (完成或等待輸入) 有沒有人嘗試過等待100毫秒,因爲從過程的最後輸出並聲明它「已完成」?

任何更好的想法,我怎麼可以實現一個很好的包裝周圍的東西,如runtime.exec(「cmd.exe」)?

回答

2

使用PlexusUtils Apache Maven 2使用它來執行所有外部進程。

+0

不是我正在尋找的東西,但可能是最接近的我即將獲得。 謝謝 – radai 2010-03-23 19:56:18

+0

我對這個庫沒有任何問題,並且因爲它被信任在Maven 2中使用,我會說它是一樣好。 – 2010-03-24 14:05:19

+0

我並不是想暗示代碼的質量。我只是說它並不是爲進程和java代碼之間的任何真正的交互而設計的 - 只是爲了運行外部進程並獲得單一結果 – radai 2010-03-24 20:45:53

1

我自己也在尋找同樣的東西,並且我找到了一個Expect的Java端口,名爲ExpectJ。我還沒有嘗試過,但它看起來很有前途

+0

有趣,但它似乎不處理錯誤。如果我「期待」某個運行的某個輸出並打印出某個錯誤,那麼當它超時時,它只會知道它... – radai 2010-03-22 04:47:48

0

我會讀入流中的輸入,然後將其寫入ByteArrayOutputStream。字節數組將繼續增長,直到不再有可用的字節讀取。此時,您將通過將字節數組轉換爲字符串並將其在平臺line.separator上拆分,將數據刷新到歷史記錄。然後,您可以迭代這些行以添加歷史記錄條目。 ByteArrayOutputStream然後被重置,並且while循環阻塞,直到有更多的數據或到達流的末尾(可能是因爲該過程完成)。

public void run() { 
    ByteArrayOutputStream out = new ByteArrayOutputStream(); 

    int bite; 
    try { 
     while((bite = stream.read()) != -1) { 
      out.write(bite); 

      if (stream.available() == 0) { 
       String string = new String(out.toByteArray()); 
       for (String line : string.split(
         System.getProperty("line.separator"))) { 
        history.addEntry(new HistoryEntry(type, line)); 
       } 

       out.reset(); 
      } 
     } 
    } catch (Exception e) { 
     e.printStackTrace(); 
    } 
} 

這將確保你拿起最後一行輸入,它解決了你知道流何時結束的問題。

+0

你認爲一次性執行過程,我運行一個命令,等待它的輸出並且而已。 我希望能夠處理更復雜的命令行應用程序,如wget等可能不會寫很長時間的任何東西,並且可能不是一次性的 – radai 2010-03-23 19:37:55