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()
Related
I'm running a jar file from another jar like here somebody answers but waiting for the process.
Process proc = Runtime.getRuntime().exec("java -jar A.jar" + stringParams);
try {
proc.waitFor();
} catch (InterruptedException e) {
e.printStackTrace();
}
InputStream in = proc.getInputStream();
InputStream err = proc.getErrorStream();
My problem comes when i have no feedback on the status of the program that is called, but i don't want my program continues beyond those lines. I would need the standard and error outputs but the results are shown when the execution is over. Is there any way of executing and getting those streams while the jar is still running?
Buffered/unbuffered
It seems like an issue with buffered output.
Executed process (in this case java -jar <path>) buffers output and writes it only when it's done (in big chunks, we don't like that!)
So one way to go is execute process through unbuffering (very hacky tools):
unbuffered <command>
stdbuf -i0 -o0 -e0 <command>
use terminal emulation
Hacking
stdbuf is part of GNU tools.
https://www.gnu.org/software/coreutils/manual/html_node/stdbuf-invocation.html
unbuffered is part of expect package.
https://wiki.tcl.tk/3548
The key thing is making the program thinking that it's in interactive mode (like you are launching it from console).
The first two options are very hacky and do not work in all cases (idk if java command works with them?)
Emulation
The third option is most promising.
We launch a program (terminal emulator) that emulates interactive terminal making program think it's working in real active session!
Pty4j
You might use pty4j too:
From there: https://github.com/traff/pty4j
// The command to run in a PTY...
String[] cmd = { "java", "-jar", "path_to_jar" };
// The initial environment to pass to the PTY child process...
String[] env = { "TERM=xterm" };
PtyProcess pty = PtyProcess.exec(cmd, env);
OutputStream os = pty.getOutputStream();
InputStream is = pty.getInputStream();
// ... work with the streams ...
// wait until the PTY child process terminates...
int result = pty.waitFor();
// free up resources.
pty.close();
Zt-exec
Maybe it's worth trying zt-exec?
I have no idea how it executes commands.
But it may be it (I didn't test that).
Using https://github.com/zeroturnaround/zt-exec
new ProcessExecutor().command("java", "-jar path_to_jar")
.redirectOutput(new LogOutputStream() {
#Override
protected void processLine(String line) {
...
}
})
.execute();
That should work, but I didn't test that.
In general, there are no ways to nicely resolve your problem.
Depending on what platforms you want to target consider using unbuffered, stdbuff or the (slowest) terminal emulation...
Please let me know if that helps and good luck! :)
After having read a lot of similar questions about Runtime.exec and Java, I've not encountered a safe and clean idiom for compiling and running a .java file. I have just finished reading the "When Runtime.exec() won't" article, which helped me understand the general do's and don't's, but does not provide this idiom I'm looking for.
Overall I'm trying to create separate processes that will compile and run in series (multiple servers), without hanging or leaving any orphan processes. I'm not well-versed with these topics, so I am looking for more pointers.
So far I am able to compile the file by doing this:
try {
Runtime rt = Runtime.getRuntime();
Process proc = rt.exec("javac Server.java");
int exitVal = proc.waitFor();
System.out.println("Process exitValue: " + exitVal);
} catch (Throwable t) {
t.printStackTrace();
}
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).
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.
I'm trying to run a system command to extract a tar.bz2 file to a specified directory. This is the code:
ProcessBuilder myProc = new ProcessBuilder("tar", "-xjf", "/path/to/MyTarFile.tar.bz2");
myProc.directory(new File("/directory/i/want/results/in/"));
myProc.start();
System.out.println(myProc.command());
It runs without error, however the file is deleted and not extracted anywhere.
Any help would be greatly appreciated.
I know Runtime.exec() has a really nasty feature where if you don't manually drain STDOUT/STDERR, it effectively appears to hang. I would hope that ProcessBuilder corrected that deficiency, but this page includes this tidbit:
A word of caution about the examples in this tip. It is possible that the examples will deadlock if the subprocess generates enough output to overflow the system. A more robust solution requires draining the process stdout and stderr in separate threads.
So, make sure you're handling Process.getInputStream() and Process.getErrorStream(), as I recommended in the comments; it could solve the problem outright!
Change the myProc.start(); line to
Process p = myProc.start();
p.waitFor();
That will make sure your program doesn't exit until the tar is finished.
Run this to see errors. Perhaps one of your paths is incorrect.
import java.io.File;
import java.io.InputStream;
public class Untar {
public static void main(String[] args) throws Exception {
ProcessBuilder myProc = new ProcessBuilder("tar", "-xjf", "foo.tar.bz2");
myProc.directory(new File("newdir"));
Process p = myProc.start();
InputStream is = p.getErrorStream();
int c;
while( (c = is.read()) != -1 ){
System.out.print((char)c);
}
p.waitFor();
System.out.println(myProc.command());
}
}
Just tried this code. It works.
Check your paths.