3

I am launching a service (process) with the code below. My issue is as follows:

  • I need to read the output of the process to make sure it gets started
  • if it gets started, I return and everything is fine
  • if it does not get started for whatever reason, the while will block forever as the process just hangs without outputting anything

Any ideas how I could exit the method gracefully if I don't get the expected string?

ps: I could do it with a Future and a timeout on get but thought there might be a better way.

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);
    }        
}
4

3 回答 3

2

如果您可以修改服务代码,最好将其更改为在无法启动时不要挂起 - 它应该退出并记录错误消息。这样,您的 Java 代码将按原样工作。

如果不能,除了设置超时之外别无他法,因为您的 Java 代码无法知道发生了什么。

当然,如果您可以修改服务,另一种方法是监视进程的标准输出/错误以外的输出,例如 PID 文件、错误日志消息或其他任何内容。例如,如果子进程已经创建了一个 PID 文件,您可以安排对该文件而不是标准输入的检查,但实际上这是相同的概念,只是应用不同以使用更好/更简单的代码

于 2012-09-27T12:57:44.113 回答
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();
    }
}
于 2012-09-27T14:38:50.393 回答
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);
    }
  }
}

我没有测试过这段代码,但它应该可以工作。

您将需要进行调整以保留服务是否已启动。

于 2012-09-27T13:38:46.387 回答