package net.floodlightcontroller.core.util;
import java.util.ArrayList;
import java.util.List;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufInputStream;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import org.apache.thrift.TBase;
import org.apache.thrift.protocol.TCompactProtocol;
import org.apache.thrift.transport.TIOStreamTransport;
/**
* Slice and decode Thrift Serialized Messages from the channel, push a {@link List} of Messages upstream.
*
* Each message is preceeded by a four-byte length field.
*
* Subclasses should implement {@link #allocateMessage()} to allocate an instance of the
* desired message type.
*
* Implementation note: this decoder class was initially built for netty3, and is unnecessarily
* complex and inelegant. In particular, netty has efficient support for handling lists of messages
* now, so this could be replaced by a plain {@link LengthFieldBasedFrameDecoder} to do the slicing
* and then a simple ThriftDecoder that would just decode one message at a time.
*
* @author readams
* @author Andreas Wundsam <andreas.wundsam@bigswitch.com>
*/
public abstract class ThriftFrameDecoder<T extends TBase<?,?>> extends LengthFieldBasedFrameDecoder {
public ThriftFrameDecoder(int maxSize) {
super(maxSize, 0, 4, 0, 4);
}
protected abstract T allocateMessage();
@Override
protected final Object decode(ChannelHandlerContext ctx,
ByteBuf buffer) throws Exception {
/* This is initialized to null because the decode function must return
* null if the buffer does not contain a complete frame and cannot be
* decoded.
*/
List<T> ms = null;
ByteBuf frame = null;
while (null != (frame = (ByteBuf) super.decode(ctx, buffer))) {
if (ms == null) ms = new ArrayList<T>();
ByteBufInputStream is = new ByteBufInputStream(frame);
TCompactProtocol thriftProtocol =
new TCompactProtocol(new TIOStreamTransport(is));
T message = allocateMessage();
message.read(thriftProtocol);
ms.add(message);
}
return ms;
}
@Override
protected final ByteBuf extractFrame(ChannelHandlerContext ctx, ByteBuf buffer, int index,
int length) {
return buffer.slice(index, length);
}
}