I'm having trouble starting an application from my JavaFX GUI. I'm using ProcessBuilder. It creates the process, but the application won't launch until I close my Java program. Is it because that specific program is waiting for arguments or something wrong with my code?
#FXML
private void runWorldpac() {
try {
ProcessBuilder process = new ProcessBuilder("C:\\speedDIAL\\speedDIAL.exe");
Process p = process.start();
} catch (IOException e) {
e.printStackTrace();
}
}
External application starts but won't allow any interaction with the original application until i close this external program. Tried running a new thread, same result.
Here's the new code:
try {
ProcessBuilder process = new ProcessBuilder("C:\\speedDIAL\\speedDIAL.exe");
Map<String, String> environ = process.environment();
Process p = process.start();
InputStream is = p.getInputStream();
InputStreamReader isr = new InputStreamReader(is);
BufferedReader br = new BufferedReader(isr);
String line;
while ((line = br.readLine()) != null) {
//System.out.println(line);
}
System.out.println("Program terminated!");
} catch (IOException e) {
e.printStackTrace();
}
Read that article, good info. Also read another good example on here. It's running in a new thread now, but my program is waiting for the external application to finish before it continues, I understand that's usually desired, but not in this case, how can i disable that?
Wait for the production of the exit value in a new thread. Something like:
try {
ProcessBuilder pBuilder = new ProcessBuilder("C:\\speedDIAL\\speedDIAL.exe");
// don't forget to handle the error stream, and so
// either combine error stream with input stream, as shown here
// or gobble it separately
pBuilder.redirectErrorStream(true);
final Process process = pBuilder.start();
final InputStream is = process.getInputStream();
// in case you need to send information back to the process
// get its output stream. Don't forget to close when through with it
final OutputStream os = process.getOutputStream();
// thread to handle or gobble text sent from input stream
new Thread(() -> {
// try with resources
try (BufferedReader reader = new BufferedReader(new InputStreamReader(is));) {
String line = null;
while ((line = reader.readLine()) != null) {
// TODO: handle line
}
} catch (IOException ex) {
ex.printStackTrace();
}
}).start();
// thread to get exit value from process without blocking
Thread waitForThread = new Thread(() -> {
try {
int exitValue = process.waitFor();
// TODO: handle exit value here
} catch (InterruptedException e) {
e.printStackTrace();
}
});
waitForThread.start();
// if you want to join after a certain time:
long timeOut = 4000;
waitForThread.join(timeOut);
} catch (IOException | InterruptedException e) {
e.printStackTrace();
}
With the code below, I'm trying to simulate the command shell, I even created a command and called it (Showerrlog) to help the user seeing his invalid commands that he entered during his current work session, as you can see, I did that using filehandler, which will save the wrong commands in a log file. But as you know filehandler will start a new file for each new working session, and the new file will be named as (file.log, file.log.1, file.log.2, etc) and so on, the question is: how to make the program to avoid opening a new file everytime, in other words isn't there any other way that the program will just format the previous work session and add the new one instead?
Or at least how to make the program open the last log file which belongs to the current work session ?
public class WithEyul implements Runnable {
String command;
public WithEyul(String command) {
this.command = command;
}
#Override
public void run() {
List<String> input = new ArrayList<String>();
StringTokenizer tokenizer = new StringTokenizer(command);
while (tokenizer.hasMoreTokens()) {
input.add(tokenizer.nextToken());
}
ProcessBuilder pb = new ProcessBuilder(input);
// ProcessBuilder creates a process corresponding to the input command
// now start the process
BufferedReader br = null;
try {
Process proc = pb.start();
// obtain the input and output streams
InputStream is = proc.getInputStream();
InputStreamReader isr = new InputStreamReader(is);
br = new BufferedReader(isr);
// read what the process returned
String line;
while ((line = br.readLine()) != null) {
System.out.println(line);
}
br.close();
} catch (java.io.IOException ioe) {
try {
System.err.println("Error");
Logger logger = Logger.getLogger("Testing");
FileHandler fh = new FileHandler("E:/MyLogFile.log");
logger.addHandler(fh);
SimpleFormatter formatter = new SimpleFormatter();
fh.setFormatter(formatter);
logger.info(command);
} catch (SecurityException e) {
printStackTrace();
} catch (IOException ex) {
Logger.getLogger(WithEyul.class.getName()).log(Level.SEVERE, null, ex);
}
} finally {
if (br != null) {
try {
br.close();
} catch (IOException ex) {
Logger.getLogger(WithEyul.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
}
}
and here is the main method class
public class TestProcessBuilder {
static void createProcess(String command) throws java.io.IOException {
Thread t = new Thread(new WithEyul(command));
t.start();
}
public static void main(String[] args) throws java.io.IOException {
String commandLine;
File wd;
BufferedReader console = new BufferedReader(new InputStreamReader(System.in));
System.out.println("\n\n***** Welcome to the Java Command Shell *****");
System.out.println("If you want to exit the shell, type END and press RETURN.\n");
// we break out with ‘END’
while (true) {
// show the Java shell prompt and read what command they entered
System.out.print("jsh>");
commandLine = console.readLine();
// if user entered a return, just loop again
if (commandLine.equals("")) {
continue;
}
if (commandLine.equalsIgnoreCase("Showerrlog")) {
try {
// Runtime.getRuntime().exec("E:\\MyLogFile.log");
if (Desktop.isDesktopSupported()) {
Desktop.getDesktop().open(new File("E:\\MyLogFile.log"));
}
} catch (IOException ex) {
Logger.getLogger(WithEyul.class.getName()).log(Level.SEVERE, null, ex);
}
}
if (commandLine.toLowerCase().equals("end")) { //User wants to end shell
System.out.println("\n***** Command Shell Terminated. See you next time. BYE for now. *****\n");
System.exit(0);
}
createProcess(commandLine);
}
}
}
You could use the FileHandler constructor that allows you to specify the rotation, limit, and append options.
new FileHandler("E:/MyLogFile.log", 0, 1, true);
The FileHandler can rotate for a number of reasons that are out of your control. If you don't want to deal with file rotation you could open a FileOutputStream and wrap that with a StreamHandler. However, you will have to handle file locking conflicts.
You should also avoid creating and adding a handler that points to the same target file everytime an error is generated. You should install the handler on startup and store a string reference to your logger.
I've been trying to google this around for quite a while now, without any success. I'm hoping to get my issue solved here.
First function:
public void startTFServer(Channel c) {
try {
ProcessBuilder procBuilder = new ProcessBuilder("tfs.exe");
procBuilder.redirectErrorStream();
Process proc = null;
System.out.println(Runtime.getRuntime().freeMemory());
proc = procBuilder.start();
System.out.println(Runtime.getRuntime().freeMemory());
StreamGobbler gobbler = new StreamGobbler(proc.getInputStream(), "STD_OUT");
gobbler.start();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
The thread that captures the process output:
private class StreamGobbler extends Thread {
InputStream is;
String type;
private StreamGobbler(InputStream is, String type) {
this.is = is;
this.type = type;
}
#Override
public void run() {
try {
InputStreamReader isr = new InputStreamReader(is);
BufferedReader br = new BufferedReader(isr);
String line = null;
while ((line = br.readLine()) != null) {
System.out.println(type + "> " + line);
}
}
catch (IOException ioe) {
ioe.printStackTrace();
}
}
}
The problem:
When running the application the input interrupts at the same place every time. The application I'm running with the "Process class" is a server that required quite some memory to run, can this be one of the reasons for why the process won't finish loading my app? I was thinking that the memory would run out, but I can't really diagnostise it.
Any help would be greatly appreciated!
There is one issues that I notice:
procBuilder.redirectErrorStream();
This is not what you want. This is a getter method which tells you what the value of the redirectErrorStream property is. It could possibly be that you have errors and you are blocked because they are not read. Therefore, you need to use the setter method: see the API redirectErrorStream(boolean)
procBuilder.redirectErrorStream(true);
I'm guessing so sorry if I'm wrong, but I think you are exhausting the output from the external process, not waiting until it finishes in order to keep reading. I mean, basically:
while ((line = br.readLine()) != null) {
System.out.println(type + "> " + line);
}
If the process stops writing to the output for a second, your logger will stop logging. If it starts writing again, you will be out of the loop.
I have a method I am using to execute a command on the local host. I'd like to add a timeout parameter to the method so that if the command being called doesn't finish in a reasonable amount of time the method will return with an error code. Here's what it looks like so far, without the ability to timeout:
public static int executeCommandLine(final String commandLine,
final boolean printOutput,
final boolean printError)
throws IOException, InterruptedException
{
Runtime runtime = Runtime.getRuntime();
Process process = runtime.exec(commandLine);
if (printOutput)
{
BufferedReader outputReader = new BufferedReader(new InputStreamReader(process.getInputStream()));
System.out.println("Output: " + outputReader.readLine());
}
if (printError)
{
BufferedReader errorReader = new BufferedReader(new InputStreamReader(process.getErrorStream()));
System.out.println("Error: " + errorReader.readLine());
}
return process.waitFor();
}
Can anyone suggest a good way for me to implement a timeout parameter?
If you're using Java 8 or later you could simply use the new waitFor with timeout:
Process p = ...
if(!p.waitFor(1, TimeUnit.MINUTES)) {
//timeout - kill the process.
p.destroy(); // consider using destroyForcibly instead
}
public static int executeCommandLine(final String commandLine,
final boolean printOutput,
final boolean printError,
final long timeout)
throws IOException, InterruptedException, TimeoutException {
Runtime runtime = Runtime.getRuntime();
Process process = runtime.exec(commandLine);
/* Set up process I/O. */
...
Worker worker = new Worker(process);
worker.start();
try {
worker.join(timeout);
if (worker.exit != null)
return worker.exit;
else
throw new TimeoutException();
} catch(InterruptedException ex) {
worker.interrupt();
Thread.currentThread().interrupt();
throw ex;
} finally {
process.destroyForcibly();
}
}
private static class Worker extends Thread {
private final Process process;
private Integer exit;
private Worker(Process process) {
this.process = process;
}
public void run() {
try {
exit = process.waitFor();
} catch (InterruptedException ignore) {
return;
}
}
}
Following the answer by erickson, I created a more generic way to do the same thing.
public class ProcessWithTimeout extends Thread
{
private Process m_process;
private int m_exitCode = Integer.MIN_VALUE;
public ProcessWithTimeout(Process p_process)
{
m_process = p_process;
}
public int waitForProcess(int p_timeoutMilliseconds)
{
this.start();
try
{
this.join(p_timeoutMilliseconds);
}
catch (InterruptedException e)
{
this.interrupt();
}
return m_exitCode;
}
#Override
public void run()
{
try
{
m_exitCode = m_process.waitFor();
}
catch (InterruptedException ignore)
{
// Do nothing
}
catch (Exception ex)
{
// Unexpected exception
}
}
}
Now, all you have to do is as follows:
Process process = Runtime.getRuntime().exec("<your command goes here>");
ProcessWithTimeout processWithTimeout = new ProcessWithTimeout(process);
int exitCode = processWithTimeout.waitForProcess(5000);
if (exitCode == Integer.MIN_VALUE)
{
// Timeout
}
else
{
// No timeout !
}
I implemented this using the three approaches suggested which came with detailed code example (I am a novice with thread programming and these example codes were invaluable -- I would still be scratching my head as to how to do this if it was just explained in English without code).
I implemented the utility class I'm using for this with the three methods for executing a command with a timeout like so:
package com.abc.network.lifecycle.util;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* Utility class for performing process related functions such as command line processing.
*/
public class ProcessUtility
{
static Log log = LogFactory.getLog(ProcessUtility.class);
/**
* Thread class to be used as a worker
*/
private static class Worker
extends Thread
{
private final Process process;
private Integer exitValue;
Worker(final Process process)
{
this.process = process;
}
public Integer getExitValue()
{
return exitValue;
}
#Override
public void run()
{
try
{
exitValue = process.waitFor();
}
catch (InterruptedException ignore)
{
return;
}
}
}
/**
* Executes a command.
*
* #param command
* #param printOutput
* #param printError
* #param timeOut
* #return
* #throws java.io.IOException
* #throws java.lang.InterruptedException
*/
public static int executeCommandWithExecutors(final String command,
final boolean printOutput,
final boolean printError,
final long timeOut)
{
// validate the system and command line and get a system-appropriate command line
String massagedCommand = validateSystemAndMassageCommand(command);
try
{
// create the process which will run the command
Runtime runtime = Runtime.getRuntime();
final Process process = runtime.exec(massagedCommand);
// consume and display the error and output streams
StreamGobbler outputGobbler = new StreamGobbler(process.getInputStream(), "OUTPUT", printOutput);
StreamGobbler errorGobbler = new StreamGobbler(process.getErrorStream(), "ERROR", printError);
outputGobbler.start();
errorGobbler.start();
// create a Callable for the command's Process which can be called by an Executor
Callable<Integer> call = new Callable<Integer>()
{
public Integer call()
throws Exception
{
process.waitFor();
return process.exitValue();
}
};
// submit the command's call and get the result from a
Future<Integer> futureResultOfCall = Executors.newSingleThreadExecutor().submit(call);
try
{
int exitValue = futureResultOfCall.get(timeOut, TimeUnit.MILLISECONDS);
return exitValue;
}
catch (TimeoutException ex)
{
String errorMessage = "The command [" + command + "] timed out.";
log.error(errorMessage, ex);
throw new RuntimeException(errorMessage, ex);
}
catch (ExecutionException ex)
{
String errorMessage = "The command [" + command + "] did not complete due to an execution error.";
log.error(errorMessage, ex);
throw new RuntimeException(errorMessage, ex);
}
}
catch (InterruptedException ex)
{
String errorMessage = "The command [" + command + "] did not complete due to an unexpected interruption.";
log.error(errorMessage, ex);
throw new RuntimeException(errorMessage, ex);
}
catch (IOException ex)
{
String errorMessage = "The command [" + command + "] did not complete due to an IO error.";
log.error(errorMessage, ex);
throw new RuntimeException(errorMessage, ex);
}
}
/**
* Executes a command.
*
* #param command
* #param printOutput
* #param printError
* #param timeOut
* #return
* #throws java.io.IOException
* #throws java.lang.InterruptedException
*/
public static int executeCommandWithSleep(final String command,
final boolean printOutput,
final boolean printError,
final long timeOut)
{
// validate the system and command line and get a system-appropriate command line
String massagedCommand = validateSystemAndMassageCommand(command);
try
{
// create the process which will run the command
Runtime runtime = Runtime.getRuntime();
Process process = runtime.exec(massagedCommand);
// consume and display the error and output streams
StreamGobbler outputGobbler = new StreamGobbler(process.getInputStream(), "OUTPUT", printOutput);
StreamGobbler errorGobbler = new StreamGobbler(process.getErrorStream(), "ERROR", printError);
outputGobbler.start();
errorGobbler.start();
// run a thread which will set a flag once it has slept for the timeout period
final boolean[] flags = { true };
new Thread()
{
#Override
public void run()
{
try
{
Thread.sleep(timeOut);
}
catch (InterruptedException ex)
{
String errorMessage = "Timeout loop thread unexpectedly interrupted.";
log.error(errorMessage, ex);
throw new RuntimeException(errorMessage, ex);
}
flags[0] = false;
}
}.start();
// execute the command and wait
int returnValue = -1;
while (flags[0] && (returnValue < 0))
{
returnValue = process.waitFor();
}
// if the command timed out then log it
if (returnValue < 0)
{
log.warn("The command [" + command + "] did not complete before the timeout period expired (timeout: " +
timeOut + " ms)");
}
return returnValue;
}
catch (InterruptedException ex)
{
String errorMessage = "The command [" + command + "] did not complete due to an unexpected interruption.";
log.error(errorMessage, ex);
throw new RuntimeException(errorMessage, ex);
}
catch (IOException ex)
{
String errorMessage = "The command [" + command + "] did not complete due to an IO error.";
log.error(errorMessage, ex);
throw new RuntimeException(errorMessage, ex);
}
}
/**
* Executes a command.
*
* #param command
* #param printOutput
* #param printError
* #param timeOut
* #return
* #throws java.io.IOException
* #throws java.lang.InterruptedException
*/
public static int executeCommandWithWorker(final String command,
final boolean printOutput,
final boolean printError,
final long timeOut)
{
// validate the system and command line and get a system-appropriate command line
String massagedCommand = validateSystemAndMassageCommand(command);
try
{
// create the process which will run the command
Runtime runtime = Runtime.getRuntime();
Process process = runtime.exec(massagedCommand);
// consume and display the error and output streams
StreamGobbler outputGobbler = new StreamGobbler(process.getInputStream(), "OUTPUT", printOutput);
StreamGobbler errorGobbler = new StreamGobbler(process.getErrorStream(), "ERROR", printError);
outputGobbler.start();
errorGobbler.start();
// create and start a Worker thread which this thread will join for the timeout period
Worker worker = new Worker(process);
worker.start();
try
{
worker.join(timeOut);
Integer exitValue = worker.getExitValue();
if (exitValue != null)
{
// the worker thread completed within the timeout period
return exitValue;
}
// if we get this far then we never got an exit value from the worker thread as a result of a timeout
String errorMessage = "The command [" + command + "] timed out.";
log.error(errorMessage);
throw new RuntimeException(errorMessage);
}
catch (InterruptedException ex)
{
worker.interrupt();
Thread.currentThread().interrupt();
throw ex;
}
}
catch (InterruptedException ex)
{
String errorMessage = "The command [" + command + "] did not complete due to an unexpected interruption.";
log.error(errorMessage, ex);
throw new RuntimeException(errorMessage, ex);
}
catch (IOException ex)
{
String errorMessage = "The command [" + command + "] did not complete due to an IO error.";
log.error(errorMessage, ex);
throw new RuntimeException(errorMessage, ex);
}
}
/**
* Validates that the system is running a supported OS and returns a system-appropriate command line.
*
* #param originalCommand
* #return
*/
private static String validateSystemAndMassageCommand(final String originalCommand)
{
// make sure that we have a command
if (originalCommand.isEmpty() || (originalCommand.length() < 1))
{
String errorMessage = "Missing or empty command line parameter.";
log.error(errorMessage);
throw new RuntimeException(errorMessage);
}
// make sure that we are running on a supported system, and if so set the command line appropriately
String massagedCommand;
String osName = System.getProperty("os.name");
if (osName.equals("Windows XP"))
{
massagedCommand = "cmd.exe /C " + originalCommand;
}
else if (osName.equals("Solaris") || osName.equals("SunOS") || osName.equals("Linux"))
{
massagedCommand = originalCommand;
}
else
{
String errorMessage = "Unable to run on this system which is not Solaris, Linux, or Windows XP (actual OS type: \'" +
osName + "\').";
log.error(errorMessage);
throw new RuntimeException(errorMessage);
}
return massagedCommand;
}
}
I created a class to consume and display the output and error streams from a command (taken from http://www.javaworld.com/javaworld/jw-12-2000/jw-1229-traps.html?page=4):
package com.abc.network.lifecycle.util;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* Utility thread class which consumes and displays stream input.
*
* Original code taken from http://www.javaworld.com/javaworld/jw-12-2000/jw-1229-traps.html?page=4
*/
class StreamGobbler
extends Thread
{
static private Log log = LogFactory.getLog(StreamGobbler.class);
private InputStream inputStream;
private String streamType;
private boolean displayStreamOutput;
/**
* Constructor.
*
* #param inputStream the InputStream to be consumed
* #param streamType the stream type (should be OUTPUT or ERROR)
* #param displayStreamOutput whether or not to display the output of the stream being consumed
*/
StreamGobbler(final InputStream inputStream,
final String streamType,
final boolean displayStreamOutput)
{
this.inputStream = inputStream;
this.streamType = streamType;
this.displayStreamOutput = displayStreamOutput;
}
/**
* Consumes the output from the input stream and displays the lines consumed if configured to do so.
*/
#Override
public void run()
{
try
{
InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
String line = null;
while ((line = bufferedReader.readLine()) != null)
{
if (displayStreamOutput)
{
System.out.println(streamType + ">" + line);
}
}
}
catch (IOException ex)
{
log.error("Failed to successfully consume and display the input stream of type " + streamType + ".", ex);
ex.printStackTrace();
}
}
}
I created a test command which takes roughly 10 seconds to complete:
#!/bin/bash
sleep 10
echo 'TEST COMMAND RAN OK'
Then I created a test program to test the three different methods, calling each with a timeout value of 5 seconds (command should fail) and with a timeout value of 15 seconds (command should succeed):
package com.abc.network.lifecycle.util;
public class ProcessUtilityTester
{
/**
* #param args
*/
public static void main(final String[] args)
{
try
{
String command = args[0];
int exitValue = -1;
System.out.println("\n\n5000ms timeout With Executors:");
try
{
exitValue = -1;
exitValue = ProcessUtility.executeCommandWithExecutors(command, true, true, 5000);
}
catch (Exception ex)
{
ex.printStackTrace();
}
finally
{
System.out.println("\nExit value:" + exitValue);
}
System.out.println("\n\n5000ms timeout With Sleep:");
try
{
exitValue = -1;
exitValue = ProcessUtility.executeCommandWithSleep(command, true, true, 5000);
}
catch (Exception ex)
{
ex.printStackTrace();
}
finally
{
System.out.println("\nExit value:" + exitValue);
}
System.out.println("\n\n5000ms timeout With Worker:");
try
{
exitValue = -1;
exitValue = ProcessUtility.executeCommandWithWorker(command, true, true, 5000);
}
catch (Exception ex)
{
ex.printStackTrace();
}
finally
{
System.out.println("\nExit value:" + exitValue);
}
System.out.println("\n\n15000ms timeout With Executors:");
try
{
exitValue = -1;
exitValue = ProcessUtility.executeCommandWithExecutors(command, true, true, 15000);
}
catch (Exception ex)
{
ex.printStackTrace();
}
finally
{
System.out.println("\nExit value:" + exitValue);
}
System.out.println("\n\n15000ms timeout With Sleep:");
try
{
exitValue = -1;
exitValue = ProcessUtility.executeCommandWithSleep(command, true, true, 15000);
}
catch (Exception ex)
{
ex.printStackTrace();
}
finally
{
System.out.println("\nExit value:" + exitValue);
}
System.out.println("\n\n15000ms timeout With Worker:");
try
{
exitValue = -1;
exitValue = ProcessUtility.executeCommandWithWorker(command, true, true, 15000);
}
catch (Exception ex)
{
ex.printStackTrace();
}
finally
{
System.out.println("\nExit value:" + exitValue);
}
}
catch (Exception ex)
{
ex.printStackTrace();
}
finally
{
System.exit(0);
}
}
}
Here's what I see when I run the test program:
5000ms timeout With Executors:
May 1, 2009 1:55:19 AM com.abc.network.lifecycle.util.ProcessUtility executeCommandWithExecutors
SEVERE: The command [/tmp/testcmd.sh] timed out.
java.util.concurrent.TimeoutException
at java.util.concurrent.FutureTask$Sync.innerGet(FutureTask.java:228)
at java.util.concurrent.FutureTask.get(FutureTask.java:91)
at com.abc.network.lifecycle.util.ProcessUtility.executeCommandWithExecutors(ProcessUtility.java:179)
at com.abc.network.lifecycle.util.ProcessUtilityTester.main(ProcessUtilityTester.java:19)
java.lang.RuntimeException: The command [/tmp/testcmd.sh] timed out.
at com.abc.network.lifecycle.util.ProcessUtility.executeCommandWithExecutors(ProcessUtility.java:186)
at com.abc.network.lifecycle.util.ProcessUtilityTester.main(ProcessUtilityTester.java:19)
Caused by: java.util.concurrent.TimeoutException
at java.util.concurrent.FutureTask$Sync.innerGet(FutureTask.java:228)
at java.util.concurrent.FutureTask.get(FutureTask.java:91)
at com.abc.network.lifecycle.util.ProcessUtility.executeCommandWithExecutors(ProcessUtility.java:179)
... 1 more
Exit value:-1
5000ms timeout With Sleep:
OUTPUT>TEST COMMAND RAN OK
OUTPUT>TEST COMMAND RAN OK
Exit value:0
5000ms timeout With Worker:
May 1, 2009 1:55:34 AM com.abc.network.lifecycle.util.ProcessUtility executeCommandWithWorker
SEVERE: The command [/tmp/testcmd.sh] timed out.
java.lang.RuntimeException: The command [/tmp/testcmd.sh] timed out.
at com.abc.network.lifecycle.util.ProcessUtility.executeCommandWithWorker(ProcessUtility.java:338)
at com.abc.network.lifecycle.util.ProcessUtilityTester.main(ProcessUtilityTester.java:47)
Exit value:-1
15000ms timeout With Executors:
OUTPUT>TEST COMMAND RAN OK
OUTPUT>TEST COMMAND RAN OK
Exit value:0
15000ms timeout With Sleep:
OUTPUT>TEST COMMAND RAN OK
Exit value:0
15000ms timeout With Worker:
OUTPUT>TEST COMMAND RAN OK
Exit value:0
So from what I can tell the approach using a Worker thread class works the best, in that it gives the expected results in both cases. The approach using Executors works as expected as well, with the caveat that it appears to be running the command twice in the 15000ms timout case (i.e. I see the output for the command twice). The approach using the sleep() method does not timeout the command as expected in the 5000ms timeout case, and displays the output twice, but runs the command as expected in the 15000ms timeout case.
For everybody using the executor framework: you are all forgetting to shutdown the executor. So change it to the following:
ExecutorService service = Executors.newSingleThreadExecutor();
try {
Future<Integer> ft = service.submit(call);
try {
int exitVal = ft.get(2000L, TimeUnit.MILLISECONDS);
return exitVal;
} catch (TimeoutException to) {
p.destroy();
throw to;
}
}
finally {
service.shutdown();
}
If you don't your program will keep an active non-daemon thread, ensuring your program will never exit until you call System.exit
For those who can't use the new Java 8 method waitFor(long timeout, TimeUnit unit) (because they are on Android or simply can't upgrade) you can simply rip it from the JDK source code and add it somewhere in your utils file :
public boolean waitFor(long timeout, TimeUnit unit, final Process process)
throws InterruptedException
{
long startTime = System.nanoTime();
long rem = unit.toNanos(timeout);
do {
try {
process.exitValue();
return true;
} catch(IllegalThreadStateException ex) {
if (rem > 0)
Thread.sleep(
Math.min(TimeUnit.NANOSECONDS.toMillis(rem) + 1, 100));
}
rem = unit.toNanos(timeout) - (System.nanoTime() - startTime);
} while (rem > 0);
return false;
}
The only change I've made to the original one from JDK8 source code is the addition of the Process parameter so that we can call the exitValue method from the process.
The exitValue method will directly try to return or throw an IllegalThreadStateException if the process has not yet terminated. In that case, we wait the received timeout and terminate.
The method return a boolean, so if it return false then you know you need to manually kill the process.
This way seems simplier than anything posted above (expect the direct call to waitFor for sure).
A light-weight solution for small apps:
public class Test {
public static void main(String[] args) throws java.io.IOException, InterruptedException {
Process process = new ProcessBuilder().command("sleep", "10").start();
int i=0;
boolean deadYet = false;
do {
Thread.sleep(1000);
try {
process.exitValue();
deadYet = true;
} catch (IllegalThreadStateException e) {
System.out.println("Not done yet...");
if (++i >= 5) throw new RuntimeException("timeout");
}
} while (!deadYet);
}
}
Implement as a delegate and fail the call if it takes above your threshold to complete.
Try using a Timer (or Sleep()), in a separate thread or in your event queue if you have one available.
There are various ways to do this, but I'd consider using an Executor-- it just helps you encapsulate passing the exit value or exception from the thread back to the original caller.
final Process p = ...
Callable<Integer> call = new Callable<Integer>() {
public Integer call() throws Exception {
p.waitFor();
return p.exitValue();
}
};
Future<Integer> ft = Executors.newSingleThreadExecutor().submit(call);
try {
int exitVal = ft.get(2000L, TimeUnit.MILLISECONDS);
return exitVal;
} catch (TimeoutException to) {
p.destroy();
throw to;
}
I think you can't get round the race condition whereby the wait times out, and then process terminates just before you call destroy().
I also tested the worker implementation and works like a charm. Under handling process io, I added threads to handle stde and stdo. If the worker thread times out I also exit the io threads.
Process p = Runtime.getRuntime().exec(cmd.trim());
//setup error and output stream threads
CommandStreamThread eStream = new CommandStreamThread(p.getErrorStream(), "STDE");
CommandStreamThread oStream = new CommandStreamThread(p.getInputStream(), "STDO");
// kick them off
eStream.start();
oStream.start();
//setup a worker thread so we can time it out when we need
CommandWorkerThread worker=new CommandWorkerThread(p);
worker.start();
try {
worker.join(this.getTimeout());
if (worker.getExit() != null)
return worker.getExit();
else
throw new TimeoutException("Timeout reached:"+this.getTimeout()+" ms");
} catch(InterruptedException ex) {
eStream.interrupt();
oStream.interrupt();
worker.interrupt();
Thread.currentThread().interrupt();
throw ex;
} finally {
p.destroy();
}
First some background info, I came across the issue to have a timeout while running a command because the program that I tried to execute would never print any debug or error information incase of error and would just keep on retrying internally by itself resulting in process stuck because there was never an error or output stream when it was retrying.
So after process.exec() or process.start() ,
It would be stuck forever at this line,
BufferedReader input = new BufferedReader(newInputStreamReader(process.getInputStream()));
As per java 1.8 with public boolean waitFor(long timeout,TimeUnit unit) method it should have "ideally" timed out after the specified timeout but in my case for some reason it never timed out may be because I was running application as a windows service (I have checked the user permissions and everything on the account but it didn't help).
So I tried to implement it with the below logic, where we would keep checking the input stream with input.ready() and a timeout flag.This simple solution worked like a charm compared to all other that existed.
Code:
public boolean runCommand() throws IOException, InterruptedException, Exception {
StringBuilder rawResponse = new StringBuilder();
System.out.println("Running Command " + Arrays.toString(command));
ProcessBuilder processBuilder = new ProcessBuilder(Arrays.asList(command));
processBuilder.redirectErrorStream(true);
Process process = processBuilder.start(); //Executing the process
BufferedReader input = new BufferedReader(new InputStreamReader(process.getInputStream()));
waitForTimeout(input, process); //Waiting for Timout
String line;
while ((line = input.readLine()) != null) {
rawResponse.append(line).append("\n");
}
return true;
}
//Timeout method
private void waitForTimeout(BufferedReader input, Process process) throws InterruptedException, Exception {
int timeout = 5;
while (timeout > 0) {
if (!process.isAlive() || input.ready()) {
break;
} else {
timeout--;
Thread.sleep(1000);
if (timeout == 0 && !input.ready()) {
destroyProcess(process);
throw new Exception("Timeout in executing the command "+Arrays.toString(command));
}
}
}
}
You can launch a Thread that sleeps for the time you want and after the sleep changing a boolean that you loop on in your executeCommandLine method.
Something like that (not tested nor compiled, this solution is a prototype you should refactor it if it suit you needs):
public static int executeCommandLine(final String commandLine,
final boolean printOutput,
final boolean printError)
throws IOException, InterruptedException
{
Runtime runtime = Runtime.getRuntime();
Process process = runtime.exec(commandLine);
if (printOutput)
{
BufferedReader outputReader = new BufferedReader(new InputStreamReader(process.getInputStream()));
System.out.println("Output: " + outputReader.readLine());
}
if (printError)
{
BufferedReader errorReader = new BufferedReader(new InputStreamReader(process.getErrorStream()));
System.out.println("Error: " + errorReader.readLine());
}
ret = -1;
final[] b = {true};
new Thread(){
public void run(){
Thread.sleep(2000); //to adapt
b[0] = false;
}
}.start();
while(b[0])
{
ret = process.waitFor();
}
return ret;
}
and here is the StreamThread
public class CommandStreamThread extends Thread{
private InputStream iStream;
private String cPrompt;
CommandStreamThread (InputStream is, String cPrompt)
{
this.iStream = is;
this.cPrompt = cPrompt;
}
public void run()
{
try
{
InputStreamReader streamReader= new InputStreamReader(this.iStream);
BufferedReader reader = new BufferedReader(streamReader);
String linesep=System.getProperty("line.separator");
String line=null;
while ((line=reader.readLine())!=null){
System.out.println(line);
//Process the next line seperately in case this is EOF is not preceded by EOL
int in;
char[] buffer=new char[linesep.length()];
while ( (in = reader.read(buffer)) != -1){
String bufferValue=String.valueOf(buffer, 0, in);
System.out.print(bufferValue);
if (bufferValue.equalsIgnoreCase(linesep))
break;
}
}
//Or the easy way out with commons utils!
//IOUtils.copy(this.iStream, System.out);
} catch (Exception e){
e.printStackTrace();
}
}
public InputStream getIStream() {
return iStream;
}
public void setIStream(InputStream stream) {
iStream = stream;
}
public String getCPrompt() {
return cPrompt;
}
public void setCPrompt(String prompt) {
cPrompt = prompt;
}
}
Apache Commons Exec can help you to do it.
See http://commons.apache.org/proper/commons-exec/tutorial.html
String line = "your command line";
CommandLine cmdLine = CommandLine.parse(line);
DefaultExecutor executor = new DefaultExecutor();
ExecuteWatchdog watchdog = new ExecuteWatchdog(60000);
executor.setWatchdog(watchdog);
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
PumpStreamHandler streamHandler = new PumpStreamHandler(outputStream);
executor.setStreamHandler(streamHandler);
int exitValue = executor.execute(cmdLine);
System.out.println(exitValue);
System.out.println(outputStream.toString());
If using Java 8 I'd go with Aleksander Blomskøld answer i.e. p.waitFor(1, TimeUnit.MINUTE)
else if Java 6/7 and using Swing, then you can use a SwingWorker:
final Process process = ...
SwingWorker<Integer, Integer> sw = new SwingWorker<>() {
#Override
protected Integer doInBackground() throws Exception {
process.waitFor();
return process.exitValue();
}
};
sw.execute();
int exitValue = sw.get(1, TimeUnit.SECONDS);
if (exitValue == 0) {
//everything was fine
} else {
//process exited with issues
}
I know this is really old post; i needed some help with a similar project so I thought I might give some of my code that I worked and ones that work.
long current = System.currentTimeMillis();
ProcessBuilder pb = new ProcessBuilder(arguments);
try{
pb.redirectErrorStream(true);
process = pb.start();
int c ;
while((c = process.getInputStream().read()) != -1 )
if(System.currentTimeMillis() - current < timeOutMilli)
result += (char)c;
else throw new Exception();
return result.trim();
}catch(Exception e){
e.printStackTrace();
}
return result;
Hope this helps the future :D