Class AsyncOutputStream

java.lang.Object
java.io.OutputStream
java.io.FilterOutputStream
org.dellroad.stuff.io.AsyncOutputStream
All Implemented Interfaces:
Closeable, Flushable, AutoCloseable

public class AsyncOutputStream extends FilterOutputStream
An OutputStream that performs writes using a background thread, so that write, flush, and close operations never block.

If the underlying output stream throws an IOException during any operation, this instance will re-throw the exception for all subsequent operations.

Instances use an internal buffer whose size is configured at construction time; if the buffer overflows, a BufferOverflowException is thrown. Alternately, if a buffer size of zero is configured, the internal buffer will expand automatically as needed (up to 231 bytes). However, this creates a memory leak if the underlying OutputStream blocks indefinitely.

Instances of this class are thread safe, and moreover writes are atomic: if multiple threads are writing at the same time the bytes written in any single method invocation are written contiguously to the underlying output.

  • Field Details

    • log

      protected final Logger log
  • Constructor Details

    • AsyncOutputStream

      public AsyncOutputStream(OutputStream out)
      Convenience constructor for when an auto-expanding buffer is desired and a default thread name is to be used.
      Parameters:
      out - underlying output stream
      Throws:
      IllegalArgumentException - if out is null
    • AsyncOutputStream

      public AsyncOutputStream(OutputStream out, String name)
      Convenience constructor for when an auto-expanding buffer is desired.
      Parameters:
      out - underlying output stream
      name - name for this instance; used to create the name of the background thread
      Throws:
      IllegalArgumentException - if out or name is null
    • AsyncOutputStream

      public AsyncOutputStream(OutputStream out, int bufsize, String name)
      Constructor.
      Parameters:
      out - underlying output stream
      bufsize - maximum number of bytes we can buffer, or zero for an auto-expanding buffer that has no fixed limit
      name - name for this instance; used to create the name of the background thread
      Throws:
      IllegalArgumentException - if out or name is null
      IllegalArgumentException - if bufsize is negative
  • Method Details

    • write

      public void write(int b) throws IOException
      Write data.

      This method will never block. To effect a normal blocking write, use waitForSpace(int, long) first.

      Overrides:
      write in class FilterOutputStream
      Parameters:
      b - byte to write (lower 8 bits)
      Throws:
      IOException - if an exception has been thrown by the underlying stream
      IOException - if this instance has been closed
      BufferOverflowException - if the buffer does not have room for the new byte
    • write

      public void write(byte[] data, int off, int len) throws IOException
      Write data.

      This method will never block. To effect a normal blocking write, invoke waitForSpace(int, long) first.

      Overrides:
      write in class FilterOutputStream
      Parameters:
      data - bytes to write
      off - starting offset in buffer
      len - number of bytes to write
      Throws:
      IOException - if an exception has been thrown by the underlying stream
      IOException - if this instance has been closed
      BufferOverflowException - if the buffer does not have room for the new data
      IllegalArgumentException - if len is negative
    • flush

      public void flush() throws IOException
      Flush output. This method will cause the underlying stream to be flushed once all of the data written to this instance at the time this method is invoked has been written to it.

      If additional data is written and then a second flush is requested before the first flush has actually occurred, the first flush will be canceled and only the second flush will be applied. Normally this is not a problem because the act of writing more data and then flushing forces earlier data to be flushed as well.

      This method will never block. To block until the underlying flush operation completes, invoke waitForIdle(long).

      Specified by:
      flush in interface Flushable
      Overrides:
      flush in class FilterOutputStream
      Throws:
      IOException - if this instance has been closed
      IOException - if an exception has been detected on the underlying stream
      IOException - if the current thread is interrupted; the nested exception will an InterruptedException
    • close

      public void close() throws IOException
      Close this instance. This will (eventually) close the underlying output stream.

      If this instance has already been closed, nothing happens.

      This method will never block. To block until the underlying close operation completes, invoke waitForIdle(long).

      Specified by:
      close in interface AutoCloseable
      Specified by:
      close in interface Closeable
      Overrides:
      close in class FilterOutputStream
      Throws:
      IOException - if an exception has been detected on the underlying stream
    • getException

      public IOException getException()
      Get the exception thrown by the underlying output stream, if any.
      Returns:
      thrown exception, or null if none has been thrown by the underlying stream
    • getBufferSize

      public int getBufferSize()
      Get the capacity of this instance's output buffer.

      If a (fixed) non-zero value was given at construction time, this will return that value.

      Returns:
      current output buffer capacity
    • availableBufferSpace

      public int availableBufferSpace() throws IOException
      Get the number of free bytes remaining in the output buffer.
      Returns:
      current number of available bytes in the output buffer
      Throws:
      IOException - if this instance is or has been closed
      IOException - if an exception has been detected on the underlying stream
      See Also:
    • isWorkOutstanding

      public boolean isWorkOutstanding() throws IOException
      Determine if there is outstanding work still to be performed (writes, flushes, and/or close operations) by the background thread.
      Returns:
      true if work remains to be done
      Throws:
      IOException - if this instance is or has been closed
      IOException - if an exception has been detected on the underlying stream
      See Also:
    • waitForSpace

      public boolean waitForSpace(int numBytes, long timeout) throws IOException, InterruptedException
      Wait for buffer space availability.

      If a zero buffer size was configured at construction time, indicating an auto-expanding buffer, this will return immediately.

      Parameters:
      numBytes - amount of buffer space required
      timeout - maximum time to wait in milliseconds, or zero for infinite
      Returns:
      true if space was found, false if time expired
      Throws:
      IOException - if this instance is or has been closed
      IOException - if an exception has been detected on the underlying stream
      IllegalArgumentException - if numBytes is greater than the configured buffer size
      IllegalArgumentException - if timeout is negative
      InterruptedException - if the current thread is interrupted
      See Also:
    • waitForIdle

      public boolean waitForIdle(long timeout) throws IOException, InterruptedException
      Wait for all outstanding work to complete.
      Parameters:
      timeout - maximum time to wait in milliseconds, or zero for infinite
      Returns:
      true for success, false if time expired
      Throws:
      IOException - if this instance is or has been closed
      IOException - if an exception has been detected on the underlying stream
      IllegalArgumentException - if timeout is negative
      InterruptedException - if the current thread is interrupted
      See Also: