bb.io
Class StreamUtil

java.lang.Object
  extended by bb.io.StreamUtil

public final class StreamUtil
extends Object

Provides miscellaneous static utility methods for dealing with streams.

This class is multithread safe: it is immutable (both its immediate state, as well as the deep state of its fields).

This class checks for thread interruption only before major blocking operations in order to achieve a reasonable balance between cancellation responsiveness and program performance (see Doug Lea Concurrent Programming in Java Second Edition p. 170).

Author:
Brent Boyer

Nested Class Summary
private static class StreamUtil.Drainer
          Solely used by the internal worker thread of the above drain method.
private static class StreamUtil.TransferProgressReporter
          Solely used for reporting progress of the transfer method.
static class StreamUtil.UnitTest
          See the Overview page of the project's javadocs for a general description of this unit test class.
 
Field Summary
private static int bufferSizeDrainMin
          Used by drain(InputStream) when it calls calcBufferSize to set a lower bound on the size of the buffer used by drain.
private static int bufferSizeTransfer
           
private static Class[] classArray
          Cached value used by close.
private static Object[] parameterArray
          Cached value used by close.
 
Constructor Summary
private StreamUtil()
          This sole private constructor suppresses the default (public) constructor, ensuring non-instantiability outside of this class.
 
Method Summary
private static int calcBufferSize(InputStream in, int lengthMax)
          First checks that in.available <= lengthMax, throwing an IllegalStateException if that constraint is not obeyed, since if it is violated then drain cannot work.
static void close(Closeable closeable)
          Immediately returns if obj is null.
static void close(Object obj)
          Immediately returns if obj is null.
static byte[] drain(InputStream in)
          Returns drain(in, Integer.MAX_VALUE).
static byte[] drain(InputStream in, int lengthMax)
          Attempts to read all the bytes from in until End Of Stream is reached.
static byte[] drain(InputStream in, int lengthMax, long timeout)
          Returns drain(in, lengthMax), if that call executes within timeout, else throws a TimeoutException.
static char[] drainAsciiToChars(InputStream in)
          Passes in to the drain(InputStream) method.
static String drainIntoString(InputStream in)
          Passes in to the drain(InputStream) method.
static String drainIntoString(InputStream in, String charEncoding)
          Passes in to the drain(InputStream) method.
static void readFully(InputStream in, byte[] bytes)
          Fully reads into the entire supplied byte[].
static void readFully(InputStream in, byte[] bytes, int offset, int length)
          Tries to read exactly the requested length from in into bytes.
static void readFully(Reader in, char[] chars)
          Fully reads into the entire supplied char[].
static void readFully(Reader in, char[] chars, int offset, int length)
          Tries to read exactly the requested length from in into chars.
static void transfer(InputStream in, OutputStream out)
          Simply calls transfer(in, out, null).
static void transfer(InputStream in, OutputStream out, PrintWriter logger)
          Transfers all the data from in to out, that is, reads bytes from in and writes them to out until End Of Stream with in is reached.
 
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
 

Field Detail

bufferSizeDrainMin

private static final int bufferSizeDrainMin
Used by drain(InputStream) when it calls calcBufferSize to set a lower bound on the size of the buffer used by drain.

The size of this value needs to balance creating buffers that are too small (which causes resizes inside drain) versus being too large and wasting memory when the stream has only a little data.

See Also:
Constant Field Values

bufferSizeTransfer

private static final int bufferSizeTransfer
See Also:
Constant Field Values

classArray

private static final Class[] classArray
Cached value used by close.


parameterArray

private static final Object[] parameterArray
Cached value used by close.

Constructor Detail

StreamUtil

private StreamUtil()
This sole private constructor suppresses the default (public) constructor, ensuring non-instantiability outside of this class.

Method Detail

readFully

public static void readFully(InputStream in,
                             byte[] bytes)
                      throws IllegalArgumentException,
                             EOFException,
                             IOException
Fully reads into the entire supplied byte[]. Is a convenience method that simply calls readFully(in, bytes, 0, bytes.length)

Throws:
IllegalArgumentException - if in or bytes == null
EOFException - if Hit End Of Stream before reading all the requested bytes
IOException - if an I/O problem occurs

readFully

public static void readFully(InputStream in,
                             byte[] bytes,
                             int offset,
                             int length)
                      throws IllegalArgumentException,
                             EOFException,
                             IOException
Tries to read exactly the requested length from in into bytes. The writing of bytes starts at the index == offset and continues up to (but not including) the index == offset + length.

This method is required because InputStream.read(byte[], int, int) does not guarantee that requested length will be read, whereas this method does (unless an Exception is thrown first).

Note: this method does not close in when finished.

Throws:
IllegalArgumentException - if in or bytes == null, offset or length are negative, or offset + length are greater than bytes.length
EOFException - if Hit End Of Stream before reading all the requested bytes
IOException - if an I/O problem occurs

readFully

public static void readFully(Reader in,
                             char[] chars)
                      throws IllegalArgumentException,
                             EOFException,
                             IOException
Fully reads into the entire supplied char[]. Is a convenience method that simply calls readFully(in, chars, 0, chars.length)

Throws:
IllegalArgumentException - if in or chars == null
EOFException - if Hit End Of Stream before reading all the requested chars
IOException - if an I/O problem occurs

readFully

public static void readFully(Reader in,
                             char[] chars,
                             int offset,
                             int length)
                      throws IllegalArgumentException,
                             EOFException,
                             IOException
Tries to read exactly the requested length from in into chars. The writing of chars starts at the index == offset and continues up to (but not including) the index == offset + length.

This method is required because Reader.read(char[], int, int) does not guarantee that requested length will be read, whereas this method does (unless an Exception is thrown first).

Note: this method does not close in when finished.

Throws:
IllegalArgumentException - if in or chars == null, offset or length are negative, or offset + length are greater than chars.length
EOFException - if Hit End Of Stream before reading all the requested chars
IOException - if an I/O problem occurs

drain

public static byte[] drain(InputStream in)
                    throws IllegalArgumentException,
                           IllegalStateException,
                           IOException
Returns drain(in, Integer.MAX_VALUE).

Throws:
IllegalArgumentException - if in == null
IllegalStateException - if in holds more than Integer.MAX_VALUE bytes (which cannot be held in a java array)
IOException - if an I/O problem occurs

drain

public static byte[] drain(InputStream in,
                           int lengthMax)
                    throws IllegalArgumentException,
                           IllegalStateException,
                           IOException
Attempts to read all the bytes from in until End Of Stream is reached. If the number of bytes read is <= lengthMax, then they are returned as a byte[]; this result is never null, but may be zero-length. Otherwise, if in contains more than lengthMax bytes, an IllegalStateException is thrown.

This method blocks for an unlimited time until End Of Stream is reached; see drain(InputStream, int, long) for a version with timeout.

Note: the final action is to close in.

Warning: be careful with using this method on large capacity streams (e.g. GB sized files), since could run out of memory.

An alternative to this method might be to use NIO's Channels and ByteBuffers (especially mapped ones); see this sample code.

Throws:
IllegalArgumentException - if in == null; lengthMax <= 0
IllegalStateException - if in holds more than lengthMax bytes
IOException - if an I/O problem occurs

calcBufferSize

private static int calcBufferSize(InputStream in,
                                  int lengthMax)
                           throws IOException,
                                  IllegalStateException
First checks that in.available <= lengthMax, throwing an IllegalStateException if that constraint is not obeyed, since if it is violated then drain cannot work. Then checks that bufferSizeDrainMin <= lengthMax, returning lengthMax if that condition is not obeyed, since the result should never exceed lengthMax.

Assuming those checks are passed, then returns Math.max(in.available(), bufferSizeDrainMin). In other words, the result is normally the amount of data available on the stream except when that value is too small, in which case a min value is returned instead.

The reason why the amount of data available on the stream should normally be returned is to handle InputStreams where the length should static and fully determinable by calling the InputStream's available method (e.g. InputStreams from files). In this case, drain(InputStream) should be extremely efficient because only a single buffer need be used with no further allocations or copying.

The reason why you need bufferSizeDrainMin to establish a lower bound is because some InputStreams (e.g. from sockets) cannot return an accurate value for the eventual amount of data that will be read.

Throws:
IOException - if an I/O problem occurs
IllegalStateException - if in.available() > lengthMax

drain

public static byte[] drain(InputStream in,
                           int lengthMax,
                           long timeout)
                    throws Throwable
Returns drain(in, lengthMax), if that call executes within timeout, else throws a TimeoutException.

From the caller's perspective, this is a synchronous method call. Internally, however, a new worker thread is created that does the actual call to drain which enables the calling thread to detect timeout.

Parameters:
in - an arbitrary InputStream
lengthMax - the maximum number of bytes that the stream should contain
timeout - the length of time (in ms) to allow for draining to occur
Returns:
all the bytes from in
Throws:
Throwable - (or some subclass) if any problem occurs; in particular, note that throws
  • an IllegalArgumentException if in == null or timeout <= 0
  • a TimeoutException if timeout occurs
  • any Throwable caught by the internal draining thread will be rethrown by this method

drainAsciiToChars

public static char[] drainAsciiToChars(InputStream in)
                                throws IllegalArgumentException,
                                       IllegalStateException,
                                       IOException
Passes in to the drain(InputStream) method. Converts the returned byte[] into a char[] (using StringUtil.asciiBytesToChars), and returns that char[]. The data inside in must solely consist of US-ASCII bytes (i.e. no negative values).

Throws:
IllegalArgumentException - if in == null; a non-ascii byte (i.e. a negative value) is encountered
IllegalStateException - if in holds more than Integer.MAX_VALUE bytes (which cannot be held in a java array)
IOException - if an I/O problem occurs

drainIntoString

public static String drainIntoString(InputStream in,
                                     String charEncoding)
                              throws IllegalArgumentException,
                                     IllegalStateException,
                                     IOException,
                                     UnsupportedEncodingException
Passes in to the drain(InputStream) method. Converts the returned byte[] into a String using the spacified char encoding, and returns that String.

Throws:
IllegalArgumentException - if in == null
IllegalStateException - if in holds more than Integer.MAX_VALUE bytes (which cannot be held in a java array)
IOException - if an I/O problem occurs
UnsupportedEncodingException - if charEncoding is not supported

drainIntoString

public static String drainIntoString(InputStream in)
                              throws IllegalArgumentException,
                                     IllegalStateException,
                                     IOException
Passes in to the drain(InputStream) method. Converts the returned byte[] into a String using the platform's default char encoding, and returns that String.

Throws:
IllegalArgumentException - if in == null
IllegalStateException - if in holds more than Integer.MAX_VALUE bytes (which cannot be held in a java array)
IOException - if an I/O problem occurs

transfer

public static void transfer(InputStream in,
                            OutputStream out)
                     throws IllegalArgumentException,
                            IOException
Simply calls transfer(in, out, null).

Throws:
IllegalArgumentException - if in == null or if out == null
IOException - if an I/O problem occurs

transfer

public static void transfer(InputStream in,
                            OutputStream out,
                            PrintWriter logger)
                     throws IllegalArgumentException,
                            IOException
Transfers all the data from in to out, that is, reads bytes from in and writes them to out until End Of Stream with in is reached. If logger is non-null, then feedback on the transfer process is written to it.

Warning: this method does not close the streams; that is the caller's responsibility.

Throws:
IllegalArgumentException - if in == null or if out == null
IOException - if an I/O problem occurs

close

public static void close(Closeable closeable)
Immediately returns if obj is null. Otherwise, calls closeable.close.

Contract: this method should never throw a Throwable. Any Throwable that is raised is caught and logged robustly to the default Logger.


close

public static void close(Object obj)
Immediately returns if obj is null. Otherwise, calls ReflectUtil.callLogError(Object, String)(obj, "close").