Вопрос

I am trying to pass data from a program MainProgram to another program TestProgram which I invoke using Reflection. For this, i re-route Standard.in to a ByteArrayOutputStream containing the data I want to transmit. TestProgram then reads from Standard.in using a BufferedReader and readLine(). I execute the whole thing several times in a for-loop. The problem is this: The first time, everything works fine, from the second time on, TestProgram reads null from System.in. Java Documentation states that the BufferedReader will return null, if the end of stream is reached. But there is definitely data in the stream and the pos of my ByteArrayOutputStream is also 0. Why could the BufferedReader think that suddenly System.in is empty or at the end of stream?

Here is the code that's manipulating the streams and invoking the method:

for(int i = 0; i < numberOfCases; i++) {

  Byte[] paramBytes = getCurrentParamBytes();

  InputStream inputStream = new BufferedInputStream(new ByteArrayInputStream(paramBytes));

  System.setIn(inputStream);
  String[] params = null;

  testProgram.invoke(null, (Object) params);
}  

Here is the code that's reading from System.in:

BufferedReader in = new BufferedReader(new InputStreamReader(System.in));

String current = in.readLine();

I have assured myself of the state of System.in using the debugging mode of Eclipse and everything is fine there. The fact that it only reads null starting with the second invocation of the TestProgram's main method is also very puzzling to me. Especially, because all values I have checked are exactly the same as they were the first time (except for the data in the stream, of course).

Sadly, I cannot change the use of the BufferedReader because TestProgram is not my class and has to be used as-is.

Any help would be greatly appreciated.

Это было полезно?

Решение

The problem is likely in the fact that BufferedReader created once, on first invocation. BufferedReader uses instance of InputStream that you provided on first iteration of a loop. On second, third, etc invocations it still uses this stream and doesn't read from updated System.in. I see 2 possible solutions if you can't modify source code of TestProgram.

  1. Create new instance of TestProgram on each iteration instead of reusing old. It might help to "refresh" inner BufferedReader if it is stored as class variable, not static.
  2. if you can't create new instance of TestProgram because it has state or BufferedReader stored in static variable you can try following: use special kind of InputStream that allows you to concatenate several InputStreams on fly. Check ConcatInputStream.

Example of second approach:

ConcatInputStream concat = new ConcatInputStream();
System.setIn(concat);

for(int i = 0; i < numberOfCases; i++) {

  Byte[] paramBytes = getCurrentParamBytes();

  InputStream inputStream = new BufferedInputStream(new ByteArrayInputStream(paramBytes));

  concat.addInputStream(inputStream);
  String[] params = null;

  testProgram.invoke(null, (Object) params);
}  

I'm not sure it will work because I don't know what is behaviour of BufferedReader if it sees end of stream on first invocation and then, on second invocation, stream suddenly not empty any more (because we've added new stream to concat). But hopefully it will work.

There are also third approach: you can decompile TestProgram using e.g. JD and find where instance of BufferedReader is stored. And then you can set it to null between iterations using reflections. But it's very error prone.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top