2012-09-27 109 views
3

我正在啓動一個服務(進程),下面的代碼。我的問題是:從阻止進程中優雅退出

  • 我需要讀取過程的輸出,以確保它得到啓動
  • ,如果它得到開始,我回來,一切都很好
  • ,如果它不上手無論出於何種原因,在同時將永遠阻止的過程只是掛起,而不輸出任何東西

任何想法我如何能正常退出的方法,如果我沒有得到期望的字符串?

PS:我可以用一個未來超時做它,但還是覺得有可能是一個更好的辦法。

public boolean startService() { 
    try { 
     ProcessBuilder pb = new ProcessBuilder("service.exe"); 
     pb.directory(new File("C:/serviceFolder/")); 
     pb.redirectErrorStream(true); 
     Process p = pb.start(); 
     BufferedReader reader = new BufferedReader(new InputStreamReader(p.getInputStream())); 
     String line; 
     while ((line = reader.readLine()) != null) { 
      if (line.toLowerCase().contains("started")) { 
       return true; 
      } 
     } 
     return false; //I never get there when it fails 
    } catch (IOException e) { 
     throw new RuntimeException("Could not start the service.exe process", e); 
    }   
} 
+3

一個'Future'一個超時*爲*更好的辦法 – Bohemian

回答

2

如果你可以修改服務代碼,最好改變,如果它不能啓動它不掛 - 它應該退出並記錄錯誤消息。這樣你的Java代碼將按原樣工作。

如果你不能,除了設置超時之外沒有別的辦法,因爲你的Java代碼無法知道發生了什麼。

當然,如果您可以修改服務,另一種方法是監視輸出,而不是過程的標準輸出/錯誤,如PID文件,錯誤日誌消息或其他。如果子進程已經創建了一個PID文件,例如,您可以安排在該文件,而不是標準輸入支票,但實際上它是同一個概念,只是適用不同使用更好/更簡單的代碼

0

像這樣的東西應該工作。實質上,在一個單獨的線程中啓動該服務,並創建一個Timer,在一段時間後它會中斷它。請注意,計時器任務是Daemon,因此如果需要退出,它不應阻塞您的進程。

如果reader.readLine()消耗並丟棄中斷,顯然這不起作用。

private static class ServiceRunner implements Runnable { 
    // Am I running? 
    volatile boolean running = true; 
    // My thread. 
    volatile Thread thread = Thread.currentThread(); 

    @Override 
    public void run() { 
    // Start a timer. 
    Timer timer = new Timer("Wait for ServiceRunner to finish.", true); 
    // Fire it after 2 seconds. 
    timer.schedule(new StopTask(), 2000); 
    try { 
     // Start the service. 
     startService(); 
    } finally { 
     // No longer running. 
     running = false; 
    } 
    } 

    class StopTask extends TimerTask { 

    @Override 
    public void run() { 
     if (running) { 
     // Interrupt the service runner. 
     thread.interrupt(); 
     } 
    } 
    } 

    public boolean startService() { 
    try { 
     ProcessBuilder pb = new ProcessBuilder("service.exe"); 
     pb.directory(new File("C:/serviceFolder/")); 
     pb.redirectErrorStream(true); 
     Process p = pb.start(); 
     BufferedReader reader = new BufferedReader(new InputStreamReader(p.getInputStream())); 
     String line; 
     while ((line = reader.readLine()) != null) { 
     if (line.toLowerCase().contains("started")) { 
      return true; 
     } 
     } 
     return false; //I never get there when it fails 
    } catch (IOException e) { 
     throw new RuntimeException("Could not start the service.exe process", e); 
    } 
    } 
} 

我還沒有測試過這段代碼,但它應該可以工作。

您將需要做出調整,以保持服務是否啓動與否。

+0

謝謝,但看起來像未來#重新實現與超時得到。 – assylias

+0

如果超時過期,Future.get會中止操作嗎?這樣做。然而,我同意這可以很容易地通過「FutureTask」實現。 – OldCurmudgeon

+0

超時時,它會嘗試中斷正在運行的任務,在我的情況下會拋出InterruptedException。 – assylias

0

看來,Future#get是更可取的。對於未來的參考,我已經修改了代碼如下方式:

public boolean startService() { 

    Callable<Boolean> start = new Callable<Boolean>() { 
     @Override 
     public Boolean call() throws Exception { 
      ProcessBuilder pb = new ProcessBuilder("service.exe"); 
      pb.directory(new File("C:/serviceFolder/")); 
      pb.redirectErrorStream(true); 
      Process p = pb.start(); 
      BufferedReader reader = new BufferedReader(new InputStreamReader(p.getInputStream())); 
      String line; 
      while ((line = reader.readLine()) != null) { 
       if (line.toLowerCase().contains("started")) { 
        return true; 
       } 
      } 
      return false; 
     } 
    }; 

    ExecutorService executor = Executors.newSingleThreadExecutor(); 
    Future<Boolean> future = executor.submit(start); 

    try { 
     return future.get(1, TimeUnit.SECONDS); 
    } catch (InterruptedException ignore) { 
     Thread.currentThread().interrupt(); 
     return false; 
    } catch (ExecutionException | TimeoutException e) { 
     logger.error("Could not start service", e); 
     return false; 
    } finally { 
     executor.shutdownNow(); 
    } 
}