Reputation:
I've posted the same question here a few days ago(Java reading standard output from an external program using inputstream), and I found some excellent advices in dealing with a block in while reading ( while(is.read()) != -1)), but I still cannot resolve the problem.
After reading the answers to this similar question,
Java InputStream blocking read (esp, the answer posted by Guss),
I am beginning to believe that looping an input stream by using is.read() != -1 condition doesn't work if the program is interactive (that is it takes multiple inputs from user and present additional outputs upon subsequent inputs, and the program exits only when an explicit exit command is given). I admit that I don't know much about multi-threading, but I think what I need is a mechanism to promptly pause input stream threads(one each for stdout, stderr) when an user input is needed, and resume once the input is provided to prevent a block. The following is my current code which is experiencing a block on the line indicated:
EGMProcess egm = new EGMProcess(new String[]{directory + "/egm", "-o", "CasinoA", "-v", "VendorA", "-s", "localhost:8080/gls/MessageRobot.action ", "-E", "glss_env_cert.pem", "-S", "glss_sig_cert.pem", "-C", "glsc_sig_cert.pem", "-d", "config", "-L", "config/log.txt", "-H", "GLSA-SampleHost"}, new String[]{"PATH=${PATH}"}, directory);egm.execute(); BufferedReader stdout = new BufferedReader(new InputStreamReader(egm.getInputStream())); BufferedReader stderr = new BufferedReader(new InputStreamReader(egm.getErrorStream())); EGMStreamGobbler stdoutprocessor = new EGMStreamGobbler(stdout, egm); EGMStreamGobbler stderrprocessor = new EGMStreamGobbler(stderr, egm); BufferedWriter stdin = new BufferedWriter(new OutputStreamWriter(egm.getOutputStream())); stderrprocessor.run(); //<-- the block occurs here! stdoutprocessor.run(); //EGM/Agent test cases //check bootstrap menu if(!checkSimpleResult("******** EGM Bootstrap Menu **********", egm)) { String stdoutdump = egm.getStdOut(); egm.cleanup(); throw new Exception("can't find '******** EGM Bootstrap Menu **********'" + "in the stdout" + "\nStandard Output Dump:\n" + stdoutdump); } //select bootstrap stdin.write("1".toCharArray()); stdin.flush(); if(!checkSimpleResult("Enter port to receive msgs pushed from server ('0' for no push support)", egm)){ String stdoutdump = egm.getStdOut(); egm.cleanup(); throw new Exception("can't find 'Enter port to receive msgs pushed from server ('0' for no push support)'" + "in the stdout" + "\nStandard Output Dump:\n" + stdoutdump); }
...
public class EGMStreamGobbler implements Runnable{
private BufferedReader instream; private EGMProcess egm; public EGMStreamGobbler(BufferedReader isr, EGMProcess aEGM) { instream = isr; egm = aEGM; } public void run() { try{ int c; while((c = instream.read()) != 1) { egm.processStdOutStream((char)c); } } catch(IOException e) { e.printStackTrace(); } }
}
I apologize for the length of the code, but my questions are,
1) Is there any way to control the process of taking in inputstreams (stdout, stderr) without using read()? Or am I just implementing this badly?
2) Is multi-threading the right strategy for developing the process of taking in inputstreams and writing an output?
PS: if anyone can provide a similar problem with solution, it will help me a lot!
Upvotes: 4
Views: 2976
Reputation: 199244
Isn't this why the nio was created?
I don't know much about the Channels in nio, but this answer may be helpful. It shows how to read a file using nio. May be useful.
Upvotes: 1
Reputation: 55907
First, you need to read up on Thread and Runnable. You do not call Runnable.run() directly, you set up Threads to do that, and start the threads.
But more important, the presence of three independent threads implies the need for some careful design. Why 3 thread? The two you just started, and the main one.
I assume that the generall idea of your app is to wait for some output to arrive, interpret it and as a result send a command to the application you are controlling?
So your main thread needs to wait around for one of the reader threads to say "Aha! that's interesting, better ask the user what he wants to do."
In other words you need some communication mechanism between your readers and your writer. This might be implemented using Java's event mechanism. Yet more reading I'm afraid.
Upvotes: 1
Reputation: 1400
Upvotes: 2
Reputation: 9058
instead of
stderrprocessor.run(); //<-- the block occurs here!
stdoutprocessor.run();
You need to start threads:
Thread errThread = new Thread(stderrprocessor);
errThread.setDaemon( true );
errThread.start();
Thread outThread = new Thread(stdoutprocessor);
outThread.setDaemon( true );
outThread.start();
run()
is just a method specified in Runnable
. Thread.start()
calls run()
on the Runnable
in a new Thread
.
Upvotes: 8