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");
}
Related
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().
I run analysis on a file directly from an analyzer program and it finishes under one minute. But if I make jar of analyzer and run it via ProcessBuilder, it does not get finished even in 8 minutes (500 sec). Here is the code I am using for ProcessBuilder. Can someone please explain the reason?
I cannot run the analyzer program directly because depending upon the input file, it may actually take 15-20 minutes which I don't want. I want to finish it under 8 minutes.
Here is code for ProcessBuilder.
public void myFun(){
String fileName = file.getName();
System.out.println(file.getAbsolutePath());
Process p;
ProcessBuilder pb;
String filePath = file.getAbsolutePath();
pb = new ProcessBuilder("C:\\Program Files\\Java\\jdk1.7.0_13\\bin\\java.exe", "-jar", "ta.jar", filePath);
pb.directory(new File("D:\\Softwares\\analyzerRun\\bin"));
long startTime = System.currentTimeMillis();
try {
p = pb.start();
long currTime = System.currentTimeMillis();
long diff = currTime - startTime;
boolean isBreak = false;
while(diff < 500000)
{
currTime = System.currentTimeMillis();
diff = currTime - startTime;
if(processIsTerminated(p))
{
isBreak = true;
break;
}
}
if(!isBreak)
{
System.out.println("Interrupting current thread!!");
p.destroy();
Thread.currentThread().interrupt();
}
else
{
System.out.println("process terminated peacefully");
}
System.out.println("Done with "+ fileName);
} catch (IOException e) {
e.printStackTrace();
}
}
}
private static boolean processIsTerminated (Process process) {
try {
process.exitValue();
} catch (IllegalThreadStateException itse) {
return false;
}
return true;
}
Edit1:
So after given suggestions, I removed while loop and my try code looks like below now:
try {
p = pb.start();
p.waitFor();
}
Interestingly, I run this program and it does not get finished under 5 minutes. But if I run my analyzer directly, it gets finished under 30 seconds. Does ProcessBuilder not get enough priority from CPU?
Your polling loop is insane.
Suggestion:
1) start() your process
2) Save the start time.
3) Call waitFor() to block until the process is done.
4) Then take the delta.
You'll get MUCH better results - honest!
If you absolutely must run asynchronously (if your app cannot block while your subprocess runs), then create a new Java thread for steps 1) ... 4).
IMHO...
ALSO:
Run Windows Performance Monitor (or equivalent for your OS) and monitor I/O wait, CPU utilization, etc while the process is running to try to figure out where the latency might be coming from.
But your polling loop is itself introducing excessive CPU utilization. Don't do it!
Because you're wasting most of the time smoking the CPU with sleep-free polling, instead of:
(a) consuming the process's output and error streams, and then
(b) calling waitFor().
To answer this, "Interestingly, I run this program and it does not get finished under 5 minutes. But if I run my analyzer directly, it gets finished under 30 seconds. Does ProcessBuilder not get enough priority from CPU? "
After a long-time, I finally figured out that I was writing two lines on standard output in my original program. So, when I ran the jar file using ProcessBuilder, I didn't read from standard output. The process was getting hanged ultimately getting killed by the main-class thread. I just removed those two lines from the original program and now it takes equal time in both ways.
All,
I originally had a shell script that called SQLLoader (Oracles data upload tool).
The problem was that SQLLoader takes a plain text password as input so I decided to build a Java application to call SQLLoader internally passing a decrypted password into the command string.
e.g.
sqlldr user/pass#DBServer control=../sqlloader.ctl log=sqlloader.log data=mydata.csv
So with my java wrapper it became this in my shell script
java -jar sqlloader.jar sqlloader.ctl mydata.csv
However a new problem developed when SQLLoader complained there was no file to load. After some head scratching it was discovered that a subsequent command in my shell script seemed to be executing while my java application was still running. Therefore it was behaving asynchronously.
The next command was moving the input file sqlloader was using before it could get a chance to use it. So I put a sleep command in of 20 seconds to give my java application time to run.
java -jar sqlloader.jar sqlloader.ctl mydata.csv
echo $?
sleep 20
if [ $? -ne 0 ]
then
echo "SQLLoader failed during execution, please check the log : "
mv mydata.csv
else
echo "SQLLoader successfully processed file : "
mv mydata.csv
fi
Does anyone know why unix is behaving this way, does Java execute my SQLLoader as a different user/ thread?
This is my java code:
Runtime Rt;
Process Prc;
Prc = Rt.exec("sqlldr user/decryptedpass#DBServer control=../sqlloader.ctl log=sqlloader.log data=mydata.csv);
system.exit(0);
I checked the Runtime Class for anything about it being Asynchronous but couldnt find anything
http://docs.oracle.com/javase/7/docs/api/java/lang/Runtime.html
Any theories or suggestions?
Thanks
Yes. If you look at Runtime.exec again it does specify that it will launch a new process in the specified environment (e.g. independently of the current "environment" or as you put it asynchronously). You should use ProcessBuilder to create a Process and then waitFor that Process to finish before calling System.exit - which certainly isn't mandatory. Something like this
public static void main(String[] args) {
// String command = "/usr/bin/sleep 5";
List<String> command = new ArrayList<String>();
command.add("c:/cygwin/bin/sleep");
command.add("5");
ProcessBuilder pb = new ProcessBuilder(command);
BufferedReader is = null;
try {
System.out.println("Starting command " + command);
Process p = pb.start();
int ret = p.waitFor();
is = new BufferedReader(new InputStreamReader(p.getInputStream()));
String line;
while ((line = is.readLine()) != null) {
System.out.println(line);
}
if (ret == 0) {
System.out.println("Command has completed.");
System.exit(ret);
} else {
System.out.println("Command completed with return code " + ret);
System.exit(ret);
}
} catch (Exception e) {
System.out.println("Caught Exception " + e.getMessage()
+ " running command " + command);
e.printStackTrace();
} finally {
if (is != null) {
try {
is.close();
} catch (IOException e) {
}
}
}
System.out.println("COMMAND FAILED");
System.exit(1);
}
You need to wait for process completion, you should also read all output (stdout and stderr) from the process you are starting.
If you call exit() after exec(), Java will do just that - exit immediatedly.
Here is an article that explains Runtime.exec pitfalls: http://www.javaworld.com/javaworld/jw-12-2000/jw-1229-traps.html?page=4 (also consider the other pages).
this is basically what I am trying to do: I created a Process that simulates the command line. Like this:
private Process getProcess() {
ProcessBuilder builder = new ProcessBuilder("C:/Windows/System32/cmd.exe");
Process p = null;
try {
p = builder.start();
} catch (IOException e1) {
e1.printStackTrace();
}
return p;
}
Now I can "feed" this process with commands:
BufferedWriter p_stdin = new BufferedWriter(
new OutputStreamWriter(process.getOutputStream()));
try {
p_stdin.write("dir"); // Just a sample command
p_stdin.newLine();
p_stdin.flush();
} catch (IOException e) {
e.printStackTrace();
return "Failed to run " + fileName;
}
What I now would like to do is wait for the command, that is the subprocess of my process, to complete. How can I do that? I know that I can wait for processes with the waitFor() method, but what about a subprocess??
The dir command is not a subprocess, it is executed internal to cmd. However, this is not much relevant, anyway: from the perspective of Java any other command, which is launched in a subprocess, would behave the same.
To wait for the dir command to complete you must interpret the incoming stdout output from cmd and realize when the prompt was printed again. This is a quite brittle mechanism, though.
In any case, you currently don't consume cmd's stdout at all, which will cause it to block soon, never recovering.
This is a followup question to my other question : Run bat file in Java and wait
The reason i am posting this as a separate question is that the one i already asked was answered correctly. From some research i did my problem is unique to my case so i decided to create a new question. Please go read that question before continuing with this one as they are closely related.
Running the proposed code blocks the program at the waitFor invocation. After some research i found that the waitFor method blocks if your process has output that needs to be proccessed so you should first empty the output stream and the error stream. I did those things but my method still blocks. I then found a suggestion to simply loop while waiting the exitValue method to return the exit value of the process and handle the exception thrown if it is not, pausing for a brief moment as well so as not to consume all the CPU. I did this:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
public class Test {
public static void main(String[] args) {
try {
Process p = Runtime.getRuntime().exec(
"cmd /k start SQLScriptsToRun.bat" + " -UuserName -Ppassword"
+ " projectName");
final BufferedReader input = new BufferedReader(new InputStreamReader(p.getInputStream()));
final BufferedReader error = new BufferedReader(new InputStreamReader(p.getErrorStream()));
new Thread(new Runnable() {
#Override
public void run() {
try {
while (input.readLine()!=null) {}
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
new Thread(new Runnable() {
#Override
public void run() {
try {
while (error.readLine()!=null) {}
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
int i = 0;
boolean finished = false;
while (!finished) {
try {
i = p.exitValue();
finished = true;
} catch (IllegalThreadStateException e) {
e.printStackTrace();
try {
Thread.sleep(500);
} catch (InterruptedException e1) {
e1.printStackTrace();
}
}
}
System.out.println(i);
} catch (IOException e) {
e.printStackTrace();
}
}
}
but my process will not end! I keep getting this error:
java.lang.IllegalThreadStateException: process has not exited
Any ideas as to why my process will not exit? Or do you have any libraries to suggest that handle executing batch files properly and wait until the execution is finished?
Start cmd with the /c switch instead of /k and get rid of the start:
Process p = Runtime.getRuntime().exec(
"cmd /c SQLScriptsToRun.bat" + " -UuserName -Ppassword"
+ " projectName");
/k tells cmd: “Run that command and then stay open”, while /c says “Run that command and then exit.”
/k is for interactive use where you want an initializing batch file and afterwards still use the console.
Your main problem here, however, is that you are creating yet another process by using start. To run a batch file this is totally unnecessary and prevents you from knowing when the batch was run completely, since Java has a reference to the original cmd process you started, not the one you spawned with start.
In principle, this now looks like the following:
Java program starts
Java program runs cmd and instructs it to run start foo.bat and stay open for interactive input (/k)
Java memorizes the process ID (PID 42) to later reference that process
cmd (PID 42) starts
cmd (PID 42) runs start foo.bat
start foo.bat launches another instance of cmd, since that's what should happen to run batch files
cmd (PID 57005) starts
cmd (PID 57005) runs foo.bat
cmd (PID 57005) exits (This marks the event you'd like to know about)
cmd (PID 42) shows the prompt and obediently waits for input (unbeknownst to them the prompt is never seen by a user and no input will ever come ... but cmd (PID 42) waits ...)
Java likes to know whether the process is finished and checks PID 42
Yup, it's still there. Now what?
What you want (and what above change will do) is:
Java program starts
Java program runs cmd and instructs it to run foo.bat and close after running the command (/c)
Java memorizes the process ID (PID 42) to later reference that process
cmd (PID 42) starts
cmd (PID 42) runs foo.bat
cmd (PID 42) exits
Java likes to know whether the process is finished and checks PID 42
Hooray, the process is gone, the batch file has been run.