我正在使用以下代码启动服务(流程)。我的问题如下:
如果我没有得到预期的字符串,我会如何优雅地退出方法?
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);
}
}
答案 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();
}
}