Exit gracefully from blocked process - java

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

If you can modify the service code, it's better to change it not to hang if it can't start - it should exit and log an error message. This way your Java code will work as-is.
If you can't, there is no other way beside setting a timeout, because your Java code has no means to know what's going on.
Of course, if you can modify the service, an alternative is to watch for output other then the process's standard output/error, like a PID file, an error log message, or whatever. If the subprocess already creates a PID file, for example, you can schedule a check on this file instead of the standard input, but really it's the same concept, just applied differently to use nicer/simpler code

Something like this should work. Essentially, start the service in a separate thread and create a Timer that interrupts it after a certain period. Note that the timer task is a Daemon so it should not hold up your process if it needs to quit.
Obviously this will not work if reader.readLine() consumes and discards interrupts.
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);
}
}
}
I have not tested this code but it should work.
You will need to make adjustments to retain whether the service started or not.

It seems that the Future#get approach is preferred. For future reference, I have modified the code in the following way:
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();
}
}

Related

Java ProcessBuilder.start() taking considerably longer than executing in a terminal

I have a GUI that starts a new thread (getFilesThread) when the user clicks a button. This thread invokes the start() method of an instance of my ClearCaseProcess class below (my organization won't let me use the ClearCase Java API), and when the process(es) ends, it updates the GUI.
private static class ClearCaseProcess {
private ArrayList<String> stdout = new ArrayList<>();
private ArrayList<String> stderr = new ArrayList<>();
private ProcessBuilder pb = null;
public ClearCaseProcess(ArrayList<String> commands, String dir) throws IOException {
pb = new ProcessBuilder(commands);
pb.directory(new File(dir));
}
public void start() throws IOException {
long minStart = System.nanoTime();
Process process = pb.start();
Thread sout = new Thread() {
#Override
public void run() {
BufferedReader out = new BufferedReader(
new InputStreamReader(process.getInputStream()));
String outLine = "";
try {
while ((outLine = out.readLine()) != null) {
stdout.add(outLine);
System.out.println(outLine);
}
} catch (IOException ex) {
System.err.println(ex.getMessage());
}
}
};
sout.start();
Thread serr = new Thread() {
#Override
public void run() {
BufferedReader err = new BufferedReader(
new InputStreamReader(process.getErrorStream()));
String errLine = "";
try {
while ((errLine = err.readLine()) != null) {
stderr.add(errLine);
System.err.println(errLine);
}
} catch (IOException ex) {
System.err.println(ex.getMessage());
}
}
};
serr.start();
try {
process.waitFor();
long execTime = System.nanoTime() - minStart;
System.out.println("Process '" + description + "' took " + execTime);
} catch (InterruptedException ex) {
System.err.println(ex.getMessage());
}
}
}
getFiles() needs to gather data from four different ClearCaseProcesses. I plan to run these concurrently in four threads. Each of these threads has two associated auxiliary threads to consume stdout and stderr, as shown above. I'm assuming that this will be faster than running ClearCase four times sequentially. For now, however, I'm testing just one ClearCase call.
My issue is that the time elapsed between calling Process.start() and the return of Process.waitFor() is much longer (about 5 minutes) than the time elapsed when I run the same ClearCase command on the terminal (about 1.5 minutes). I suspect that my loops reading stdout and stderr are the culprits, because even my print statements are slow to produce output in NetBeans' console. How can I speed this up?
For my specific case, running (not debugging) inside the IDE was causing the overhead.
When ran from the console (java -jar myExecutable.jar), the times were very close.
Noobish mistake.

Java Stop Process and Close Streams

I'm using ProcessBuilder to launch an external process, but I need to be able to kill it. Right now I have no problem killing the process but for some reason the error stream doesn't close so the thread reading the stream never finishes. This keeps me from closing my program.
Here's where I start the threads reading from the input and error streams.
final Thread inputPrinter = new Thread() {
public void run() {
BufferedReader inputStream = new BufferedReader(new InputStreamReader(builder.getInputStream()));
String line;
try {
while ((line = inputStream.readLine()) != null) {
Util.println(line, false);
}
} catch (IOException e) {
} finally {
Util.println("input end");
try {
inputStream.close();
} catch (IOException e) {
}
}
}
};
inputPrinter.start();
Thread errorPrinter = new Thread() {
public void run() {
BufferedReader errorStream = new BufferedReader(new InputStreamReader(builder.getErrorStream()));
String line;
try {
while ((line = errorStream.readLine()) != null) {
Util.println(line, true);
}
} catch (IOException e) {
} finally {
Util.println("error end");
try {
errorStream.close();
} catch (IOException e) {
}
}
}
};
errorPrinter.start();
builder.waitFor();
Util.println("");
Util.println("Finished building project.");
Here's my code for stopping the process.
try {
builder.getOutputStream().close();
builder.getInputStream().close();
builder.getErrorStream().close();
} catch (IOException e) {
e.printStackTrace();
}
builder.destroy();
Util.println("");
Util.println("Build aborted by user.", true);
When I try to stop the process I get the following printed.
Build aborted by user.
Finished building project.
input end
I never get "error end" and debugging the program shows the thread is just sitting at "readLine()".
The code that waits for the process is running in it's own thread (separate from the code that kills the process).
What do I need to do to make sure that the errorPrinter thread dies?
I had the same problem,i use a speechRecognizer,so i am running a separate Thread which is running another .jar which prints to console and read the output using BufferedReader(something like this..):
//In seperate Thread from the Main App Thread
while (!stopped) {
while ((line = bufferedReader.readLine()) != null && !line.isEmpty()) {
System.out.println(line);
checkSpeechResult(line);
}
}
The problem
Basically the bufferedRead.readLine() lags until it has something to read.
If nothing comes it will wait forever.
Answer:
From another Thread call this:
process.destroy();
and it will stop the process so the bufferedRead.readLine() will exit.

How to successfully destroy the process if it takes too much time in Java?

I am executing a shell script from my Java program using Process and I want to kill/destroy that process if my script takes long time. What is the best way to do this?
Below is my code:
public static void main(String[] args) throws IOException, InterruptedException {
// Your script
String script = "#!/bin/bash\n\necho \"Hello World\"\n\n readonly PARAM1=$param1\n echo $PARAM1\n\nreadonly PARAM2=$param2\n echo $PARAM2\n\n";
// create a temp file and write your script to it
File tempScript = File.createTempFile("temp_scripts_", "");
tempScript.setExecutable(true);
try (OutputStream output = new FileOutputStream(tempScript)) {
output.write(script.getBytes());
}
// build the process object and start it
List<String> commandList = new ArrayList<>();
commandList.add(tempScript.getAbsolutePath());
ProcessBuilder builder = new ProcessBuilder(commandList);
builder.redirectErrorStream(true);
builder.environment().put("param1", "abc");
builder.environment().put("param2", "xyz");
Process shell = builder.start();
// read the output and show it
try (BufferedReader reader = new BufferedReader(new InputStreamReader(shell.getInputStream()))) {
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
}
// wait for the process to finish
// but I want to kill/destroy the process if it takes too much time
int exitCode = shell.waitFor();
// delete your temp file
tempScript.delete();
// check the exit code (exit code = 0 usually means "executed ok")
System.out.println("EXIT CODE: " + exitCode);
}
UPDATE
The class Process doesn't have a "waitFor" method with a timeout unless you're using java 8. As an alternative, you can try starting a thread that waits for the process to finish and join such thread with join(timeout).
The following is a proof of concept with your code, modified to work with a thread:
import java.io.*;
import java.util.*;
public class Test {
public static void main(String[] args) throws IOException, InterruptedException {
// Your script
String script = getScriptFromSomewhere();
// create a temp file and write your script to it
File tempScript = File.createTempFile("temp_scripts_", "");
tempScript.setExecutable(true);
try (OutputStream output = new FileOutputStream(tempScript)) {
output.write(script.getBytes());
}
// build the process object and start it
List<String> commandList = new ArrayList<>();
commandList.add(tempScript.getAbsolutePath());
ProcessBuilder builder = new ProcessBuilder(commandList);
builder.redirectErrorStream(true);
builder.environment().put("param1", "abc");
builder.environment().put("param2", "xyz");
Process shell = builder.start();
// Start the interrupting thread
long timeoutMillis = 5000;
ExecutingThread thread = new ExecutingThread(shell, timeoutMillis);
thread.start();
// read the output and show it
byte[] buffer = new byte[1024];
try (InputStream input = shell.getInputStream()) {
int read;
while ((read = input.read(buffer)) != -1) {
String text = new String(buffer, 0, read);
System.out.print(text);
}
}
// wait for the process to finish (or be interrupted)
thread.join();
if(!thread.isFinished()) {
System.out.println("PROCESS WAS INTERRUPTED");
} else {
// check the exit code (exit code = 0 usually means "executed ok")
System.out.println("PROCESS FINISHED, EXIT CODE: " + thread.getExitValue());
}
// delete your temp file
tempScript.delete();
}
}
class ExecutingThread extends Thread {
private long timeoutMillis;
private WaitingThread waitingThread;
public ExecutingThread(Process shell, long timeoutMillis) {
this.timeoutMillis = timeoutMillis;
this.waitingThread = new WaitingThread(shell);
}
#Override
public void run() {
waitingThread.start();
try {
waitingThread.join(timeoutMillis);
} catch (InterruptedException ignore) {
}
if(waitingThread.isAlive()) {
waitingThread.interrupt();
}
}
public int getExitValue() {
return waitingThread.getExitValue();
}
public boolean isFinished() {
return waitingThread.isFinished();
}
}
class WaitingThread extends Thread {
private Process shell;
private volatile int exitValue;
private volatile boolean finished = false;
public WaitingThread(Process shell) {
this.shell = shell;
}
#Override
public void run() {
try {
exitValue = shell.waitFor();
finished = true;
} catch (InterruptedException e) {
shell.destroy();
}
}
public int getExitValue() {
return exitValue;
}
public boolean isFinished() {
return finished;
}
}
Try something like:
long startTime = System.currentTimeMillis();
long endTime = start + 60*1000;
while (System.currentTimeMillis() < endTime)
{
// your code
}
For what i see you are currently waiting infinitely long for a result, because
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
will block due to readline() being a blocking operation.
The naive approach would be to externalize the reading from the streams into an own thread, which you could interrupt at any time.
So basically you would have a thread object which in its constructor would get the appropriate streams and a common lockObject, and then in the run() method starts to try reading from the stream, and do whatever you wanna do with the data.
In your main thread, you could then, after starting the script and getting the wanted streams, construct such a thread instance and start it in its own thread. Then you would wait in your main thread for the second "reader" thread to finish (keyword here is wait(timeout), where timeout is the max (allowed) run time of your script.
So now your main thread is waiting for your reader thread to finish it's task, but at most for timeout milliseconds (or was it seconds?)
After that wait(timeout) you could then either kill the script (if it hangs, because your set timeout expired) or do whatever you want if it exited nicely.
I know there is no actual code included, but the approach should be valid.
Good luck

waitFor() method returning null although the process is finish

Here is my code:
private static model connectRemoteSession(String accountName,String password) throws IOException{
StringBuilder output = new StringBuilder();
StringBuilder error = new StringBuilder();
Runtime runtime = Runtime.getRuntime();
String com = // some command
proc = runtime.exec(com);
Worker worker = new Worker(proc);
reader = new BufferedReader(new InputStreamReader(proc.getInputStream()));
stderr = new BufferedReader(new InputStreamReader(
proc.getErrorStream()));
String outLine;
String errLine;
worker.start();
try {
worker.join(300000);
if (worker.exit != null){
//read the outout and error stream and take actions accordingly
}
else{
proc.destroy();
while ((outLine = reader.readLine()) != null)
{
CloudBackup.logger.info("online exchange output line ="+outLine);
output.append(outLine);
}
while((errLine = stderr.readLine()) != null){
CloudBackup.logger.info("online exchange error line ="+errLine);
error.append(errLine);
}
throw new TimeoutException();
}
} catch(InterruptedException ex) {
worker.interrupt();
Thread.currentThread().interrupt();
throw ex;
} finally {
proc.destroy();
}
}catch(Exception e){
CloudBackup.logger.severe(e.getMessage());
}finally{
reader.close();
proc.getOutputStream().close();
stderr.close();
}
return model;
}
class Worker extends Thread {
private final Process process;
Integer exit;
Worker(Process process) {
this.process = process;
}
public void run() {
try {
exit = process.waitFor();
} catch (InterruptedException ignore) {
return;
}
}
This issue I am facing is one one machine the code is working fine but on another machine the worker.exit is always null, although I put the logs in the worker.exit == null and saw that the process is getting over but somehow process.waitFor is not capturing it.
I know that process.waitFor() is available in Java 8 so I checked the version on both the machined and they have same version Java 8. Also there is no other thread running.
Try this, it can be a caching issue.
volatile Integer exit;

Make parent thread wait till child thread completes or timeout

I am running a Java process on unix.
I need to run an external process which is spawned by the main process using ProcessBuilder. The main process waits until the external process is done, and then spawnes the next external process. I have got it working till here.
public static void main(String[] args) {
for(...) {
int exitVal = runExternalProcess(args);
if(exitVal !=0) {
failedProcess.add(args);
}
}
}
private int runExternalProcess(String[] args) {
ProcessBuilder pb = new ProcessBuilder(args[0], args[1], args[2]);
pb.redirectErrorStream(true);
Process proc = pb.start();
BufferedReader br = new BufferedReader(new InputStreamReader(
proc.getInputStream()));
String line = null;
while ( (line = br.readLine()) != null)
LOG.log(Level.SEVERE, line);
//Main thread waits for external process to complete.
//What I need to do is.
// If proc.executionTime() > TIMEOUT
// kill proc;
int exitVal = proc.waitFor();
proc.getInputStream().close();
proc.getOutputStream().close();
proc.getErrorStream().close();
return exitVal;
}
What I am not able to figure out is, how to do this. For some inputs the external process hangs and in such cases, I want to wait for a set timeout period and if the external process is not complete by then, just kill it and return control to the main process (along with exit value so that I can track the failed processes), so that the next external process can be launched.
I tried using proc.wait(TIMEOUT) and then using proc.exitValue(); to get the exit value, but could not get it working.
Thanks!
YOu could do Thread.join(long) or Thread.join(long, int) and start process in separate thread.
Adding some code. (Works but Not fully tested with all corner cases)
public class Test {
public static void main(String[] args) throws Throwable {
for(int i = 0; i < 3; i++) {
ProcessRunner pr = new ProcessRunner(args);
pr.start();
// wait for 100 ms
pr.join(100);
// process still going on? kill it!
if(!pr.isDone()) {
System.err.println("Destroying process " + pr);
pr.abort();
}
}
}
static class ProcessRunner extends Thread {
ProcessBuilder b;
Process p;
boolean done = false;
ProcessRunner(String[] args) {
super("ProcessRunner " + args); // got lazy here :D
b = new ProcessBuilder(args);
}
public void run() {
try {
p = b.start();
// Do your buffered reader and readline stuff here
// wait for the process to complete
p.waitFor();
}catch(Exception e) {
System.err.println(e.getMessage());
}finally {
// some cleanup code
done = true;
}
}
int exitValue() throws IllegalStateException {
if(p != null) {
return p.exitValue();
}
throw new IllegalStateException("Process not started yet");
}
boolean isDone() {
return done;
}
void abort() {
if(! isDone()) {
// do some cleanup first
p.destroy();
}
}
}
}

Categories

Resources