Runtime.exec() won't finish properly - java

I've created a WebApp that relies on external scripts to gather query request by the user (internal software). I've tested, with sucess, the WebApp off the glassfish server provided by netbeans but whenever I try and upload my App to a third party server (Apache Tomcat) I run into the issue of the process.getRuntime exitValue never being written and the WebApp never gets to the result page....
This is the code that I have implemented so far:
Update --> The code now works after reading both stderr and stdin:
pd = new ProcessBuilder("runscript.bat");
pd.redirectErrorStream(true);
process = pd.start();
StreamGobbler inputGobbler = new StreamGobbler(process.getInputStream(), "Input");
StreamGobbler errorGobbler = new StreamGobbler(process.getErrorStream(),"Error");
errorGobbler.start();
inputGobbler.start();
int exitVal = -1;
try {
exitVal = process.waitFor();
} catch (InterruptedException ex) {
//log Error
}
class StreamGobbler extends Thread {
OutputWriterReader outWR = new OutputWriterReader();
BufferedWriter deadWriter;
InputStream is;
// reads everything from is until empty.
StreamGobbler(InputStream is, String type) {
this.is = is;
createWatch(type);
}
// depending on if Error stream or Input Stream
private void createWatch(String type){
try {
if(type.equals("Error"))
deadWriter = new BufferedWriter(new FileWriter("StdError.txt"));
else if(type.equals("Input"))
deadWriter = new BufferedWriter(new FileWriter("StdInput.txt"));
} catch (IOException ex) {
//log Error
}
}
#Override
public void run() {
try {
InputStreamReader isr = new InputStreamReader(this.is);
BufferedReader br = new BufferedReader(isr);
String line=null;
while ( (line = br.readLine()) != null)
deadWriter.append(line);
deadWriter.flush();
deadWriter.close();
} catch (IOException ioe) {
//log Error
}
}
}
Any Suggerstions? Thanks in advance for any help

The process may not be complete when you call exitValue() on it.
Before process.exitValue() call add:
process.waitFor();

Related

Starting external application inside Java

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();
}

Obtaining process CPU usage

I have a program that loads a text file holding some information and based on that information it runs multiple thread. Each thread is a process. Here is my code:
public class runMultiClient {
public static void main(String[] args){
List<Process> PRlist = new ArrayList<Process>();
List<String[]> commandsList = new ArrayList<String[]>();
boolean running = true;
if (args.length == 2 && args[0].matches("-f")){
String dir = System.getProperty("user.dir");
String path = dir + "/" + args[1];
FileReader fr;
try {
fr = new FileReader(path);
BufferedReader bf = new BufferedReader(fr);
String line = "";
while ((line = bf.readLine()) != null){
String[] tk = line.split(" ");
String[] cmd = {"java", "-jar", "Client.jar", "-a", tk[0], "-p", tk[1],
"-u", tk[2], "-pw", tk[3], "-m", tk[4], "-s", tk[5]};
Process pr = new ProcessBuilder().inheritIO().command(cmd).start();
PRlist.add(pr);
commandsList.add(cmd);
System.out.println(tk[4] + " streaming process is established.");
}
}
catch (FileNotFoundException ex) {ex.printStackTrace();}
catch (IOException ex) {ex.printStackTrace();}
} else {
System.out.println("No stream file was specified.");
}
}}
Inside my Client.jar file, i have a variable that monitors the cpu load of that class:
OperatingSystemMXBean osBean = ManagementFactory.getPlatformMXBean(OperatingSystemMXBean.class);
cpuLoad = osBean.getProcessCpuLoad();
Is there any way i can reach that variable from the runMultiClient class?
If not, is there any way of using the OperatingSystemMXBean on the running process?
I have tried pr.getClass(), but it got me nowhere.
Any help would be appreciated.
Option #1: Add agent library and expose JMX over HTTP
You can bundle Jolokia agent with your monitored application (another similar thing is SimpleJMX. It exposes JMX beans over http/json so this works for interacting with JMX from other languages like python (and super comfy when troubleshooting from command like). After that you can access mbeans of your interest via apache http client or the like.
Option #2: JMX client
allow remote connections by adding the following params when starting your monitored application:
-Dcom.sun.management.jmxremote.port=9999 \
-Dcom.sun.management.jmxremote.authenticate=false \
-Dcom.sun.management.jmxremote.ssl=false
Then you should be able to access the mbeans by jconsole and hand written JMX client code, like in the tutorial
Not sure if you need to call ProcessBuilder().inheritIO() for some other requirement, but if not, you could start a daemon thread in your Client.jar process that periodically writes the cpu load to System.out. Then your runMultiClient thread[s] could read those from the InputStream representing the process's System.out. Or, have the thread accept commands and print accordingly. Rough example:
Run this in the spawned Client.jar:
public static void startCmdListener() {
try {
Thread t = new Thread("CmdListener") {
BufferedReader br = null;
InputStreamReader isr = null;
final OperatingSystemMXBean os = (OperatingSystemMXBean) ManagementFactoryHelper.getOperatingSystemMXBean();
public void run() {
try {
isr = new InputStreamReader(System.in);
br = new BufferedReader(isr);
} catch (Exception ex) {
ex.printStackTrace(System.err);
return;
}
try {
String cmd = null;
while(true) {
cmd = br.readLine();
if("cpu".equalsIgnoreCase(cmd)) { // cpu command, print the process load
System.out.println(os.getProcessCpuLoad());
} else if("exit".equalsIgnoreCase(cmd)) { // exit command, break
break;
}
}
} catch (Exception ex) {
ex.printStackTrace(System.err);
return;
}
}
};
t.setDaemon(true);
t.start();
} catch (Exception ex) {
ex.printStackTrace(System.err);
}
}
Run this in the runMultiClient to get the cpu load:
public static double getCpu(OutputStream processIn, InputStream processOut) {
PrintStream ps = null;
BufferedReader br = null;
InputStreamReader isr = null;
try {
ps = new PrintStream(processIn);
isr = new InputStreamReader(processOut);
br = new BufferedReader(isr);
ps.println("cpu");
ps.flush();
return Double.parseDouble(br.readLine());
} catch (Exception ex) {
throw new RuntimeException(ex);
} finally {
if(ps!=null) try { ps.close(); } catch (Exception x) {}
if(br!=null) try { br.close(); } catch (Exception x) {}
if(isr!=null) try { isr.close(); } catch (Exception x) {}
}
}

Freezing window when using multithread in java

I've got a gui in java and I want to run inside my GUI when a button is pressed one executable. My code for running the executable is the following:
Process pr;
Runtime rt = Runtime.getRuntime();
new Thread(() -> {
try {
Process proc = rt.exec("Release\\face.exe", null, new File("Release\\"));
} catch (Exception e1) {
e1.printStackTrace();
}
}).start();
The executable is running when I am pressing the button however I detect a freezing in the executable behavior. I tried to add multithreading to check if that is the reason for freezing, however still I am experienced the same thing. What could be wrong here?
EDIT:
I tried to add the following lines for inputStreamReader inside my thread:
new Thread(() -> {
try {
Runtime rt = Runtime.getRuntime();
Process proc = rt.exec("face.exe", null, new File("Release\\"));
BufferedReader in = new BufferedReader(new InputStreamReader(proc.getInputStream()));
BufferedReader err = new BufferedReader(new InputStreamReader(proc.getErrorStream()));
} catch (Exception e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}).start();
I got the message InputStreamReader cannot be resolved to a type.
As bowmore mentioned, your executable likely blocks because the output is not handled and "gets stuck" after a while as the executable cannot write more to its standard out (likely the console).
I usually use code like the following to run external commands from java:
Process p = Runtime.getRuntime().exec(args);
BufferedReader in = new BufferedReader(new InputStreamReader(p.getInputStream()));
BufferedReader err = new BufferedReader(new InputStreamReader(p.getErrorStream()));
StreamHandler outputHandler = new StreamHandler(in);
outputHandler.start();
StreamHandler errorHandler = new StreamHandler(err);
errorHandler.start();
where StreamHandler is defined as follows:
class StreamHandler extends Thread {
private final BufferedReader in;
private final JTextArea[] textArea;
public StreamHandler(final BufferedReader in) {
this.in = in;
}
#Override
public void run() {
try {
String line = null;
while ((line = this.in.readLine()) != null) {
System.out.println(line);
}
} catch (IOException e) {
log.info("StreamHandler got interrupted");
e.printStackTrace();
}
}
}
Naturally, the stream handler could be more sophisticated, e.g. write errors to System.err instead of System.out, or to write to a JTextArea, or somewhere else.
Since you don not read the InputStreams for this process object, it is possible that it's stuck because the stream's buffer is full, causing the exe to block on the write to stdout.

Process started from Java hangs

I am trying to execute a c++ code from java on a remote Windows machine. In order to deal with the remote part, I have created a Web service from where the actual command is run using Runtime.exec(). The c++ exe is not being called directly from the java code. I have a batch file that eventually calls the exe.
The problem is, both java and c++ processes hang. The java code on server side does handle the output stream and error stream. Also, the c++ code is logging everything in a file on Windows. The strange thing is that, when I remove the WS call and run the java code on server side as a standalone java program, it succeeds. Here is the java code:
public class RunCPlusPlusExecutable {
public int runExecutable() {
int exitValue = 0;
try {
Process p = null;
Runtime rt = Runtime.getRuntime();
System.out.println("About to execute" + this + rt);
p = rt.exec("c:/temp/execcplusplus.bat");
System.out.println("Process HashCode=" + p.hashCode());
StreamProcessor errorHandler = new StreamProcessor(p.getErrorStream(), "Error");
StreamProcessor outputHandler = new StreamProcessor(p.getInputStream(), "Output");
errorHandler.start();
outputHandler.start();
exitValue = p.waitFor();
System.out.println("Exit value : " + exitValue);
if (exitValue == 0)
System.out.println("SUCCESS");
else
System.out.println("FAILURE");
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (Exception e) {
}
return exitValue;
}
class StreamProcessor extends Thread {
private InputStream is = null;
private String type = null;
private InputStreamReader isr = null;
private BufferedReader br = null;
private FileWriter writer = null;
private BufferedWriter out = null;
StreamProcessor(InputStream is, String type) {
this.is = is;
this.type = type;
}
public void run() {
try {
isr = new InputStreamReader(is);
br = new BufferedReader(isr);
writer = new FileWriter("*******path to log file********");
out = new BufferedWriter(writer);
String line = null;
while ((line = br.readLine()) != null) {
Date date = new Date();
out.write("[" + type + "]: " + date + " : " + line);
out.newLine();
}
writer.flush();
} catch (IOException ioe) {
ioe.printStackTrace();
} finally {
try {
if (br != null)
br.close();
if (isr != null)
isr.close();
if (out != null)
out.close();
if (writer != null)
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
Any idea what is causing the problem and how to debug it? Please note that I won't be able to debug the c++ code.
Thanks
Update 1:
Here are some more details...
The WS server is running from some admin user. And I have been running the standalone java program from some other user.
*It seems that the c++ executable is giving referenced memory error while executing from WS call. There are pop-ups citing the error with OK and Cancel buttons. *
Update 2:
The tomcat server where the WS is deployed is running as a Windows NT service. Can that be the cause of the error? If yes, how to resolve this?

Reading streams from java Runtime.exec

I have the following snippet of code:
Process proc = runtime.exec(command);
errorGobbler = new ErrorStreamGobbler(proc.getErrorStream(), logErrors, mdcMap);
outputGobbler = new OutputStreamGobbler(proc.getInputStream(), mdcMap);
executor.execute(errorGobbler);
executor.execute(outputGobbler);
processExitCode = proc.waitFor();
where the gobblers are Runnables which use a BufferedReader to read the input and error streams of the executing process. While this works most of the time, I get the occasional window (of about 2 minutes or so) where I get the processExitCode as 0, which indicates normal termination but there is nothing in the input and error streams - nothing to even indicate end-of-stream.
Like I indicated before, this works most of the time but this failure occurs every once in a while - and I am totally puzzled. Any ideas?
Rags
I've struggled with the same kind of issues.
I can't remember what exactly was wrong (maybe I forgot to flush / close the streams correctly or something ...).
Anyway, here is what I came up with.
/**
* Handle communication with a process, reading its output/error and feeding its input
* #param process The process to execute
* #param _in Reader that will feed the input pipe of the process
* #param out Writer that will receive the output of the process
* #param err Writer that will receive the error pipe of the process
*/
public static void communicate(
Process process,
final Reader _in,
final Writer out,
final Writer err)
{
// Buffer the input reader
final BufferedReader in = new BufferedReader(_in);
// Final versions of the the params, to be used within the threads
final BufferedReader stdOut = new BufferedReader(new InputStreamReader(process.getInputStream()));
final BufferedReader stdErr = new BufferedReader(new InputStreamReader(process.getErrorStream()));
final BufferedWriter stdIn = new BufferedWriter(new OutputStreamWriter(process.getOutputStream()));
// Thread that reads std out and feeds the writer given in input
new Thread() {
#Override public void run() {
String line;
try {
while ((line = stdOut.readLine()) != null) {
out.write(line + newline);
}
} catch (Exception e) {throw new Error(e);}
try {
out.flush();
out.close();
} catch (IOException e) { /* Who cares ?*/ }
}
}.start(); // Starts now
// Thread that reads std err and feeds the writer given in input
new Thread() {
#Override public void run() {
String line;
try {
while ((line = stdErr.readLine()) != null) {
err.write(line + newline);
}
} catch (Exception e) {throw new Error(e);}
try {
err.flush();
err.close();
} catch (IOException e) { /* Who cares ?*/ }
}
}.start(); // Starts now
// Thread that reads the std in given in input and that feeds the input of the process
new Thread() {
#Override public void run() {
String line;
try {
while ((line = in.readLine()) != null) {
stdIn.write(line + newline);
}
} catch (Exception e) {throw new Error(e);}
try {
stdIn.flush();
stdIn.close();
} catch (IOException e) { /* Who cares ?*/ }
}
}.start(); // Starts now
// Wait until the end of the process
try {
process.waitFor();
} catch (Exception e) {
throw new Error(e);
}
} // End of #communicate
I hope this helps.

Categories