从被阻止的进程中正常退出

时间:2012-09-27 12:11:09

标签: java process

我正在使用以下代码启动服务(流程)。我的问题如下:

  • 我需要阅读流程的输出以确保它开始
  • 如果它开始了,我回来了,一切都很好
  • 如果由于某种原因没有开始,那么while将会永久阻止,因为进程只是挂起而没有输出任何内容

如果我没有得到预期的字符串,我会如何优雅地退出方法?

ps:我可以用一个Future和一个超时获取,但认为可能有更好的方法。

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 个答案:

答案 0 :(得分:2)

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

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

当然,如果你可以修改服务,另一种方法是监视输出除了进程的标准输出/错误之外的输出,比如PID文件,错误日志消息等等。例如,如果子进程已经创建了一个PID文件,你可以安排检查这个文件而不是标准输入,但实际上它是相同的概念,只是采用不同的方式来使用更好/更简单的代码

答案 1 :(得分: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);
    }
  }
}

我没有测试过这段代码,但应该可以使用。

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

答案 2 :(得分: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();
    }
}