package org.rzo.netty.ahessian.io;
import static org.jboss.netty.buffer.ChannelBuffers.dynamicBuffer;
import java.io.IOException;
import java.io.OutputStream;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.channel.ChannelFuture;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.channel.Channels;
import org.jboss.netty.channel.DownstreamMessageEvent;
import org.rzo.netty.ahessian.Constants;
/**
* A buffer for storing outgoing bytes. Bytes are sent upstream if
* The number of written bytes is > than a watermark, or if flush is called
*/
public class OutputStreamBuffer extends OutputStream
{
/** The context of the channel on which to send the bytes downstream */
private volatile ChannelHandlerContext _ctx;
/** Indicates if the stream has been closed */
private volatile boolean _closed = false;
private Lock _lock = new ReentrantLock();
/** If written bytes > watermark, the bytes are sent downstream */
int _watermark = 1024*1024;
int _initialBuffSize = 1024;
/** The buffer for storing outgoing bytes. Once the bytes have been sent downstream a new buffer is created */
private ChannelBuffer _buf = dynamicBuffer(_initialBuffSize);
/**
* Instantiates a new output stream buffer.
*
* @param ctx the context in which bytes are sent downstream
*/
OutputStreamBuffer(ChannelHandlerContext ctx)
{
super();
_ctx = ctx;
}
/* (non-Javadoc)
* @see java.io.OutputStream#write(int)
*/
@Override
public void write(int b) throws IOException
{
if (_closed)
throw new IOException("stream closed");
_lock.lock();
_buf.writeByte((byte)b);
if (_buf.writerIndex() >= _watermark)
sendDownstream(null);
_lock.unlock();
}
/* (non-Javadoc)
* @see java.io.OutputStream#write(byte[], int, int)
*/
@Override
public void write(byte b[], int off, int len) throws IOException
{
if (_closed)
throw new IOException("stream closed");
_lock.lock();
_buf.writeBytes(b, off, len);
if (_buf.writerIndex() >= _watermark)
sendDownstream(null);
_lock.unlock();
}
/* (non-Javadoc)
* @see java.io.OutputStream#flush()
*/
@Override
public void flush() throws IOException
{
flush(null);
}
public void flush(ChannelFuture future) throws IOException
{
_lock.lock();
if (_buf.readableBytes() > 0)
try
{
super.flush();
if (future == null)
{
ChannelFuture f = sendDownstream(null);
f.await(20000);
//if (!f.await(10000))
// throw new IOException("write longer than 10 secs");
}
else
{
sendDownstream(future);
}
}
catch (Exception e)
{
throw new IOException(e);
}
finally
{
_lock.unlock();
}
}
private ChannelFuture sendDownstream(ChannelFuture future) throws IOException
{
if (! _ctx.getChannel().isConnected())
throw new IOException("channel disconnected");
while (!_ctx.getChannel().isWritable())
try
{
Thread.sleep(100);
}
catch (Exception ex)
{
Constants.ahessianLogger.warn("",ex);
}
if (future == null)
future = Channels.future(_ctx.getChannel());
_ctx.sendDownstream(new DownstreamMessageEvent(_ctx.getChannel(), future, _buf, _ctx.getChannel().getRemoteAddress()));
_buf = dynamicBuffer(1024);
return future;
}
/* (non-Javadoc)
* @see java.io.OutputStream#close()
*/
@Override
public void close() throws IOException
{
_lock.lock();
_closed = true;
_lock.unlock();
}
public void setContext(ChannelHandlerContext ctx)
{
_ctx = ctx;
reset();
}
public ChannelHandlerContext getContext()
{
return _ctx;
}
public void reset()
{
_lock.lock();
_buf = dynamicBuffer();
_closed = false;
_lock.unlock();
}
}