package org.rzo.netty.ahessian.io;
import java.io.IOException;
import java.io.OutputStream;
import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.channel.ChannelPipeline;
import org.jboss.netty.channel.ChannelStateEvent;
import org.jboss.netty.channel.SimpleChannelHandler;
import org.rzo.netty.ahessian.stopable.StopableHandler;
/**
* Encodes bytes written to an {@link OutputStream} into a {@link ChannelBuffer}
* . A typical setup for a serialization protocol in a TCP/IP socket would be:
*
* <pre>
* {@link ChannelPipeline} pipeline = ...;
*
* // Encoder
* pipeline.addLast("outputStream", new {@link handler.io.OutputStream}());
* pipeline.addLast("outputHandler", new MyOutputHandler());
*
* // Decoder
* pipeline.addLast("inputStream", new {@link handler.io.InputStream}());
* pipeline.addLast("inputHandler", new MyInputHandler());
* </pre>
*
* and then, within the handler you can use a {@link java.io.InputStream} or
* {@link java.io.OutputStream} instead of a {@link ChannelBuffer} as a message: <br>
* Writing to OutputStream:
*
* <pre>
* // synchronized for multithreaded environment to avoid messages mixing
* synchronized public void writeRequested(ChannelHandlerContext ctx, MessageEvent e) throws Exception
* {
* byte[] message = (byte[]) e.getMessage();
* OutputStream out = OutputStreamEncoder.getOutputStream(ctx);
* out.write(message);
* // if this is the last chunk of bytes we should flush the output
* out.flush();
* // netty seems to require this, so that the boss thread may read input from the channel
* Thread.yield();
* }
*
* </pre>
*
* <br>
* Reading from InputStream:
*
* <pre>
* void messageReceived(ChannelHandlerContext ctx, MessageEvent e)
* {
* // message received is called only once to deliver the input stream
* // it is called in a separate thread and not in the netty worker thread.
* // incoming bytes are consumed in this method.
* // the stream is closed once the channel is disconnected
* InputStream in = (InputStream) evt.getMessage();
*
* while (ctx.getChannel().isConnected())
* {
* // parse the incoming stream and forward the result to the next handler
* Channels.fireMessageReceived(ctx, parseReply(in));
* }
* }
* </pre>
*/
public class OutputStreamEncoder extends SimpleChannelHandler implements StopableHandler
{
volatile OutputStreamBuffer _buffer = null;
private boolean _stopEnabled = true;
boolean _crcCheck = false;
public OutputStreamEncoder()
{
}
public OutputStreamEncoder(boolean crcCheck)
{
_crcCheck = crcCheck;
}
/* (non-Javadoc)
* @see org.jboss.netty.channel.SimpleChannelHandler#channelConnected(org.jboss.netty.channel.ChannelHandlerContext, org.jboss.netty.channel.ChannelStateEvent)
*/
@Override
public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception
{
if (_buffer == null)
{
if (_crcCheck)
_buffer = new CRCOutputStream(ctx);
else
_buffer = new OutputStreamBuffer(ctx);
ctx.setAttachment(_buffer);
}
else
_buffer.setContext(ctx);
ctx.sendUpstream(e);
}
/* (non-Javadoc)
* @see org.jboss.netty.channel.SimpleChannelHandler#channelDisconnected(org.jboss.netty.channel.ChannelHandlerContext, org.jboss.netty.channel.ChannelStateEvent)
*/
@Override
public void channelDisconnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception
{
if (_buffer != null)
{
_buffer.close();
}
ctx.sendUpstream(e);
}
/**
* Helper method: Gets the output stream from the pipeline of a given context.
*
* @param ctx the context
*
* @return the output stream
*/
public static OutputStream getOutputStream(ChannelHandlerContext ctx)
{
return (OutputStream) ctx.getPipeline().getContext(OutputStreamEncoder.class).getAttachment();
}
public static OutputStreamEncoder getOutputEncoder(ChannelHandlerContext ctx)
{
return (OutputStreamEncoder) ctx.getPipeline().getContext(OutputStreamEncoder.class).getHandler();
}
public OutputStreamBuffer getBuffer()
{
return _buffer;
}
public boolean isStopEnabled()
{
return _stopEnabled ;
}
public void setStopEnabled(boolean stopEnabled)
{
_stopEnabled = stopEnabled;
}
public void stop()
{
try
{
_buffer.close();
}
catch (IOException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
_buffer = null;
}
}