---EDIT below
I'm actually implementing the Mina ProtocolCodecFilter in order to receive messages from a serial device.
The codec specifies multiple different messages (with their pojos), and even thou the implementation works correctly 99% of the time, I'm getting issues with one type of message: the only message that doesn't have a fixed length. I can know the minimum length, but never the maximum.
This is the exception message I'm receiving (just the important parts):
org.apache.mina.filter.codec.ProtocolDecoderException: org.apache.mina.core.buffer.BufferDataException: dataLength: -2143812863 (Hexdump: 02 01 A2 02 01 A0 02)
at org.apache.mina.filter.codec.ProtocolCodecFilter.messageReceived(ProtocolCodecFilter.java:25
...
Caused by: org.apache.mina.core.buffer.BufferDataException: dataLength: -2143812863
at org.apache.mina.core.buffer.AbstractIoBuffer.prefixedDataAvailable(AbstractIoBuffer.java:2058)
at my.codec.in.folder.codec.MAFrameDecoder.doDecode(MAFrameDecoder.java:29)
at org.apache.mina.filter.codec.CumulativeProtocolDecoder.decode(CumulativeProtocolDecoder.java:178)
at org.apache.mina.filter.codec.ProtocolCodecFilter.messageReceived(ProtocolCodecFilter.java:241)
Sometimes the dataLength
is negative, sometimes positive (didn't find any clue about the cause of this).
The MAFrameDecoder:29 is the second sentence of my implementation of the CumulativeProtocolDecoder
's doDecode()
method (MAX_SIZE=4096):
public boolean doDecode(IoSession session, IoBuffer in, ProtocolDecoderOutput out)
throws Exception
{
boolean result=false;
if(in.prefixedDataAvailable(4, MAX_SIZE)) //-->This is line 29
{
int length = in.getInt();
byte[] idAndData = new byte[length];
in.get(idAndData);
//do things, read from buffer, create message, out.write, etc
//if all has been correct, result=true
}
return result;
}
While debugging the error through a TCP sniffer, we figured out that the exception was thrown when multiple messages where inserted in the same IoBuffer (in).
Seems like my Decoder
simply cannot handle multiple messages inside the same buffer. But as I told before, there's also the non-fixed length message issue ( which I really can't know if it has some relevance ). In other doDecode implementations I've seen another methods to manage the buffer, such as:
while (in.hasRemaining())
or
InputStream is=in.asInputStream();
Anyway, I'm trying to avoid blind steps, so this is why I'm asking this here. Instead of just fixing the error, I would like to know the reason of it.
Hope you can help me, any advice would be really appreciated. : )
p.s: The encoder that sends me the messages through the buffer has its autoExpand parameter in false.
EDIT 10/11/2014
I've been exploring the AbstractIoBuffer method and found out this:
@Override
public boolean prefixedDataAvailable(int prefixLength, int maxDataLength) {
if (remaining() < prefixLength) {
return false;
}
int dataLength;
switch (prefixLength) {
case 1:
dataLength = getUnsigned(position());
break;
case 2:
dataLength = getUnsignedShort(position());
break;
case 4:
dataLength = getInt(position());
break;
default:
throw new IllegalArgumentException("prefixLength: " + prefixLength);
}
if (dataLength < 0 || dataLength > maxDataLength) {
throw new BufferDataException("dataLength: " + dataLength);
}
return remaining() - prefixLength >= dataLength;
}
The prefixLength I'm sending is 4, so the switch enters on the last valid case:
dataLength = getInt(position());
After that, it throws the BufferDataException with the negative dataLength, which means the AbstractIoBuffer's position()
method is returning a negative value.
I always thought that a nioBuffer could never hold a negative value on its position parameter. Any clues of why is this happening?
I think you should try first reading the size of the packet you have to decode, and ensuring you have enough bytes remaining in the buffer for the decoding to complete successfully.
If there aren't enough bytes you should return false, so the cumulative protocol decoder can get more data for you.
Be careful to return the buffer to the appropriate position before returning the buffer, otherwise you will lose the length data for the next iteration. (If you are using 4 bytes for the length you should be rewinding 4 bytes).
Edit: You could actually use the mark()
and reset()
methods of the IoBuffer to achieve this behaviour