I am a pretty new guy to the world of threading, have been trying to solve this problem for a week now.
The run method in the Thread class is not being called for some reason, I dont know why ( but would love to know)
ProcessBuilder processBuilder = new ProcessBuilder();
processBuilder.command("/bin/sh", "-c", "echo \"w30000001z,none,16488,,181075\nw30000001z,none,16488,,181082\n\" | /home/beehive/bin/exec/tableSize");
Process process = processBuilder.start();
process.waitFor();
InputStream stdin = process.getInputStream();
OutputStream stdout = process.getOutputStream();
InputStream stderr = process.getErrorStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(stdin));
BufferedReader error = new BufferedReader(new InputStreamReader(stderr));
StreamGobbler errorStream = new StreamGobbler(process.getErrorStream(), "ERROR");
StreamGobbler outputStream = new StreamGobbler(process.getInputStream(), "OUTPUT");
errorStream.start();
outputStream.start();
errorStream.join();
outputStream.join();
tableSize is a python executable which takes input through its stdin, processes it and outputs a few lines of text. I need to collect this output and do some further processing on it.
There is a separate thread to process the output on the inputStream and errorStream. This thread class is as shown below.
/* StreamGobbler.java */
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
class StreamGobbler extends Thread
{
InputStream is;
String type;
OutputStream os;
StreamGobbler(InputStream is, String type)
{
this(is, type, null);
}
StreamGobbler(InputStream is, String type, OutputStream redirect)
{
this.is = is;
this.type = type;
this.os = redirect;
}
public void run()
{
try
{
PrintWriter pw = null;
if (os != null)
pw = new PrintWriter(os);
System.out.println("Ajay" + type);
InputStreamReader isr = new InputStreamReader(is);
BufferedReader br = new BufferedReader(isr);
String line=null;
while ( (line = br.readLine()) != null)
{
if (pw != null)
pw.println(line);
System.out.println(type + ">" + line);
}
if (pw != null)
pw.flush();
} catch (IOException ioe)
{
ioe.printStackTrace();
}
}
}
For some reason, the run method in the StreamGobbler class is not being called. However the constructor of this class is being executed.
Any help would be much appreciated.
You need to start the stream gobblers before you call process.waitFor().
The stream gobblers read and discard the external processes output / error streams so that the external process doesn't block while trying to write. But the way you have it, the process has to have finished writing (and exited) before you attempt to gobble the output.
Result - deadlock ... if the external process writes too much to its output streams.
Actually, I think that the primary problem is that your Java code does not write anything to inputStream. So, the external process just sits there waiting for input that never arrives ... and process.waitFor() waits for the external process. Deadlock.
The correct order of things should be:
build the process and start it.
build the gobblers and start them.
open the input stream.
write stuff to input stream.
close input stream.
call process.waitFor() and check the processes return code.
look at the output captured by the gobblers
You need consume the STDOUT and/or STDERR before Process.waitFor(), here's an excellent article:
http://www.javaworld.com/javaworld/jw-12-2000/jw-1229-traps.html?page=2
1) Try removing the waitFor() if it is not required. It only makes the executing thread wait until the process execution is completed.
Related
I am unable to read the complete output of the program and sometimes it hangs the interface and does not compiles completely.
In the output console of the netbeans it displays complete output but not in jtextarea.
Help me out to first execute command in cmd(command prompt) and then read output to textarea from cmd.
In cmd the command executes quickly with complete results. But I have no idea how to get results from cmd.
Here is my code:
String line;
String [] cmds={"xyz.exe","--version"};
try{
Process p =Runtime.getRuntime().exec(cmds);
p.waitFor();
int val=p.exitValue();
if(val==0)
{
b1.setForeground(Color.green);
InputStream ins = p.getInputStream();
InputStreamReader insr = new InputStreamReader(ins);
BufferedReader br = new BufferedReader(insr);
while ((line = br.readLine()) != null) {
System.out.println( line);
t1.setText( line);
}
} else if(val==1)
{
b1.setForeground(Color.red);
InputStream error = p.getErrorStream();
InputStreamReader isrerror = new InputStreamReader(error);
BufferedReader bre = new BufferedReader(isrerror);
while ((line = bre.readLine()) != null) {
t1.setText(line);
}
}
} catch(IOException e){
System.out.println("error");
e.printStackTrace();
} catch (InterruptedException ex) {
Logger.getLogger(MainFrame.class.getName()).log(Level.SEVERE, null, ex);
}
The problem I see is this. You are trying to execute a program but you are not reading from the outputstream (ie. getInputStream() of Process) of the program until it has finished executing. So if your subProcess happens to have a lot of output and it exhausts the buffers, then the subProcess will be blocked.
So to solve your problem, you need to be absolutely reading the inputstream and errorstream while the subprocess is still executing to avoid the subprocess being blocked. You can either use blocking IO which means you need separate threads to service outputstream and errorstream or non blocking IO which you can use 1 thread that continually monitor outputstream & errorstream for data to be read.
Java Docs on java.lang.Process says
Because some native platforms only provide limited buffer size for standard input and output streams, failure to promptly write the input stream or read the output stream of the subprocess may cause the subprocess to block, and even deadlock.
import java.io.*;
import java.nio.channels.*;
import java.nio.*;
public static void nioExecute() throws Exception {
Process p = Runtime.getRuntime().exec("javac");
ReadableByteChannel stdoutChannel = Channels.newChannel(p.getInputStream());
ReadableByteChannel stderrChannel = Channels.newChannel(p.getErrorStream());
ByteBuffer buffer = ByteBuffer.allocate(1000);
StringBuilder stdOut = new StringBuilder();
StringBuilder stdErr = new StringBuilder();
while(stdoutChannel.isOpen() || stderrChannel.isOpen()) {
buffer.clear();
if(stderrChannel.isOpen()) {
int bytesRead = stderrChannel.read(buffer);
if(bytesRead>0) stdErr.append(new String(buffer.array(),0,bytesRead));
if(bytesRead==-1) stderrChannel.close();
}
buffer.clear();
if(stdoutChannel.isOpen()) {
int bytesRead = stdoutChannel.read(buffer);
if(bytesRead>0) stdOut.append(new String(buffer.array(),0,bytesRead));
if(bytesRead==-1) stdoutChannel.close();
}
Thread.sleep(100);
}
if(stdOut.length()>0) System.out.println("STDOUT: " + stdOut);
if(stdErr.length()>0) System.out.println("STDERR: " + stdErr);
}
I have a python script and it takes a long time to finish. I would like to run it from Java, but also output the script's output while it is executing, so that I can tell if it is properly running.
I've searched and only found examples where we output the output after the system command has finished, rather than during its execution.
Any way to do it while the script is running?
Here's what I have
public void doSomething() throws IOException {
String[] callAndArgs = {"python", "/hi.py"};
Process p = Runtime.getRuntime().exec(callAndArgs);
BufferedReader stdInput = new BufferedReader(new InputStreamReader(p.getInputStream()));
BufferedReader stdError = new BufferedReader(new InputStreamReader(p.getErrorStream()));
String s;
while ((s = stdInput.readLine()) != null) {
System.out.println(s);
}
while ((s = stdError.readLine()) != null) {
System.out.println(s);
}
}
i managed to get it working like this (Note it requires java7):
package test;
import java.lang.ProcessBuilder.Redirect;
public class Test {
public static void main(String... args) throws Exception {
ProcessBuilder pb = new ProcessBuilder("python","/home/foobar/Programming/test/src/test/test.py");
pb.redirectOutput(Redirect.INHERIT);
Process p = pb.start();
p.waitFor();
}
}
python (note i flush on python to make it work using sys.stdout.flush())
import time,sys
c =0
while c<=50:
time.sleep(1)
print("----")
c = c +1
sys.stdout.flush()
Note if you don't want to flush in a loop you can use this:
ProcessBuilder pb = new ProcessBuilder("python","-u","/home/foobar/Programming/NetBeansProjects/test/src/test/test.py");
Redirect.INHERIT
Indicates that subprocess I/O source or destination will be the same as those of the current process. This is the normal behavior of most operating system command interpreters (shells).
I've searched and only found examples where we output the output after
the system command has finished, rather than during its execution.
That's weird, because your example should be dumping the output as the command is executing.
Instead of using BufferedReader, you could try reading directly from the InputStream instead as the required conditions for readLine might not be being met until after the process exits.
I'd also recommend that you use a ProcessBuilder over Process directly, as, apart from anything else, it allows you to redirect the output from the error stream into the input stream, allowing you to read just one stream instead of two...
This might also be an issue with Python and how it flushes it output buffers...
For example, rather then waiting for the BufferedReader to decide when to return, try printing each character from the stream as it occurs/is reported
ProcessBuilder pb = new ProcessBuilder("test.py");
pb.redirectError();
Process p = pb.start();
InputStream is = null;
try {
is = p.getInputStream();
int in = -1;
while ((in = is.read()) != -1) {
System.out.print((char)in);
}
} finally {
try {
is.close();
} catch (Exception e) {
}
}
Update
Doing a little reading, Python seems to be buffering its out out before sending it to the stdout. I don't think you can fix this on the a Java side, but need to alter either the way Python is run or the script works.
See How to flush output of Python print? for more details
I'm suspecting that you are writing to stderr, which you can't see because you are blocking on stdin. Use a ProcessBuilder instead of doing exec. This way, you can redirect stderr and stdin into a single stream.
Here is an example:
import java.io.*;
public class Test {
public static void main(String... args) throws IOException {
ProcessBuilder pb =
new ProcessBuilder("test.py");
pb.redirectErrorStream(true);
Process proc = pb.start();
Reader reader = new InputStreamReader(proc.getInputStream());
BufferedReader bf = new BufferedReader(reader);
String s;
while ((s = bf.readLine()) != null) {
System.out.println(s);
}
}
}
Alternatively you can spawn threads to read from stdin/stderr respectively.
Another thing to look for is output buffering by python. You can see if this is the cause by doing:
import sys
sys.stdout.flush()
after you write to stdout
Don't use #readLine as the conditional in your while loop. Instead wrap your inputStream in a scanner and use #hasNextLine()
Scanner in = new Scanner(p.getInputStream());
while (in.hasNextLine()) {
System.out.println(in.nextLine());
}
To understand the situation, I try to describe what I'm trying to achieve:
I would like to create a java program that starts another java program (a minecraft server), monitors it's output, and sends commands to it's input accordingly.
I was able to start the jar program from my code, and I was also able to write an output listener for it, that currently only prints the jar program's output to the console.
The jar program I'm running from my code has a gui with a text output panel, where it shows messages, and an input field (textarea) where I can give commands. My problem is that when I start my code and it starts the program everything works fine until I want to write directly into the program's input field. If I type any letter into the input field, the gui freezes.
What do I wrong?
Here's my code:
public static void main(String[] args) {
try {
System.out.println("Starting.");
Runtime rt = Runtime.getRuntime();
Process proc = rt.exec("java -jar \"D:\\Program Files\\mc\\minecraft_server.jar\"");
// output both stdout and stderr data from proc to stdout of this process
StreamGobbler errorGobbler = new StreamGobbler(proc.getErrorStream());
StreamGobbler outputGobbler = new StreamGobbler(proc.getInputStream());
errorGobbler.start();
outputGobbler.start();
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
The output reader:
public class StreamGobbler extends Thread {
InputStream is;
// reads everything from is until empty.
StreamGobbler(InputStream is) {
this.is = is;
}
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(line);
} catch (IOException ioe) {
ioe.printStackTrace();
}
}
}
Thanks in advance! :)
First of all, instead of using the stream gobbler, just inherit the process's io:
final ProcessBuilder p1 = new ProcessBuilder(command).inheritIO();
p1.start();
If you don't care about stdin/out, use this to direct everything to NUL (windows version of /dev/null):
p1.redirectInput(Redirect.from(new File("NUL"))).redirectOutput(Redirect.to(new File("NUL")))
.redirectError(Redirect.to(new File("NUL")));
Does the program still crash?
public static void executeCommand(String cmd) {
try {
Process process = Runtime.getRuntime().exec(cmd, null,
new File("/usr/hadoop-0.20.2/"));
InputStream stdin = process.getInputStream();
InputStreamReader isr = new InputStreamReader(stdin);
BufferedReader br = new BufferedReader(isr);
String line;
System.out.println("<output></output>");
while ((line = br.readLine()) != null)
System.out.println(line);
InputStreamReader esr = new InputStreamReader(
process.getErrorStream());
BufferedReader errorReader = new BufferedReader(esr);
String lineError;
while ((lineError = errorReader.readLine()) != null)
System.out.println(lineError);
process.waitFor();
System.out.println("");
} catch (Exception e) {
e.printStackTrace();
}
}
Here's my code for executing a command named 'cmd'. But I cannot get realtime output through this code. The output comes out when the command finishes. I want realtime output. Is there a way to do this?
The issue you describe is most likely caused by the application you called: many applications use unbuffered I/O when connected to a terminal, but bufferen I/O when connected to a pipe. So your cmd may simply decide not to write its output in small bits, but instead in huge chunks. The proper fix is to adjust the command, to flush its output at the appropriate times. There is little you can do about this on the Java side. See also this answer.
I think you need to have a thread for handling the output.
You should try first with the cmd which run for a while
Last time, when I try with wvdial command (this wvdial will not finish until we stop it), I need a thread to read the output of wvdial
Actually, the problem is that Process.getInputStream() returns a BufferedReader.
So, even if the called subprocess flushes all its output, a read in the calling Java program will only get it if the buffer is full.
I need to start external executable in such way that user can interact with program that was just started.
For example in OpenSuse Linux there is a package manager - Zypper. You can start zypper in command mode and give commands like install, update, remove, etc. to it.
I would like to run it from Java code in a way user could interact with it: input commands and see output and errors of the program he started.
Here is a Java code I tried to use:
public static void main(String[] args) throws IOException, InterruptedException {
Process proc = java.lang.Runtime.getRuntime().exec("zypper shell");
InputStream stderr = proc.getInputStream();
InputStreamReader isr = new InputStreamReader(stderr);
BufferedReader br = new BufferedReader(isr);
String line = null;
char ch;
while ( (ch = (char)br.read()) != -1)
System.out.print(ch);
int exitVal = proc.waitFor();
System.out.println("Process exitValue: " + exitVal);
}
But unfortunately I can only see it's output:
zypper>
but no matter what I write, my input doesn't affect program that was started.
How can I do what want to?
You need to get an output stream in order to write to the process:
OutputStream out = proc.getOuptutStream();
This output stream is piped into the standard input stream of the process, so you can just write to it (perhaps you want to wrap it in a PrintWriter first) and the data will be sent to the process' stdin.
Note that it might also be convenient to get the error stream (proc.getErrorStream) in order to read any error output that the process writes to its stderr.
API reference:
http://download.oracle.com/javase/6/docs/api/java/lang/Process.html
Seems like the converting inside the while condition fails in your example, this seems to work better (I don't run Suse so I haven't tried with Zypper):
public static void main(String[] args) throws IOException, InterruptedException
{
//Process proc = java.lang.Runtime.getRuntime().exec("zypper shell");
Process proc = java.lang.Runtime.getRuntime().exec("ping -t localhost");
InputStream stderr = proc.getInputStream();
InputStreamReader isr = new InputStreamReader(stderr);
BufferedReader br = new BufferedReader(isr);
int i;
while ( (i = br.read()) != -1)
{
System.out.print((char) i);
}
int exitVal = proc.waitFor();
System.out.println("Process exitValue: " + exitVal);
}
I recently wrapped Google Closure Compiler into a .jar-file which is extracted and used in a Process. This compiler only talks via System.in/out/err. There's a big "gotcha" in connecting pipes together, which is just briefly mentioned in the Process javadoc.
"...failure to promptly write the
input stream or read the output stream
of the subprocess may cause the
subprocess to block, and even
deadlock."
On Mac OS X the buffer is 16k, and if you don't read it promptly as suggested, the process deadlocks. My only solution to this problem ATM, is a rather nasty busy wait.
https://github.com/algesten/googccwrap/blob/master/src/main/java/googccwrap/GoogleClosureCompilerWrapper.java