Problems running an interactive Process: the process's output isn't appeari

I'm trying to run an interactive Linux process from Java. That is, a program which prompts the user for input as it runs. I want to catch the output and feed in appropriate responses. See the sample code below.

When I try to run this, it hangs. The process sits there without terminating or emitting any output. I've run the program in question from the command line, and it emits several lines of output before prompting for input.

I have no problems running non-interactive processes and getting their output.

Thank you for any help! I have trawled the web about this. I found other people who've had the problem, but no solutions.

- Daniel

Example code:

// Utility class to empty the output streams using read()

class StreamGobblerextends Thread{

privatefinal InputStream is;

private StringBuffer stringBuffer;

privateboolean stopFlag;

public StreamGobbler(InputStream is){

super("gobbler:"+is.toString());

this.is = is;

stringBuffer =new StringBuffer();

}

publicvoid run(){

try{

InputStreamReader isr =new InputStreamReader(is);

BufferedReader br =new BufferedReader(isr);

while (!stopFlag){

int ich = br.read();

if (ich==-1)break;// End of stream

char ch = (char) ich;

stringBuffer.append(ch);

}

}catch (IOException ioe){

ioe.printStackTrace();

}

}

public String getString(){

return stringBuffer.toString();

}

}

// Main code

publicstaticvoid main(String[] args){

// Make a process

Process process =new ProcessBuilder(MYCOMMAND).start();

// any output?

StreamGobbler outputGobbler =new StreamGobbler(process.getInputStream());

// any error messages?

StreamGobbler errorGobbler =new StreamGobbler(process.getErrorStream());

// Start the gobblers running in their own threads

outputGobbler.start();

errorGobbler.start();

// An input channel

Writer in =new OutputStreamWriter(process.getOutputStream());

// Wait for some output...

while(true){

Methods.sleep(500);// Give the process lots of time to work

String out = outputGobbler.getString();// Get the output so far

if (out !=""){// But no output is ever received - this test always fails :(

if (testfor exit)break;

in.write("my response");

in.flush();

}

}

// End main code

}

[4739 byte] By [Daniel.Wintersteina] at [2007-11-27 9:07:19]
# 1
Does the process flush its buffers?
Bavona at 2007-7-12 21:43:54 > top of Java-index,Core,Core APIs...
# 2
I don't know. It's a 3rd party program designed to be used via the command line.
Daniel.Wintersteina at 2007-7-12 21:43:54 > top of Java-index,Core,Core APIs...
# 3

Instead of out!=""

you probably mean !out.equals("")

Also, if your program has to wait for input, then just let it use a blocking call, or have your input thread provide a way to signal when input is available.

As for the buffers: you could find out if your program works by testing with an child process of your own. Although it is probably unlikely that that would be a problem.

Can you read the first lines using BufferedReader.readLine()?

Bavona at 2007-7-12 21:43:54 > top of Java-index,Core,Core APIs...
# 4
C or C++ programs that use stdio or streams to do output to stdout automatically buffer the output if the stdout isn't a terminal. This is built into the stdio and streams libraries, nothing you can do about it.Why is this a problem?
ejpa at 2007-7-12 21:43:54 > top of Java-index,Core,Core APIs...
# 5

Bavon,

Thanks for taking the time to look at this.

> Instead of out!="" you probably mean !out.equals("")

You're right, but it makes no difference here. The buffers never move from ""

> Also, if your program has to wait for input, then just let it use a blocking call, or have your

> input thread provide a way to signal when input is available.

That might be nicer, but doesn't solve the basic problem of no input being available..

> As for the buffers: you could find out if your program works by testing with an child

> process of your own. Although it is probably unlikely that that would be a problem.

I'll investigate...

> Can you read the first lines using BufferedReader.readLine()?

BufferedReader.readLine() hangs. I.e. it never gets any input. So, same problem in a different form.

Daniel.Wintersteina at 2007-7-12 21:43:54 > top of Java-index,Core,Core APIs...
# 6

EJP,

Thanks for replying.

> C or C++ programs that use stdio or streams to do

> output to stdout automatically buffer the output if

> the stdout isn't a terminal.

I don't understand. Do you mean the output I'm after is sitting in a buffer inside the C program?

> This is built into the stdio and streams libraries, nothing you can do about

> it.

> Why is this a problem?

That sounds bad. This is a problem because I'd like to run the external program from within Java. And so I need a way of getting at it's output whilst it's running. If the terminal is treated as a special case, is there a way of tricking linux into treating the Java Process stream in the same way?

Thanks again for your time.

- Daniel

Daniel.Wintersteina at 2007-7-12 21:43:54 > top of Java-index,Core,Core APIs...
# 7

> I don't understand. Do you mean the output I'm after

> is sitting in a buffer inside the C program?

Yes.

> > This is built into the stdio and streams libraries,

> nothing you can do about

> > it.

I am wrong about that. If you are in control of the C/C++ program you can call fflush() (C) or whatever the streams equivalent is, sorry I forget but anyway this is a Java forum ;-)

> > Why is this a problem?

>

> That sounds bad. This is a problem because I'd like

> to run the external program from within Java. And so

> I need a way of getting at it's output whilst it's

> running.

You will get it every 4096 bytes or whatever the internal buffer size is. Is that a problem?

ejpa at 2007-7-12 21:43:54 > top of Java-index,Core,Core APIs...
# 8

>

> > This is built into the stdio and streams

> libraries,

> nothing you can do about

> > it.

>

> I am wrong about that. If you are in control of the

> C/C++ program you can call fflush() (C) or whatever

> the streams equivalent is, sorry I forget but anyway

> this is a Java forum ;-)

>

I believe it is also possible to disable buffering after a process is fork'ed, and before the new binary is loaded. You would have to ask about that in a C/linux forum. Anyway, you would have to write your own function to execute processes using JNI.

Bavona at 2007-7-12 21:43:54 > top of Java-index,Core,Core APIs...
# 9

> I believe it is also possible to disable buffering

> after a process is fork'ed, and before the new binary

> is loaded.

I'd be surprised. The buffering is implemented at the C library level, not the system call level, and library settings won't survive the exec().

ejpa at 2007-7-12 21:43:54 > top of Java-index,Core,Core APIs...
# 10
EJP, Bavon,Thank you very much for your help in getting to the bottom of this. I really appreciate it. For my case, I can get a copy of the C source code, so I should be able to make things work by recompiling with extra fflush() commands.Regards,Daniel
Daniel.Wintersteina at 2007-7-12 21:43:54 > top of Java-index,Core,Core APIs...