package org.jboss.pitbull.internal.nio.socket; import org.jboss.pitbull.PitbullChannel; import java.io.IOException; import java.io.InterruptedIOException; import java.io.OutputStream; import java.nio.ByteBuffer; import java.util.concurrent.TimeUnit; /** * Works the same as BufferedOutputStream except it invokes a callback prior to: * - initial flush of buffer * - subsequent flush of buffer * * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a> * @version $Revision: 1 $ */ public class BufferedBlockingOutputStream extends OutputStream { protected PitbullChannel channel; protected ByteBuffer buffer; protected int size; protected boolean closed; protected long timeout; protected boolean committed = false; /** * delegate OutputStream can be null and set at another time (i.e. at initialFlush time) * * @param out */ public BufferedBlockingOutputStream(PitbullChannel channel) { this(channel, 8192); } /** * delegate OutputStream can be null and set at another time (i.e. at initialFlush time) * * @param out * @param size must be > 0 */ public BufferedBlockingOutputStream(PitbullChannel channel, int size) { this.channel = channel; setBufferSize(size); } protected void flushBuffer() throws IOException { if (buffer.position() > 0) { buffer.flip(); writeMessage(buffer); buffer.clear(); committed = true; } } public void reset() throws IllegalStateException { if (committed == false) throw new IllegalStateException("Buffer was flushed"); buffer.clear(); } public int getBufferSize() { return size; } public void setBufferSize(int size) throws IllegalStateException { if (size <= 0) { throw new IllegalArgumentException("Cannot set a buffer size that is less than zero."); } if (buffer != null && buffer.position() > 0) throw new IllegalStateException("Buffer has been written to, cannot reset size"); this.size = size; buffer = ByteBuffer.allocate(size); } public synchronized void write(int b) throws IOException { checkClosed(); if (buffer.hasRemaining()) { flushBuffer(); } buffer.put((byte) b); } public synchronized void write(byte b[], int off, int len) throws IOException { checkClosed(); if (len >= size) { flushBuffer(); ByteBuffer tmp = ByteBuffer.wrap(b, off, len); writeMessage(tmp); return; } if (len > buffer.remaining()) { flushBuffer(); } buffer.put(b, off, len); } protected void writeMessage(ByteBuffer tmp) throws IOException { final long timeout = this.timeout; int total = 0; if (timeout == 0L) { try { total = channel.writeBlocking(tmp); } catch (InterruptedIOException e) { e.bytesTransferred = tmp.position(); throw e; } } else { try { total = channel.writeBlocking(tmp, timeout, TimeUnit.MILLISECONDS); } catch (InterruptedIOException e) { e.bytesTransferred = tmp.position(); throw e; } } } public synchronized void flush() throws IOException { checkClosed(); flushBuffer(); } public synchronized void close() throws IOException { if (closed) return; flushBuffer(); closed = true; } protected void checkClosed() throws IOException { if (closed) throw new IOException("Stream is closed."); } }