PipedReader never ready() at end of stream - java

I'm trying to write code that reads from a Reader in a non-blocking way, i.e. only calling read() after ensuring ready() returns true.
To test the code, I use the PipedReader/PipedWriter combo, and ready() works great, until I close the PipedWriter, which also marks the end of the stream on the side of PipedReader. However, although its ready() method will never again return true, read() instantly returns -1 when called.
In this case, is there a non-blocking way to figure out that a call to read() would not block?
Here's an example that reproduces the issue.
package test;
import java.io.*;
public class Piper
{
public static void main(String[] args) throws Exception
{
int sleepBetweenChars = 100;
boolean throttle = true;
Reader reader = new StringReader("abc");
if (throttle)
reader = throttle(sleepBetweenChars, reader);
while (true)
{
if (reader.ready())
{
int c = reader.read();
System.out.println("Read " + c);
if (c == -1)
break;
}
else
{
System.out.println("Not ready");
Thread.sleep(sleepBetweenChars);
if (!reader.ready())
{
System.out.println("Still not ready, sleep some more");
Thread.sleep(20 * sleepBetweenChars);
if (!reader.ready())
{
int c = reader.read();
System.out.println("Still not ready, we read nonetheless: " + c);
if (c == -1)
break;
}
}
}
}
}
private static Reader throttle(final int sleepBetweenChars, final Reader in) throws IOException
{
final PipedWriter pw = new PipedWriter();
PipedReader pr = new PipedReader(pw);
(new Thread(new Runnable()
{
public void run()
{
System.out.println("Start piping");
int c;
try
{
while ((c = in.read()) != -1)
{
pw.write(c);
Thread.sleep(sleepBetweenChars);
}
pw.close();
System.out.println("Closed PipedWriter");
}
catch (Throwable t)
{
t.printStackTrace();
}
}
})).start();
return pr;
}
}

PipedReader never ready() at end of stream
I agree. That's not what it's for. ready() tells you whether there is data available to be read, not end-of-file conditions. See the Javadoc: ' A piped character stream is ready if the circular buffer is not empty'.
There isn't a way to do what you're trying to do. Streams and Readers are irredemiably blocking. Your code is basically just an elaborate way of implementing blocking yourself, with added latency and added bugs. It's pointless.
I would also question your use of piped readers and writers at all. They are singularly useless things, really only a proof-of-concept for wait() and notify(). Possibly what you're really looking for is a Queue.

Related

unable to find while loops behavior in java

In my application, I have used two while loops for reading the sockets. while loop-1 takes 1% of CPU and while loop-2 takes 100% CPU. I am confused. Please share your valuable thoughts. For more information, I have added two while loops below:
While loop-1:
while(true) {
String message = onReadFinal(in);
if (message != null) {
synchronized (message) {
System.out.println("Message size:" + message);
}
}
}
private String onReadFinal(DataInputStream in) {
String mess = null;
try{
StringBuilder builder = new StringBuilder();
do {
builder.append(in.readInt());
} while(in.available() > 0);
mess = builder.toString();
} catch(IOException e){ e.printStackTrace(); }
return mess;
}
While loop-2:
while(true) {
String message = onRead(in);
if (message != null) {
synchronized (message) {
System.out.println("Message size:" + message);
}
}
}
public String onRead(DataInputStream input) {
String socketmessage = null;
int length;
try {
if(input != null) {
length = input.available();
if(length>0) {
byte[] message = new byte[length];
input.readFully(message, 0, message.length);
socketmessage = new String(message);
}
}
} catch (IOException ioException) {
ioException.printStackTrace();
}
return socketmessage;
}
Also, if I execute infinite while loop with no statements, it takes 100% of CPU. Someone can you please explain.
if a loop does not contain blocking operations, it always would take 100% CPU.
Your first loop contains blocking operation in.readInt(). Your second loop also contains potentially blocking operation input.readFully(), but calls it only when the stream buffer has enough bytes to read, and so in fact this operation does not block. When there is not enough bytes, the loop immediately repeats non-blocking operation input.available(), without much sense.
You better tell us what you want.

RXTX serial connection - issue with blocking read()

I am trying to use the RXTX library for blocking serial communication on Windows (XP and 7). I have tested the connection with Hyperterminal in both ends, and it works flawlessly.
I set up the connection with the following code: (exception handling and defensive checks omitted for clarity)
private InputStream inStream;
private OutputStream outStream;
private BufferedReader inReader;
private PrintWriter outWriter;
private SerialPort serialPort;
private final String serialPortName;
public StreamComSerial(String serialPortName) {
this.serialPortName = serialPortName;
CommPortIdentifier portIdentifier;
portIdentifier = CommPortIdentifier.getPortIdentifier(serialPortName);
CommPort commPort = null;
commPort = portIdentifier.open(this.getClass().getName(),500);
serialPort = (SerialPort) commPort; serialPort.setSerialPortParams(4800,SerialPort.DATABITS_8,SerialPort.STOPBITS_1,SerialPort.PARITY_NONE);
inStream = serialPort.getInputStream();
outStream = serialPort.getOutputStream();
inReader = new BufferedReader(new InputStreamReader(inStream, Settings.getCharset()));
outWriter = new PrintWriter(new OutputStreamWriter(outStream, Settings.getCharset()));
When I use
outWriter.println("test message");
flush();
the message is recieved fine on the other end, but calling
inReader.readLine()
imidiately returns "java.io.IOException: Underlying input stream returned zero bytes".
I then decided to try and implement my own blocking read logic and wrote this:
public String readLine() throws IOException {
String line = new String();
byte[] nextByte = {-1};
while (true) {
nextByte[0] = (byte)inStream.read();
logger.debug("int read: " + nextByte[0]);
if (nextByte[0] == (byte)-1) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
continue;
}
logger.debug("byte read: " + nextByte[0]);
line = line + new String(nextByte);
if (nextByte[0] == (byte)13) { // 13 is carriage return in ASCII
return line;
}
}
}
But this code goes in an infinite loop and "nextByte[0] = (byte)inStream.read();" assigns -1 no matter what is sent over the serial connection. In addition, the other end stutters quite badly and only lets me send a character every 1-3 sec. and hangs for a long time if I try to send many characters in a short burst.
Any help very appreciated.
*edit - using inStream.read(nextByte) instead of "nextByte[0] = (byte)inStream.read();" does not write to the nextByte variable, no matter what I send to it through the serial connection.
*edit2 - as my code works flawlessly with the SUN javax.comm lib and a win32com.dll I got from a friend, I have ceased trying to make it work with RXTX. I am not interested in unblocking communication, which seems to be the only way other people can make RXTX work.
Use RXTX-2.2pre2, previous versions have had a bug which prevented blocking I/O from working correctly.
And do not forget to set port to blocking mode:
serialPort.disableReceiveTimeout();
serialPort.enableReceiveThreshold(1);
I think the code you wrote in your own readLine implementation is buggy. nextByte[0] is never restored to -1 after the first character is read.
You should try to use the value returned by inStream.read(nextByte) to state the number of bytes read from the stream instead of the value of your byte array.
Anyway I think you should go for an event based method of reading the inputs with a SerialPortEventListener:
serialPort.addEventListener(new SerialPortEventListener() {
public void serialEvent(SerialPortEvent evt) {
switch (evt.getEventType()) {
case SerialPortEvent.DATA_AVAILABLE:
dataReceived();
break;
default:
break;
}
}
});
serialPort.notifyOnDataAvailable(true);
it may not be blocking but when the stream is empty, just catch the IOE and keep reading from it. This is what I do with RXTX-2.1-7 and it works fine, I use it to read and write to an arduino:
public static class SerialReader implements Runnable {
InputStream in;
public SerialReader(InputStream in) {
this.in = in;
}
public void run() {
Boolean keepRunning = true;
BufferedReader br = new BufferedReader(new InputStreamReader(in));
String line;
while (keepRunning) {
try {
while ((line = br.readLine()) != null) {
//DO YOUR STUFF HERE
}
} catch (IOException e) {
try {
//ignore it, the stream is temporarily empty,RXTX's just whining
Thread.sleep(200);
} catch (InterruptedException ex) {
// something interrupted our sleep, exit ...
keepRunning = false;
}
}
}
}
}
I have solved this way
try
{
if(input.ready()==true)
{
String inputLine=input.readLine();
System.out.println(inputLine);
}
} catch (Exception e)

How to make a java program to print both out.println() and err.println() statements?

I have written the java code below, which executes another java program named "Newsworthy_RB".
Newsworthy_RB.java contains both the System.out.printlln() and System.err.println() statements.
I want both the outputs to be printed in the command prompt console.
What has to be done inorder to obtain the same.
The below program just prints the out.println() statements and not the err.println() statements.
Kindly let me know whether the code below will function as i expect?
command = "java -cp .:../sqljdbc.jar SetHash Newsworthy_RB";
Process child1 = Runtime.getRuntime().exec(command);
InputStream in1 = child1.getErrorStream();
InputStream in2 = child2.getInputStream();
while ((c = in1.read()) != -1 || (c = in2.read()) != -1) {
System.out.print((char)c);
}
First of all, the preferred way of starting external programs is through ProcessBuilder. It is even mentioned in the docs for Runtime:
ProcessBuilder.start() is now the preferred way to start a process with a modified environment.
In ProcessBuilder you have a very convenient method called redirectErrorStream:
Sets this process builder's redirectErrorStream property.
If this property is true, then any error output generated by subprocesses subsequently started by this object's start() method will be merged with the standard output, so that both can be read using the Process.getInputStream() method. This makes it easier to correlate error messages with the corresponding output. The initial value is false.
A complete example of how to output both standard error and standard out:
import java.io.*;
public class Test {
public static void main(String... args) throws IOException {
ProcessBuilder pb =
new ProcessBuilder("java", "-cp", "yourClassPath", "HelloWorld");
pb.redirectErrorStream(true);
Process proc = pb.start();
Reader reader = new InputStreamReader(proc.getInputStream());
int ch;
while ((ch = reader.read()) != -1)
System.out.print((char) ch);
reader.close();
}
}
Response to your update: No, the code with
while ((c = in1.read()) != -1 || (c = in2.read()) != -1)
will not work, since read() is a blocking method and you only have one thread. Your only option is to use one thread per input-stream, or, (preferrably) merge the two input-streams into one, using ProcessBuilder.redirectErrorStream.
You need to pipe the output of both streams in a separate threads. Example code from here:
Process p = Runtime.getRuntime().exec(cmd.array());
copyInThread(p.getInputStream(), System.out);
copyInThread(p.getErrorStream(), System.err);
p.waitFor();
return p.exitValue();
private void copyInThread(final InputStream in, final OutputStream out) {
new Thread() {
public void run() {
try {
while (true) {
int x = in.read();
if (x < 0) {
return;
}
if (out != null) {
out.write(x);
}
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
} .start();
}
There are two methods in the Process object: getErrorStream and getInputStream. Currently, your program is only listening to one. You'll want it to listen to both.

Capturing large amounts of output from Apache Commons-Exec

I am writing a video application in Java by executing ffmpeg and capturing its output to standard output. I decided to use Apache Commons-Exec instead of Java's Runtime, because it seems better. However, I am have a difficult time capturing all of the output.
I thought using pipes would be the way to go, because it is a standard way of inter-process communication. However, my setup using PipedInputStream and PipedOutputStream is wrong. It seems to work, but only for the first 1042 bytes of the stream, which curiously happens to be the value of PipedInputStream.PIPE_SIZE.
I have no love affair with using pipes, but I want to avoid use disk I/O (if possible), because of speed and volume of data (a 1m 20s video at 512x384 resolution produces 690M of piped data).
Thoughts on the best solution to handle large amounts of data coming from a pipe? My code for my two classes are below. (yes, sleep is bad. Thoughts on that? wait() and notifyAll() ?)
WriteFrames.java
public class WriteFrames {
public static void main(String[] args) {
String commandName = "ffmpeg";
CommandLine commandLine = new CommandLine(commandName);
File filename = new File(args[0]);
String[] options = new String[] {
"-i",
filename.getAbsolutePath(),
"-an",
"-f",
"yuv4mpegpipe",
"-"};
for (String s : options) {
commandLine.addArgument(s);
}
PipedOutputStream output = new PipedOutputStream();
PumpStreamHandler streamHandler = new PumpStreamHandler(output, System.err);
DefaultExecutor executor = new DefaultExecutor();
try {
DataInputStream is = new DataInputStream(new PipedInputStream(output));
YUV4MPEGPipeParser p = new YUV4MPEGPipeParser(is);
p.start();
executor.setStreamHandler(streamHandler);
executor.execute(commandLine);
} catch (IOException e) {
e.printStackTrace();
}
}
}
YUV4MPEGPipeParser.java
public class YUV4MPEGPipeParser extends Thread {
private InputStream is;
int width, height;
public YUV4MPEGPipeParser(InputStream is) {
this.is = is;
}
public void run() {
try {
while (is.available() == 0) {
Thread.sleep(100);
}
while (is.available() != 0) {
// do stuff.... like write out YUV frames
}
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
The problem is in the run method of YUV4MPEGPipeParser class. There are two successive loops. The second loop terminates immediately if there are no data currently available on the stream (e.g. all input so far was processed by parser, and ffmpeg or stream pump were not fast enough to serve some new data for it -> available() == 0 -> loop is terminated -> pump thread finishes).
Just get rid of these two loops and sleep and just perform a simple blocking read() instead of checking if any data are available for processing. There is also probably no need for wait()/notify() or even sleep() because the parser code is started on a separate thread.
You can rewrite the code of run() method like this:
public class YUV4MPEGPipeParser extends Thread {
...
// optimal size of buffer for reading from pipe stream :-)
private static final int BUFSIZE = PipedInputStream.PIPE_SIZE;
public void run() {
try {
byte buffer[] = new byte[BUFSIZE];
int len = 0;
while ((len = is.read(buffer, 0, BUFSIZE) != -1) {
// we have valid data available
// in first 'len' bytes of 'buffer' array.
// do stuff.... like write out YUV frames
}
} catch ...
}
}

Is it possible to read from a InputStream with a timeout?

Specifically, the problem is to write a method like this:
int maybeRead(InputStream in, long timeout)
where the return value is the same as in.read() if data is available within 'timeout' milliseconds, and -2 otherwise. Before the method returns, any spawned threads must exit.
To avoid arguments, the subject here java.io.InputStream, as documented by Sun (any Java version). Please note this is not as simple as it looks. Below are some facts which are supported directly by Sun's documentation.
The in.read() method may be non-interruptible.
Wrapping the InputStream in a Reader or InterruptibleChannel doesn't help, because all those classes can do is call methods of the InputStream. If it were possible to use those classes, it would be possible to write a solution that just executes the same logic directly on the InputStream.
It is always acceptable for in.available() to return 0.
The in.close() method may block or do nothing.
There is no general way to kill another thread.
Using inputStream.available()
It is always acceptable for System.in.available() to return 0.
I've found the opposite - it always returns the best value for the number of bytes available. Javadoc for InputStream.available():
Returns an estimate of the number of bytes that can be read (or skipped over)
from this input stream without blocking by the next invocation of a method for
this input stream.
An estimate is unavoidable due to timing/staleness. The figure can be a one-off underestimate because new data are constantly arriving. However it always "catches up" on the next call - it should account for all arrived data, bar that arriving just at the moment of the new call. Permanently returning 0 when there are data fails the condition above.
First Caveat: Concrete subclasses of InputStream are responsible for available()
InputStream is an abstract class. It has no data source. It's meaningless for it to have available data. Hence, javadoc for available() also states:
The available method for class InputStream always returns 0.
This method should be overridden by subclasses.
And indeed, the concrete input stream classes do override available(), providing meaningful values, not constant 0s.
Second Caveat: Ensure you use carriage-return when typing input in Windows.
If using System.in, your program only receives input when your command shell hands it over. If you're using file redirection/pipes (e.g. somefile > java myJavaApp or somecommand | java myJavaApp ), then input data are usually handed over immediately. However, if you manually type input, then data handover can be delayed. E.g. With windows cmd.exe shell, the data are buffered within cmd.exe shell. Data are only passed to the executing java program following carriage-return (control-m or <enter>). That's a limitation of the execution environment. Of course, InputStream.available() will return 0 for as long as the shell buffers the data - that's correct behaviour; there are no available data at that point. As soon as the data are available from the shell, the method returns a value > 0. NB: Cygwin uses cmd.exe too.
Simplest solution (no blocking, so no timeout required)
Just use this:
byte[] inputData = new byte[1024];
int result = is.read(inputData, 0, is.available());
// result will indicate number of bytes read; -1 for EOF with no data read.
OR equivalently,
BufferedReader br = new BufferedReader(new InputStreamReader(System.in, Charset.forName("ISO-8859-1")),1024);
// ...
// inside some iteration / processing logic:
if (br.ready()) {
int readCount = br.read(inputData, bufferOffset, inputData.length-bufferOffset);
}
Richer Solution (maximally fills buffer within timeout period)
Declare this:
public static int readInputStreamWithTimeout(InputStream is, byte[] b, int timeoutMillis)
throws IOException {
int bufferOffset = 0;
long maxTimeMillis = System.currentTimeMillis() + timeoutMillis;
while (System.currentTimeMillis() < maxTimeMillis && bufferOffset < b.length) {
int readLength = java.lang.Math.min(is.available(),b.length-bufferOffset);
// can alternatively use bufferedReader, guarded by isReady():
int readResult = is.read(b, bufferOffset, readLength);
if (readResult == -1) break;
bufferOffset += readResult;
}
return bufferOffset;
}
Then use this:
byte[] inputData = new byte[1024];
int readCount = readInputStreamWithTimeout(System.in, inputData, 6000); // 6 second timeout
// readCount will indicate number of bytes read; -1 for EOF with no data read.
Assuming your stream is not backed by a socket (so you can't use Socket.setSoTimeout()), I think the standard way of solving this type of problem is to use a Future.
Suppose I have the following executor and streams:
ExecutorService executor = Executors.newFixedThreadPool(2);
final PipedOutputStream outputStream = new PipedOutputStream();
final PipedInputStream inputStream = new PipedInputStream(outputStream);
I have writer that writes some data then waits for 5 seconds before writing the last piece of data and closing the stream:
Runnable writeTask = new Runnable() {
#Override
public void run() {
try {
outputStream.write(1);
outputStream.write(2);
Thread.sleep(5000);
outputStream.write(3);
outputStream.close();
} catch (Exception e) {
e.printStackTrace();
}
}
};
executor.submit(writeTask);
The normal way of reading this is as follows. The read will block indefinitely for data and so this completes in 5s:
long start = currentTimeMillis();
int readByte = 1;
// Read data without timeout
while (readByte >= 0) {
readByte = inputStream.read();
if (readByte >= 0)
System.out.println("Read: " + readByte);
}
System.out.println("Complete in " + (currentTimeMillis() - start) + "ms");
which outputs:
Read: 1
Read: 2
Read: 3
Complete in 5001ms
If there was a more fundamental problem, like the writer not responding, the reader would block for ever.
If I wrap the read in a future, I can then control the timeout as follows:
int readByte = 1;
// Read data with timeout
Callable<Integer> readTask = new Callable<Integer>() {
#Override
public Integer call() throws Exception {
return inputStream.read();
}
};
while (readByte >= 0) {
Future<Integer> future = executor.submit(readTask);
readByte = future.get(1000, TimeUnit.MILLISECONDS);
if (readByte >= 0)
System.out.println("Read: " + readByte);
}
which outputs:
Read: 1
Read: 2
Exception in thread "main" java.util.concurrent.TimeoutException
at java.util.concurrent.FutureTask$Sync.innerGet(FutureTask.java:228)
at java.util.concurrent.FutureTask.get(FutureTask.java:91)
at test.InputStreamWithTimeoutTest.main(InputStreamWithTimeoutTest.java:74)
I can catch the TimeoutException and do whatever cleanup I want.
If your InputStream is backed by a Socket, you can set a Socket timeout (in milliseconds) using setSoTimeout. If the read() call doesn't unblock within the timeout specified, it will throw a SocketTimeoutException.
Just make sure that you call setSoTimeout on the Socket before making the read() call.
I would question the problem statement rather than just accept it blindly. You only need timeouts from the console or over the network. If the latter you have Socket.setSoTimeout() and HttpURLConnection.setReadTimeout() which both do exactly what is required, as long as you set them up correctly when you construct/acquire them. Leaving it to an arbitrary point later in the application when all you have is the InputStream is poor design leading to a very awkward implementation.
I have not used the classes from the Java NIO package, but it seems they might be of some help here. Specifically, java.nio.channels.Channels and java.nio.channels.InterruptibleChannel.
Here is a way to get a NIO FileChannel from System.in and check for availability of data using a timeout, which is a special case of the problem described in the question. Run it at the console, don't type any input, and wait for the results. It was tested successfully under Java 6 on Windows and Linux.
import java.io.FileInputStream;
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.nio.ByteBuffer;
import java.nio.channels.ClosedByInterruptException;
public class Main {
static final ByteBuffer buf = ByteBuffer.allocate(4096);
public static void main(String[] args) {
long timeout = 1000 * 5;
try {
InputStream in = extract(System.in);
if (! (in instanceof FileInputStream))
throw new RuntimeException(
"Could not extract a FileInputStream from STDIN.");
try {
int ret = maybeAvailable((FileInputStream)in, timeout);
System.out.println(
Integer.toString(ret) + " bytes were read.");
} finally {
in.close();
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/* unravels all layers of FilterInputStream wrappers to get to the
* core InputStream
*/
public static InputStream extract(InputStream in)
throws NoSuchFieldException, IllegalAccessException {
Field f = FilterInputStream.class.getDeclaredField("in");
f.setAccessible(true);
while( in instanceof FilterInputStream )
in = (InputStream)f.get((FilterInputStream)in);
return in;
}
/* Returns the number of bytes which could be read from the stream,
* timing out after the specified number of milliseconds.
* Returns 0 on timeout (because no bytes could be read)
* and -1 for end of stream.
*/
public static int maybeAvailable(final FileInputStream in, long timeout)
throws IOException, InterruptedException {
final int[] dataReady = {0};
final IOException[] maybeException = {null};
final Thread reader = new Thread() {
public void run() {
try {
dataReady[0] = in.getChannel().read(buf);
} catch (ClosedByInterruptException e) {
System.err.println("Reader interrupted.");
} catch (IOException e) {
maybeException[0] = e;
}
}
};
Thread interruptor = new Thread() {
public void run() {
reader.interrupt();
}
};
reader.start();
for(;;) {
reader.join(timeout);
if (!reader.isAlive())
break;
interruptor.start();
interruptor.join(1000);
reader.join(1000);
if (!reader.isAlive())
break;
System.err.println("We're hung");
System.exit(1);
}
if ( maybeException[0] != null )
throw maybeException[0];
return dataReady[0];
}
}
Interestingly, when running the program inside NetBeans 6.5 rather than at the console, the timeout doesn't work at all, and the call to System.exit() is actually necessary to kill the zombie threads. What happens is that the interruptor thread blocks (!) on the call to reader.interrupt(). Another test program (not shown here) additionally tries to close the channel, but that doesn't work either.
As jt said, NIO is the best (and correct) solution. If you really are stuck with an InputStream though, you could either
Spawn a thread who's exclusive job is to read from the InputStream and put the result into a buffer which can be read from your original thread without blocking. This should work well if you only ever have one instance of the stream. Otherwise you may be able to kill the thread using the deprecated methods in the Thread class, though this may cause resource leaks.
Rely on isAvailable to indicate data that can be read without blocking. However in some cases (such as with Sockets) it can take a potentially blocking read for isAvailable to report something other than 0.
Inspired in this answer I came up with a bit more object-oriented solution.
This is only valid if you're intending to read characters
You can override BufferedReader and implement something like this:
public class SafeBufferedReader extends BufferedReader{
private long millisTimeout;
( . . . )
#Override
public int read(char[] cbuf, int off, int len) throws IOException {
try {
waitReady();
} catch(IllegalThreadStateException e) {
return 0;
}
return super.read(cbuf, off, len);
}
protected void waitReady() throws IllegalThreadStateException, IOException {
if(ready()) return;
long timeout = System.currentTimeMillis() + millisTimeout;
while(System.currentTimeMillis() < timeout) {
if(ready()) return;
try {
Thread.sleep(100);
} catch (InterruptedException e) {
break; // Should restore flag
}
}
if(ready()) return; // Just in case.
throw new IllegalThreadStateException("Read timed out");
}
}
Here's an almost complete example.
I'm returning 0 on some methods, you should change it to -2 to meet your needs, but I think that 0 is more suitable with BufferedReader contract. Nothing wrong happened, it just read 0 chars. readLine method is a horrible performance killer. You should create a entirely new BufferedReader if you actually want to use readLine. Right now, it is not thread safe. If someone invokes an operation while readLines is waiting for a line, it will produce unexpected results
I don't like returning -2 where I am. I'd throw an exception because some people may just be checking if int < 0 to consider EOS. Anyway, those methods claim that "can't block", you should check if that statement is actually true and just don't override'em.
import java.io.BufferedReader;
import java.io.IOException;
import java.io.Reader;
import java.nio.CharBuffer;
import java.util.concurrent.TimeUnit;
import java.util.stream.Stream;
/**
*
* readLine
*
* #author Dario
*
*/
public class SafeBufferedReader extends BufferedReader{
private long millisTimeout;
private long millisInterval = 100;
private int lookAheadLine;
public SafeBufferedReader(Reader in, int sz, long millisTimeout) {
super(in, sz);
this.millisTimeout = millisTimeout;
}
public SafeBufferedReader(Reader in, long millisTimeout) {
super(in);
this.millisTimeout = millisTimeout;
}
/**
* This is probably going to kill readLine performance. You should study BufferedReader and completly override the method.
*
* It should mark the position, then perform its normal operation in a nonblocking way, and if it reaches the timeout then reset position and throw IllegalThreadStateException
*
*/
#Override
public String readLine() throws IOException {
try {
waitReadyLine();
} catch(IllegalThreadStateException e) {
//return null; //Null usually means EOS here, so we can't.
throw e;
}
return super.readLine();
}
#Override
public int read() throws IOException {
try {
waitReady();
} catch(IllegalThreadStateException e) {
return -2; // I'd throw a runtime here, as some people may just be checking if int < 0 to consider EOS
}
return super.read();
}
#Override
public int read(char[] cbuf) throws IOException {
try {
waitReady();
} catch(IllegalThreadStateException e) {
return -2; // I'd throw a runtime here, as some people may just be checking if int < 0 to consider EOS
}
return super.read(cbuf);
}
#Override
public int read(char[] cbuf, int off, int len) throws IOException {
try {
waitReady();
} catch(IllegalThreadStateException e) {
return 0;
}
return super.read(cbuf, off, len);
}
#Override
public int read(CharBuffer target) throws IOException {
try {
waitReady();
} catch(IllegalThreadStateException e) {
return 0;
}
return super.read(target);
}
#Override
public void mark(int readAheadLimit) throws IOException {
super.mark(readAheadLimit);
}
#Override
public Stream<String> lines() {
return super.lines();
}
#Override
public void reset() throws IOException {
super.reset();
}
#Override
public long skip(long n) throws IOException {
return super.skip(n);
}
public long getMillisTimeout() {
return millisTimeout;
}
public void setMillisTimeout(long millisTimeout) {
this.millisTimeout = millisTimeout;
}
public void setTimeout(long timeout, TimeUnit unit) {
this.millisTimeout = TimeUnit.MILLISECONDS.convert(timeout, unit);
}
public long getMillisInterval() {
return millisInterval;
}
public void setMillisInterval(long millisInterval) {
this.millisInterval = millisInterval;
}
public void setInterval(long time, TimeUnit unit) {
this.millisInterval = TimeUnit.MILLISECONDS.convert(time, unit);
}
/**
* This is actually forcing us to read the buffer twice in order to determine a line is actually ready.
*
* #throws IllegalThreadStateException
* #throws IOException
*/
protected void waitReadyLine() throws IllegalThreadStateException, IOException {
long timeout = System.currentTimeMillis() + millisTimeout;
waitReady();
super.mark(lookAheadLine);
try {
while(System.currentTimeMillis() < timeout) {
while(ready()) {
int charInt = super.read();
if(charInt==-1) return; // EOS reached
char character = (char) charInt;
if(character == '\n' || character == '\r' ) return;
}
try {
Thread.sleep(millisInterval);
} catch (InterruptedException e) {
Thread.currentThread().interrupt(); // Restore flag
break;
}
}
} finally {
super.reset();
}
throw new IllegalThreadStateException("readLine timed out");
}
protected void waitReady() throws IllegalThreadStateException, IOException {
if(ready()) return;
long timeout = System.currentTimeMillis() + millisTimeout;
while(System.currentTimeMillis() < timeout) {
if(ready()) return;
try {
Thread.sleep(millisInterval);
} catch (InterruptedException e) {
Thread.currentThread().interrupt(); // Restore flag
break;
}
}
if(ready()) return; // Just in case.
throw new IllegalThreadStateException("read timed out");
}
}

Categories

Resources