How to Exit a Process (.exec()) if the External Program hangs - java

Let me explain my software. What my software simply does is that it creates 10 threads and assigns a number of tasks to each thread. Each thread then creates a Runtime Process that will start a cmd batch file which in turn will start a program that will telnet to a device (I have about 200 of them) to poll its configuration. Here is the code for my process creation:
Process p1 = java.lang.Runtime.getRuntime().exec("cmd /c start /b /wait " + batchFile);
int returnVal = p1.waitFor();
batchFile is the full path of the batch file. Don't get me wrong, the software works fine up to 100% of the execution and it hanged ONLY ONE TIME at about 95%, so I'm trying to find a solution for that. Why it hanged is not my issue now, but rather how to deal with hangups later on..!
Now the problem is that I need to wait for the process to finish because my telnet client will write to a file that I will read later on in the thread; and hence the use of the .waitFor() . My question is how can I get the thread to understand that the external program hanged? In other words, can I give the external program some time limit to finish; and if it does not the thread will kill the process?
Also I have read about reading the error and output streams; however, I don't think it is applicable here, or is it?

Also I have read about reading the error and output streams; however, I don't think it is applicable here, or is it?
Almost certainly yes, it is applicable. Try using ProcessBuilder instead of Runtime.exec and read all the output before calling waitFor. For example (exception handling omitted, in particular waitFor may throw InterruptedException before the process actually exited)
ProcessBuilder pb = new ProcessBuilder(
"cmd", "/c", "start", "/b", "/wait", batchFile);
pb.redirectErrorStream(true);
Process p = pb.start();
IOUtils.closeQuietly(p.getOutputStream());
IOUtils.copy(p.getInputStream(), System.out);
// or alternatively throw away the output using
// IOUtils.copy(p.getInputStream(), NullOutputStream.NULL_OUTPUT_STREAM);
IOUtils.closeQuietly(p.getInputStream());
int returnVal = p.waitFor();
(IOUtils and NullOutputStream are from Apache commons-io).
To answer the actual question in the title, if your process still hangs even after properly reading its output, you may need to use p.destroy() to forcibly terminate the process. You could define a timer task something like
public class TimeoutProcessKiller extends TimerTask {
private Process p;
public TimeoutProcessKiller(Process p) {
this.p = p;
}
public void run() {
p.destroy();
}
}
and then do
// single shared timer instance
Timer t = new Timer();
// for each device
Process p = pb.start();
TimerTask killer = new TimeoutProcessKiller(p);
t.schedule(killer, 30000);
// ... read output stream as before ...
int returnVal = p.waitFor();
killer.cancel();
This will cause the process to be killed if it has been running for more than 30 seconds (adjust the schedule call to change the timeout).

Related

How to tell when an instance of Command Prompt is closed, in java?

I've had a bit of an issue, and I'm pretty new at Java. For context, I'm making a GUI in Javafx, with a Batch backend. The GUI only has to call the Batch script(core.bat) once, and know when it has ended after it has been called. Currently, I call the Batch script using the following code:
Runtime runtime = Runtime.getRuntime();
try {
Process p1 = runtime.exec("cmd /c start core.bat");
} catch(IOException ioException) {}
The issue comes into play when I need to know when the Batch script has ended. I would give examples of what I've tried so far, but I've tried so many things over the past three hours and overwritten my code so many times that I just don't know anymore.
Any solution will do. I just need some way to determine when the Batch script has ended, after which it will close itself. Due to this, knowing when the Batch script ends is not the only possible method.
Knowing when the instance of command prompt running the script ends is also an option.
Any solutions are helpful, thank you.
Take a look at Process#waitFor. It waits until the process has finished.
You might need to start a new Thread if you don't want to wait for it but only get notified/execute code when the program is finished(blocking seems not like a good idea in JavaFX):
Runtime runtime = Runtime.getRuntime();
try {
Process p1 = runtime.exec("core.bat");
Thread t=new Thread(()->{
try{
p1.waitFor();
//your code
}catch(InterruptedException e){
Thread.currentThread.interrupt();//not actually needed, but I think it is a good practise and...SonarLint :)
}
});
t.setDaemon(true);
t.start();
} catch(IOException ioException) {}
Also, you executed the cmd command start, that starts a new process that you cannot control that easily. Just execute core.bat or cmd /c core.bat
setDaemon(true); marks your Thread as a daemon Thread, that does not affect the end of the Program(ends if all Threads that are no daemon Threads finished).

java Runtime.exec() crashes Netbeans

I have been searching the web for quite some time now and I did find a lot about Runtime.exec(), but I didn't find a satisfying answer to my problem yet, so I decided to open a new question.
I am running asymptote (http://asymptote.sourceforge.net/) from java. After reading some articles and tinkering around a bit I found this (working) solution:
public static voidcompileAsy(File name)
{
try
{
Runtime rt = Runtime.getRuntime();
String[] cmdarray = {"/usr/texbin/asy", name.getName()};
//String[] envp = {"PATH=/usr/bin:/bin:/usr/sbin:/sbin:/usr/texbin:/usr/local/bin"};
String[] envp = null;
File fd = new File("/xxxxxx/xxxxx");
Process proc = rt.exec(cmdarray, envp, fd);
// any errors?
StreamGobbler errorGobbler = new StreamGobbler(proc.getErrorStream(), "error");
// any output?
StreamGobbler outputGobbler = new StreamGobbler(proc.getInputStream(), "output");
// kick them off
errorGobbler.start();
outputGobbler.start();
int exitVal = proc.waitFor();
System.out.println("Process exitValue: " + exitVal);
}
catch(Throwable t)
{
t.printStackTrace();
}
So far so good. It turns out that without the correct path variables set asymptote crashes, which would be no problem if I could catch this event from the java side. Unfortunately when asymptote crashes it takes down java entirely including Netbeans so I have no chance for any diagnosis. Here are my questions:
How does this happen? Isn't asymptote a process on its own and should die without touching the jvm?
How can I prevent this from happening?
The system is MacOSX 10.10.3
Happy to hear any opinion/suggestions on this!
There is one thing I can see that is wrong with your code above and that is that you read the error stream and then read the input stream.
This can cause execution to block and stream buffers fill up.
You should create a separate thread for each stream and once your waitfor call has completed join the threads. I do not know if this is contributing to the crash in some way. Perhaps you are getting an input stream buffer overflow.
Consider changing to use ProcessBuilder, which has simpler options for handling process outputs like inheritIO()

Allow Java to end with main() without closing processes that were exec()uted

I am launching a process with Runtime.getRuntime().exec()
However, once Java reaches the end of the main() loop, java will not close, unless I destroy() the process first.
Problem with that is, I need the process to keep running after Java is closed.
I pretty much want to do this
public static void main(String args[]) {
Runtime.getRuntime().exec("file.bat");
// now I want java to close, and I want file.bat to keep running
}
I tried System.exit(), it will stop my main() loop, however I think a thread or something that was started by exec() keeps running, preventing java from closing. I can't even end it in Eclipse without first exiting file.bat
Why isn't it closing? And how would I fix it?
I couldn't find anything online, and I've been experimenting for a while, so I decided to ask you guys.
Thank you,
-Alex Benoit
Figured it out. I'll share my code in case anyone else has the same question. I brought it down to 1 line.
Desktop.getDesktop().open(new File("C:\\Folder\\File"));
This is a system dependent question. I am not sure if in Windows the parent process can terminate before the child. I believe this is true in linux.
As a guideline, you should call waitFor() on the process. However, on some systems, just doing so might not be enough. As pointed out in the javadoc, the out/err stream need to be properly purged (using the streams returned by getOutputStream() and getErrorStream()) because they could keep your process from completing. To do so, I found it most appropriate to use two separate threads, one purging the err stream, the other the out stream. The calling thread (main in your case), has to do the following:
start the external process;
start a thread to purge the out stream;
start a thread to purge the err stream;
call waitFor() on the calling thread (main).
I found the above approach very robust and easy to implement (if you are familiar with threads). Please use an ExecutorService for the threads.
Launch the file with cmd for Windows Only
Older windows will probably have to use command.com instead
Runtime.getRuntime().exec("cmd /c start file.bat");
Using the start with /wait parameter waits until bat is finished without /wait it should work.
public class Command {
public static void main(String[] args) throws java.io.IOException, InterruptedException {
String path = "C:\\DOCUME~1\\\USER\\DESKTOP";
Process p = Runtime.getRuntime().exec("cmd /c start /wait " + path + "\\test.bat");
System.out.println("Waiting for batch file ...");
p.waitFor();
System.out.println("Batch file done.");
}
}
test.bat
#echo off
cls
:start
echo This is a loop
goto start
Use the command.com file to get short path name, since long path names don't get processed too well in start program.
Here is a sample I made download link below:
https://www.mediafire.com/?mu7vht3e6tto698
Your problem may be that you are not on a Windows Administrator account you could try, But this requires you to type in your Administrator password which is very stupid.
Process p = Runtime.getRuntime().exec("runas /profile /user:Administrator \"cmd.exe /c start test.bat\"");

Java process invoked by ProcessBuilder sleeps forever

I'm working on a big system, which I have written entirely in Java. But I'm also at some point communicating with a C program by running it as a process, communicating some input to it through the file system and then waiting for it to finish to read the results from it. For now, I can't do anything but this. There is no time for me to link them via an API or anything like that.
This worked well until I needed to invoke this process twice to get the results. One I make the first invocation, it works just fine. However, for the second invocation the process just hangs! I don't if it is sleeping and waiting for a signal but I don't see why it should do so.
Here is the method to do the invocation:
public synchronized boolean processCommand(List command) {
try {
ProcessBuilder pb = new ProcessBuilder(command);
Process p = pb.start();
p.waitFor();
p.destroy();
} catch(Exception ex) { return false; }
return true;
}
I really do not need to communicate to the stdout or stdin. I just need the process to run and finish its job. But it just hangs when the calling process is waiting for it ONLY the second time I call it!
My calling code is just simply making the command list and calling this method from another java object.
Both calls to the method processCommand(List command) work fine when the input to the C program is smaller. Would that be some issue with the stdin or stdout?
It's just driving me crazy! Anybody has an insight into this? I appreciate your commend :)
Update:
Here is the solution based on what #Gray mentioned:
I just need to drain the InputStream and possibly the ErrorStream:
public synchronized boolean processCommand(List command) {
try {
ProcessBuilder pb = new ProcessBuilder(command);
Process p = pb.start();
handleStream(p.getInputStream);
handleStream(p.getErrorStream);
p.waitFor();
p.destroy();
} catch(Exception ex) { return false; }
return true;
}
public void handleStream(InputStream input) {
try {
int c;
while( (c=input.read())!= -1) { //Anything }
} catch(Exception ex) { }
}
Try following the solution suggested here (The original link seems to be unavailable; an archived version can be found here, although the referenced link to the example code still points to the original site...)
The best strategy for handling this situation is to kick off a thread just prior to calling waitFor that will interrupt the current thread after a suitable time period. TimerTask was designed for precisely this sort of situation, and waitFor is very responsive to interrupts in most cases. [Follow link in original for an example].
[...]
Java 6 API clearly states that failure to promptly “read the output stream of the subprocess may cause the subprocess to block, and even deadlock.”
[...]
The safe bet for handling this problem today is to explicitly clean up every instance of Process by calling close on each stream made available through Process.getOutputSteam, Process.getInputStream, and Process.getErrorStream, and then call Process.destroy even if the process is already terminated.

Runtime.getRunTime().exec not behaving like C language "system()" command

In "C", I can run a long blocking process in the background (AND HAVE IT CONTINUE TO RUN) after the starting process has exited.
void main(void)
{
system("some_long_blocking_process &");
exit();
}
// "some_long_blocking_process" is still running here (DESIRED BEHAVIOR)
Java's getRuntime().exec() DOESN'T have this behavior. Instead, "some_long_blocking_process" ends immediately when the Java process ends.
Anyone know how I can recapture this behavior in Java?
I am using Java 1.4 (No process builder)
I specifically am looking to start the long blocking process and to exit immediately (no "waitFor(), etc.)
Things I have already tried (the process runs correctly, but I still get the same undesired behavior)
adding "nohup" and run in foreground ("nohup some_long_process")
adding "nohup" and running in background ("nohup some_long_process &")
run in foreground ("some_long_process")
run in background ("some_long_process &")
THANKS!
Thanks to all the suggestions... I've decided to use jtahlborn's answer (it worked for me)
try this:
String[] cmd = {"/bin/sh", "-c", "yourcommand args"};
Process p = Runtime.getRuntime().exec(cmd);
when redirect stream to /dev/null:
String[] cmd = {"/bin/sh", "-c", "yourcommand args > /dev/null 2>&1 &"};
Process p = Runtime.getRuntime().exec(cmd);
the only way we were able to achieve this with java was to add another layer of script. you need a simple wrapper script which invokes the app you actually want to run, e.g.:
runner.sh:
#!/bin/sh
nohup "$#" > /dev/null 2>&1 &
then invoke "/bin/sh runner.sh the real command" from your java program.
EDIT:
Have your tried this?
Runtime.getRuntime().exec("/bin/sh -c /usr/X11/bin/xterm &")
This worked for me on MacOS.
Previous answer (JDK 1.5, apologies for not reading the question correctly):
To execute a process without waiting you can use the ProcessBuilder
ProcessBuilder pb = new ProcessBuilder("/usr/X11/bin/xterm");
pb.start();
Your problem is probably due to the trailing &. Try removing it.
getRuntime().exec() is more similar to fork() and exec() than system().
system() passes the command to the shell, and it's Bash that understands that the trailing ampersand means to run the process in the background.
getRuntime().exec() parses the command using a StringTokenizer to parse the command, and doesn't do anything with the trailing ampersand. That's simply passed as the first argument to your some_long_blocking_process, which may exit out immediately on the unknown error.
Have you tried spawning a new Thread to run the executable? Try:
new Thread(new Runnable() {
#Override
public void run() {
try {
Runtime.getRuntime().exec(<your exec>);
} catch (IOException e) {
e.printStackTrace();
}
}
}).run();
This way, the main process won't shutdown until the exec has finished running (and the thread has finished).
Also, don't manually call exit() in your application unless you have some overwhelming reason to--the JVM does a good job of detecting when the application has finished on its own. This way, you won't force close threads that are running in the background.

Categories

Resources