Re: IOException thrown by process: avoidable?
On Jul 16, 4:57 pm, Ron <bnor...@gmail.com> wrote:
On Jul 2, 4:33 pm, "John B. Matthews" <nos...@nospam.invalid> wrote:
In article
<d9e0655e-0eb5-4a9e-90bd-c7291188c...@h2g2000yqg.googlegroups.com>,
Ron <bnor...@gmail.com> wrote:
On Jun 30, 8:56 pm, "John B. Matthews" <nos...@nospam.invalid> wrot=
e:
In article
<705241e2-649a-45df-b1e3-d8ad69f3e...@t13g2000yqt.googlegroups.com>=
,
Ron <bnor...@gmail.com> wrote:
[...]
I think maybe the process finishes, the stream gets closed, my
java program attempts to read the next line of output and gets
the IOException. I think the timing has to be just right thoug=
h,
because given the same command, I sometimes see the exception and
sometimes not. I also thought that maybe the process is not
outputing an EOF before exiting as a possible cause, but that
doesn't really explain why, for the same command, I sometimes get
the exception, and sometimes do not.
Looking closer at your initial example, I see that you are creating
a new ProcessBuilder and starting it to yield Process p. Then you
start a new Thread from which you read p's stdout and wait for p to
complete. Instead, shouldn't you do something more like this:
import java.io.BufferedReader;
import java.io.InputStreamReader;
public class PBTest {
public static void main(String[] args) {
ProcessBuilder pb = new ProcessBuilder("ls", ".")=
;
pb.redirectErrorStream(true);
try {
Process p = pb.start();
String s;
// read from the process's combined stdout =
& stderr
BufferedReader stdout = new BufferedReade=
r (
new InputStreamReader(p.getInputStr=
eam()));
while ((s = stdout.readLine()) != null)=
{
System.out.println(s);
}
System.out.println("Exit value: " + p.waitF=
or());
p.getInputStream().close();
p.getOutputStream().close();
p.getErrorStream().close();
} catch (Exception ex) {
ex.printStackTrace(System.out);
}
}
}
The way I did it is recommended in a number of places, including a
paper on Sun's website, and also Mr. Green's java glossary. Having
dedicated threads to service the standard out, standard error and
standard input lets you do stuff with all three simultaneously, and
you can treat stdout and stderr separately, if desired. The
waitFor() method also gives you the exit value of the process, if
that interests you.
But if you don't need any of that, I don't see why your example
program would not work. But comparing the two, I don't immediately
see a reason why your example and my example would behave differently
with regard to the problem I am seeing.
I was puzzled by the variability of the problem, which makes me wonder
about a threading issue. I see that ProcessBuilder is not synchronized.
In particular, "If multiple threads access a ProcessBuilder instance
concurrently, and at least one of the threads modifies one of the
attributes structurally, it must be synchronized externally." Roedy's
example doesn't appear to modify any process attributes, but your code
might do so.
<http://java.sun.com/j2se/1.5.0/docs/api/java/lang/ProcessBuilder.html>
<http://mindprod.com/jgloss/exec.html>
However, I will try it and see whether I actually do get the same
issue with your example.
I look forward to your results.
--
John B. Matthews
trashgod at gmail dot com
<http://sites.google.com/site/drjohnbmatthews>- Hide quoted text -
- Show quoted text -
Thanks John Matthews for helping me with this issue.
Sorry for the delay. Today I found some time to do more analysis. I=
n
a nutshell, my program generates exceptions, but the one you presented
never does. In fact, I can get my program to generate exceptions 100%
of the time by adding a sleep statement in the while loop in the
thread that is reading the output.
In the multi-threaded example (which may be a necessity, as discussed
earlier), the main thread does a waitFor() on the external process. I
originally thought that maybe the output streams were getting closed
somehow by the external process, but that isn't the case. Once the
waitFor() completes, the Process goes out of scope, which causes its
streams to get closed.
I have pasted Roedy Green's example below for convenience. There is a
comment that starts "The Receiver thread will continue...". Although
the Receiver thread continues, the streams is and os get closed when p
goes out of scope, so as the thread keeps reading it gets the
IOException. If some code is added there to wait for the Receiver
thread to finish (not always desirable I understand, but useful for
testing), then the exception is avoided.
public static void main( String[] args ) throws IOException
{
// Boomerang exchoes input to output.
final ProcessBuilder pb = new ProcessBuilder( "E:/sys/
boomerang.exe" );
// merge child's error and normal output streams.
// Note it is not called setRedirectErrorStream.
pb.redirectErrorStream( true );
final Process p = pb.start();
OutputStream os = p.getOutputStream();
InputStream is = p.getInputStream();
// spawn two threads to handle I/O with child while we wa=
it
for it to complete.
new Thread( new Receiver( is ) ).start();
new Thread( new Sender( os ) ).start();
try
{
p.waitFor();
}
catch ( InterruptedException e )
{
Thread.currentThread().interrupt();
}
out.println( "Child done" );
// at this point the child is complete. All of its out=
put may
or may not have been processed however.
// The Receiver thread will continue until it has finishe=
d
processing it.
// You must close the streams even if you never use them!=
In
this case the threads close is and os.
p.getErrorStream().close();
}
}- Hide quoted text -
- Show quoted text -
But I have more work to do. I just tried Roedy Green's example
verbatim, and it works fine, even if a large delay is added to the
Receiver thread.
I thought maybe it was because I didn't create a reference to Process
p's stream in my thread, but I made that change and tried it, and no
improvement. I'll post again when I figure out what I did wrong.