2 * Jexer - Java Text User Interface
4 * The MIT License (MIT)
6 * Copyright (C) 2017 Kevin Lamonte
8 * Permission is hereby granted, free of charge, to any person obtaining a
9 * copy of this software and associated documentation files (the "Software"),
10 * to deal in the Software without restriction, including without limitation
11 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
12 * and/or sell copies of the Software, and to permit persons to whom the
13 * Software is furnished to do so, subject to the following conditions:
15 * The above copyright notice and this permission notice shall be included in
16 * all copies or substantial portions of the Software.
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
21 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
24 * DEALINGS IN THE SOFTWARE.
26 * @author Kevin Lamonte [kevin.lamonte@gmail.com]
31 import java
.io
.IOException
;
32 import java
.io
.InputStream
;
35 * This class provides an optional millisecond timeout on its read()
36 * operations. This permits callers to bail out rather than block.
38 public class TimeoutInputStream
extends InputStream
{
43 private InputStream stream
;
46 * The timeout value in millis. If it takes longer than this for bytes
47 * to be available for read then a ReadTimeoutException is thrown. A
48 * value of 0 means to block as a normal InputStream would.
50 private int timeoutMillis
;
53 * If true, the current read() will timeout soon.
55 private volatile boolean cancel
= false;
58 * Request that the current read() operation timeout immediately.
60 public synchronized void cancelRead() {
65 * Public constructor, at the default timeout of 10000 millis (10
68 * @param stream the wrapped InputStream
70 public TimeoutInputStream(final InputStream stream
) {
72 this.timeoutMillis
= 10000;
78 * @param stream the wrapped InputStream
79 * @param timeoutMillis the timeout value in millis. If it takes longer
80 * than this for bytes to be available for read then a
81 * ReadTimeoutException is thrown. A value of 0 means to block as a
82 * normal InputStream would.
84 public TimeoutInputStream(final InputStream stream
,
85 final int timeoutMillis
) {
87 if (timeoutMillis
< 0) {
88 throw new IllegalArgumentException("Invalid timeoutMillis value, " +
93 this.timeoutMillis
= timeoutMillis
;
97 * Reads the next byte of data from the input stream.
99 * @return the next byte of data, or -1 if there is no more data because
100 * the end of the stream has been reached.
101 * @throws IOException if an I/O error occurs
104 public int read() throws IOException
{
106 if (timeoutMillis
== 0) {
107 // Block on the read().
108 return stream
.read();
111 if (stream
.available() > 0) {
112 // A byte is available now, return it.
113 return stream
.read();
116 // We will wait up to timeoutMillis to see if a byte is available.
117 // If not, we throw ReadTimeoutException.
118 long checkTime
= System
.currentTimeMillis();
119 while (stream
.available() == 0) {
120 long now
= System
.currentTimeMillis();
121 synchronized (this) {
122 if ((now
- checkTime
> timeoutMillis
) || (cancel
== true)) {
123 if (cancel
== true) {
126 throw new ReadTimeoutException("Timeout on read(): " +
127 (int) (now
- checkTime
) + " millis and still no data");
131 // How long do we sleep for, eh? For now we will go with 2
134 } catch (InterruptedException e
) {
139 if (stream
.available() > 0) {
140 // A byte is available now, return it.
141 return stream
.read();
144 throw new IOException("InputStream claimed a byte was available, but " +
145 "now it is not. What is going on?");
149 * Reads some number of bytes from the input stream and stores them into
150 * the buffer array b.
152 * @param b the buffer into which the data is read.
153 * @return the total number of bytes read into the buffer, or -1 if there
154 * is no more data because the end of the stream has been reached.
155 * @throws IOException if an I/O error occurs
158 public int read(final byte[] b
) throws IOException
{
159 if (timeoutMillis
== 0) {
160 // Block on the read().
161 return stream
.read(b
);
164 int remaining
= b
.length
;
166 if (stream
.available() >= remaining
) {
167 // Enough bytes are available now, return them.
168 return stream
.read(b
);
171 while (remaining
> 0) {
173 // We will wait up to timeoutMillis to see if a byte is
174 // available. If not, we throw ReadTimeoutException.
175 long checkTime
= System
.currentTimeMillis();
176 while (stream
.available() == 0) {
177 long now
= System
.currentTimeMillis();
179 synchronized (this) {
180 if ((now
- checkTime
> timeoutMillis
) || (cancel
== true)) {
181 if (cancel
== true) {
184 throw new ReadTimeoutException("Timeout on read(): " +
185 (int) (now
- checkTime
) + " millis and still no " +
190 // How long do we sleep for, eh? For now we will go with
193 } catch (InterruptedException e
) {
198 if (stream
.available() > 0) {
199 // At least one byte is available now, read it.
200 int n
= stream
.available();
204 int rc
= stream
.read(b
, b
.length
- remaining
, n
);
206 // This shouldn't happen.
207 throw new IOException("InputStream claimed bytes were " +
208 "available, but read() returned -1. What is going " +
216 throw new IOException("InputStream claimed all bytes were available, " +
217 "but now it is not. What is going on?");
221 * Reads up to len bytes of data from the input stream into an array of
224 * @param b the buffer into which the data is read.
225 * @param off the start offset in array b at which the data is written.
226 * @param len the maximum number of bytes to read.
227 * @return the total number of bytes read into the buffer, or -1 if there
228 * is no more data because the end of the stream has been reached.
229 * @throws IOException if an I/O error occurs
232 public int read(final byte[] b
, final int off
,
233 final int len
) throws IOException
{
235 if (timeoutMillis
== 0) {
236 // Block on the read().
237 return stream
.read(b
);
242 if (stream
.available() >= remaining
) {
243 // Enough bytes are available now, return them.
244 return stream
.read(b
, off
, remaining
);
247 while (remaining
> 0) {
249 // We will wait up to timeoutMillis to see if a byte is
250 // available. If not, we throw ReadTimeoutException.
251 long checkTime
= System
.currentTimeMillis();
252 while (stream
.available() == 0) {
253 long now
= System
.currentTimeMillis();
254 synchronized (this) {
255 if ((now
- checkTime
> timeoutMillis
) || (cancel
== true)) {
256 if (cancel
== true) {
259 throw new ReadTimeoutException("Timeout on read(): " +
260 (int) (now
- checkTime
) + " millis and still no " +
265 // How long do we sleep for, eh? For now we will go with
268 } catch (InterruptedException e
) {
273 if (stream
.available() > 0) {
274 // At least one byte is available now, read it.
275 int n
= stream
.available();
279 int rc
= stream
.read(b
, off
+ len
- remaining
, n
);
281 // This shouldn't happen.
282 throw new IOException("InputStream claimed bytes were " +
283 "available, but read() returned -1. What is going " +
290 throw new IOException("InputStream claimed all bytes were available, " +
291 "but now it is not. What is going on?");
295 * Returns an estimate of the number of bytes that can be read (or
296 * skipped over) from this input stream without blocking by the next
297 * invocation of a method for this input stream.
299 * @return an estimate of the number of bytes that can be read (or
300 * skipped over) from this input stream without blocking or 0 when it
301 * reaches the end of the input stream.
302 * @throws IOException if an I/O error occurs
305 public int available() throws IOException
{
306 return stream
.available();
310 * Closes this input stream and releases any system resources associated
313 * @throws IOException if an I/O error occurs
316 public void close() throws IOException
{
321 * Marks the current position in this input stream.
323 * @param readLimit the maximum limit of bytes that can be read before
324 * the mark position becomes invalid
327 public void mark(final int readLimit
) {
328 stream
.mark(readLimit
);
332 * Tests if this input stream supports the mark and reset methods.
334 * @return true if this stream instance supports the mark and reset
335 * methods; false otherwise
338 public boolean markSupported() {
339 return stream
.markSupported();
343 * Repositions this stream to the position at the time the mark method
344 * was last called on this input stream.
346 * @throws IOException if an I/O error occurs
349 public void reset() throws IOException
{
354 * Skips over and discards n bytes of data from this input stream.
356 * @param n the number of bytes to be skipped
357 * @return the actual number of bytes skipped
358 * @throws IOException if an I/O error occurs
361 public long skip(final long n
) throws IOException
{
362 return stream
.skip(n
);