Java runtime.exec() running multiple process using Threadpool - java

In my program I have a list of n test scripts , I need to iterate the list and run 3 test scripts in parallel. To achieve this task I created a Threadpool of size 3. My implementation is like below for the thread pool
ExecutorService executor = Executors.newFixedThreadPool(3);
for (int threadpoolCount = 0; threadpoolCount < classNames.size(); threadpoolCount++) {
Runnable worker = new ProcessRunnable(classNames.get(threadpoolCount));
executor.execute(worker);
}
executor.shutdown();
while (!executor.isTerminated()) {
}
System.out.println("Finished all threads");
below is my thread implementation where in i execute a batch file with maven commands in it
public void run() {
try {
System.out.println(testname);
System.out.println("Task ID : " + this.testname + " performed by " + Thread.currentThread().getName());
Process p = Runtime.getRuntime().exec("cmd /C Junit_runner.bat" + " " + testname);
p.waitFor();
Thread.sleep(5000);
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
Here is what I get in my console (I am not starting the command prompt and running it in background)
com.selenium.test.testname1
Task ID : com.selenium.test.testname1
performed by pool-1-thread-1
com.selenium.test.testname1
Task ID : com.selenium.test.testname2
performed by pool-1-thread-2
com.selenium.test.testname1
Task ID : com.selenium.test.testname3
performed by pool-1-thread-3
The execution pauses here and it didn't do anything , I am not sure what's happening behind. I also cross checked that the batch file its working fine.

The process takes long to execute and so your control is not returning back.
public abstract int waitFor() throws InterruptedException
Causes the current thread to wait, if necessary, until the process represented by this Process object has terminated. This method returns immediately if the subprocess has already terminated. If the subprocess has not yet terminated, the calling thread will be blocked until the subprocess exits.
As waitFor() is a blocking call all the 3 threads are stuck at this line.
NOTE: You don't need Thread.sleep(5000); as waitFor() is itself blocking in nature.
Try executing some other command and see if the control returns.
Also instead of:
while (!executor.isTerminated()) {
}
You can use ExecutorService#awaitTermination()

Read the p.getInputStream() and p.getErrorStream() and write it to your console instead of thread.sleep(), you will get an indication of what the threads are doing.

Related

Surefire is going to kill self fork JVM. The exit has elapsed 30 seconds after System.exit(0)

I'm using maven surefire plugin (LTS Version) to execute tests on two testing frameworks (e.g, jUnit, jBehave).
Have tried to implement parallelisation by spawning couple of Threads which in turn create processes to execute surefire jar, taking it from -
ManagementFactory.getRuntimeMXBean().getSystemProperties().get("sun.java.command")
Code snippet to show process creation -
CustomRunner.java
void run() {
ProcessBuilder processBuilder = new ProcessBuilder(commandArray);
Map<String, String> environment = processBuilder.environment();
environment.put("platformIndex", String.valueOf(platformIndex));
try {
processBuilder.inheritIO();
Process p = processBuilder.start();
LOGGER.info("Is Alive {} {}", p.isAlive(), LocalTime.now());
int statusCode = p.waitFor();
} catch (Exception e) {
e.printStackTrace();
}
}
EntryPoint.java
for (int i = 0; i < 3; i++) {
Thread thread = new Thread(new CustomRunner(commandArray, String.valueOf(i)));
thread.start();
threadList.add(thread);
}
threadList.forEach(thread -> {
try {
thread.join();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
});
System.exit(exitcode);
After running two or sometimes three processes in corresponding Threads, the process execution got stuck on p.waitFor();
Then the process exits after 30 secs and with error message "Surefire is going to kill self fork JVM. The exit has elapsed 30 seconds after System.exit(0)." resulting in Build Failure (sometimes it doesn't) though the tests have passed in their respective processes.
Seems like surefire execution is stuck in some processes. Could you please let me know what can be the possible reasons for it and how to mitigate this? Tried extending the ForkedProcessTimeoutInSeconds to few minutes but no luck.
Any help is much appreciated.

process.waitFor(timeout, timeUnit) does not quit the process after specified time

I'm trying to execute a visual basic script code in my java application using process builder. As script provided by the user might not finish its execution in time, I want to provide means to limit this execution time. In the following code, you can see my logic but it doesn't really do what it supposed to do. How can I make this waitfor work in order to limit the execution time?
private void run(String scriptFilePath) throws ScriptPluginException {
BufferedReader input = null;
BufferedReader error = null;
try {
ProcessBuilder p = new ProcessBuilder("cscript.exe", "//U", "\"" + scriptFilePath + "\"");
String path = "";
if (scriptFilePath.indexOf("/") != -1) {
path = scriptFilePath.substring(0, scriptFilePath.lastIndexOf("/"));
}
path += "/" + "tempvbsoutput.txt";
p.redirectOutput(new File(path));
Process pp = p.start();
try {
pp.waitFor(executionTimeout, TimeUnit.MINUTES);
} catch (InterruptedException e) {
SystemLog.writeError(jobId, ScriptConsts.COMPONENT_ID, "VBScriptExecutor", "run", 80401104,
"VB Script executes fail.");
}
if (!pp.isAlive()) {
pp.getOutputStream().close();
}
// rest of the code flow
}
Process.waitFor(long, TimeUnit) waits until the process has terminated or the specified time elapsed (Javadoc). The return value indicates whether the process exited or not.
if (process.waitFor(1, TimeUnit.MINUTES)) {
System.out.println("process exited");
} else {
System.out.println("process is still running");
}
waitFor() does not kill the process after the time elapsed.
If you want to kill the subprocess, use either destroy() or destroyForcibly().

Process.waitFor hangs even though stdout and stderr get read

I already searched the web, and the question process.waitFor() never returns indicates that it is often a problem with processes that their stdout or stderr do not get read.
We use ProcessBuilder with redirectOutput and redirectError to achieve this, and I think we should be on the safe side, see the following method we use to execute processes:
public static void execute(String directory, long timeout, File out, File err, String... command) throws InterruptedException, IOException {
LOGGER.log(Level.INFO, String.format("executing command %s (%s)", Arrays.toString(command), timeout > 0 ? String.format("timeout = %,d[ms]", timeout) : "no timeout"));
ProcessBuilder builder = new ProcessBuilder();
builder.directory(new File(directory));
builder.command(command);
builder.redirectOutput(out);
if(out == err) {
builder.redirectErrorStream(true);
} else {
builder.redirectError(err);
}
long time = System.currentTimeMillis();
Process process = builder.start();
try {
LOGGER.log(Level.FINE, "waiting for process");
boolean exited = process.waitFor(timeout, TimeUnit.MILLISECONDS);
if(!exited) {
LOGGER.log(Level.WARNING, "timeout reached, trying to destroy ...");
exited = destroy(silent, process); // Helper method to destroy processes
}
long duration = System.currentTimeMillis() - time;
int exitValue = process.exitValue();
LOGGER.log(Level.INFO, "execution finished in " + duration + "[ms] => " + exitValue);
} catch (InterruptedException | Error | RuntimeException e) {
LOGGER.log(Level.SEVERE, "execution failed", e);
throw e;
}
}
Yet, the problem is that it hangs on the process.waitFor(timeout, TimeUnit.MILLISECONDS) call, even though the process should easily have finished within the timeout.
The logging output is
Oct 07, 2017 12:39:55 AM at.ProcessExecutor execute
INFO: executing command [java, -Xmx8G, -XX:+UseG1GC, -XX:+CrashOnOutOfMemoryError, -jar, MetricCalc.jar] (timeout = 14,400,000[ms])
Oct 07, 2017 12:39:55 AM at.ProcessExecutor execute
FINE: waiting for process
(recognize that no execution finished line is written yet)
The err file reads
... Things we write to std.err ...
Finished Metrics
and the main method of MetricCalc looks like
public static void main(String[] args) {
.... do some stuff ...
System.err.println("Finished Metrics");
}
which indicates that the reading works fine, the last line of the Java program has been executed and the process should have terminated.
Anyone having an idea why the process does not terminate / it still hangs on Process.waitFor()?
It was not a problem with Process.waitFor() but a problem with process termination.
The java application that got started used an ExecutorService that did not got shotdown correctly and left zombie threads alive which prevented process termination.
Adding executorService.shutdown() solved the problem, and the application now terminates as expected.

Java subprocess terminated by itself

My application uses some daemon subprocesses for subtasks. The subprocesses are launched using ProcessBuilder and working fine on their own, but then starting them as subprocesses every associated Process.isAlive() method return FALSE. As following, no access to process is possible.
Further investigation shows the subprocesses are not started at all (don't exist in Task Manager) with no error generated at all.
Daemons typically start a separate process and exit almost immediately, which makes checks like isAlive() useless.
Often the program will have a command line switch that make the program stay in the foreground, not becoming a daemon - use that if possible. Otherwise you'll need some other way of monitoring the daemon execution, for example using the daemon's PID file.
Is the command really running? Often there are weird little issues when trying to run a program from inside Java.
For example, the PATH environment variable may not be set correctly so it fails to load a dependency.
Use this method to see if there is any console output and what the exit code is. This uses the old Runtime class instead of ProcessBuilder. It can probably be adapted to use ProcessBuilder.
public static void runExe(String[] command) throws IOException {
Runtime runtime = Runtime.getRuntime();
long start = System.currentTimeMillis();
Process proc = runtime.exec(command);
BufferedReader br = new BufferedReader(new InputStreamReader(proc.getInputStream()));
try {
while (true) {
// enter a loop where we read what the program has to say and wait for it to finish
// read all the program has to say
while (br.ready()) {
String line = br.readLine();
System.out.println("CMD: " + line);
}
try {
int exitCode = proc.exitValue();
System.out.println("exit code: " + exitCode);
// if we get here then the process finished executing
break;
} catch (IllegalThreadStateException ex) {
// ignore
}
// wait 200ms and try again
Thread.sleep(200);
}
} catch (InterruptedException ex) {
ex.printStackTrace();
}
long end = System.currentTimeMillis();
System.out.println("Command took: " + (end - start) + "ms");
}

Start, stop and listen to a process in Java

I want to start a process with Runtime.exec(). Then I want to know (listen) when the process exits. Also, I want to manually stop the process if some condition met.
The code I have now is:
public class ProcessManager extends Thread{
private static ProcessManager manager;
Process process;
private ProcessManager(){
}
public static ProcessManager getInstance(){
if(manager==null){
manager=new ProcessManager();
}
return manager;
}
public void run(){
Runtime runtime = Runtime.getRuntime();
process = runtime.exec("c:\\windows\\system32\\notepad.exe");
//cleanup();
}
public void cleanup(){
//I want to do stuffs until the program really ends;
}
//called to manually stop the process
public void stopProcess(){
if(process!=null){
//TODO: stop the process;
}
}
}
As shown in the code, my program is like notepad.exe, which pop up a window and immediately returns. How can I listen to the status of the program, and wait until it is closed, as well as close it explicitly?
How can I listen to the status of the program, and wait until it is
closed, as well as close it explicitly?
You can use Process#waitFor()
// cause this process to stop until process p is terminated
p.waitFor();
As JavaDoc says
The java.lang.Process.waitFor() method causes the current thread to
wait, if necessary, until the process represented by this Process
object has terminated. This method returns immediately if the
subprocess has already terminated. If the subprocess has not yet
terminated, the calling thread will be blocked until the subprocess
exits.
I would not recommend using Runtime.exec() directly. It is not as straightforward as it might seem. Nice article "When Runtime.exec() won't" describes these pitfalls in details. For example simple code with waitFor():
Runtime rt = Runtime.getRuntime();
Process proc = rt.exec("javac");
int exitVal = proc.waitFor();
System.out.println("Process exitValue: " + exitVal);
will produce no output and hangs, because in general you need to provide handling of input, output and error streams in separate threads.
Instead you may use Apache Commons Exec library that will handle it for you:
String line = "notepad.exe";
CommandLine cmdLine = CommandLine.parse(line);
DefaultExecutor executor = new DefaultExecutor();
int exitValue = executor.execute(cmdLine);
System.out.println("Process exitValue: " + exitValue);
Below is more complex example of asynchronous process terminated manually:
String line = "notepad.exe";
CommandLine cmdLine = CommandLine.parse(line);
ExecuteWatchdog watchdog = new ExecuteWatchdog(timeout);
Executor exec = new DefaultExecutor();
DefaultExecuteResultHandler handler = new DefaultExecuteResultHandler();
exec.execute(cmdLine, handler);
// wait for script to run
Thread.sleep(2000);
// terminate it manually if needed
if (someCondition) {
watchdog.destroyProcess();
}
// wait until the result of the process execution is propagated
handler.waitFor(WAITFOR_TIMEOUT);
System.out.println("Process exitValue: " + handler.getExitValue());

Categories