users@grizzly.java.net

Re: Need help implementing ProtocolParser

From: Oleksiy Stashok <Oleksiy.Stashok_at_Sun.COM>
Date: Tue, 18 Mar 2008 16:57:24 +0100

Hello guys,

seems currently we have bug in Grizzly, which doesn't let you to rely
on fact, that the same ProtocolParser will be called, when next
request bytes will come.
Can you please file the bug on this?

As workaround, I can propose you to not keep retain_buffer in your
ProtocolParser.
This change will make your ProtocolParser stateless, and not depend on
fact, that same instance will be called for next request on the same
channel.

The idea is to use just single buffer (saved). Please take a look at
code bellow. I didn't test it, as don't have your testcase, but you
can see the idea there.
If you will need more help - please send your complete testcase code,
so I'll be able to run it.

Thanks.

WBR,
Alexey.

public class ProtocolParser implements
         com.sun.grizzly.ProtocolParser<SimpleMessage> {

     private ByteBuffer saved;
     private int position;
     private int limit;
     private SimpleMessage mess;
     private boolean wants_more_data = false;
     private boolean has_unparsed_data = false;

     public ProtocolParser() {
     }

     public boolean isExpectingMoreData() {
         System.out.println("isExpectingMoreData : " + wants_more_data);
         return wants_more_data;
     }

     public boolean hasMoreBytesToParse() {
         return has_unparsed_data;
     }

     public SimpleMessage getNextMessage() {
         // the call pattern should be:
         // hasNextMessage() == true
         // getNextMessage()
         // but if not:
         if (null == mess) {
             mess = extractMessage();
         }
         SimpleMessage tmp = mess;
         mess = null;

         return tmp;
     }

     public boolean hasNextMessage() {

         if (null == mess) {
             mess = extractMessage();
         }

         return (mess != null);
     }

     public void startBuffer(ByteBuffer bb) {
         saved = bb;
         saved.flip(); // flip the byte buffer for reading. (should
have been done already)
     }

     public boolean releaseBuffer() {
         System.out.println("releaseBuffer : ");
         if (wants_more_data) {
             System.out.println("releaseBuffer : Doing compact");
             // If saved buffer is full and still need to read more data
             if (saved.remaining() == saved.capacity()) {
                 reallocateSaved();
             }
         } else {
             saved.clear();
         }
         return wants_more_data;
     }

     private void reallocateSaved() {
         ByteBuffer tmpBuffer =
ByteBufferFactory.allocateView(saved.capacity() * 2, false);
         tmpBuffer.put(saved);
         saved = tmpBuffer;
         ((WorkerThread) Thread.currentThread()).setByteBuffer(saved);
     }

     private SimpleMessage extractMessage() {
         SimpleMessage message = null;

         if (!saved.hasRemaining()) {
             // there are no more readable bytes in the buffer
             // this should mean that we've read all messages in the
buffer
             wants_more_data = false;
             has_unparsed_data = false;
             saved.clear(); // clean up our byte buffer. No one else
will do it for us
             return message;
         }

         // save the position before we send it off to the parser
         position = saved.position();
         message = SimpleMessage.parse(saved);
         System.out.println("parsed message is null ? " + (message ==
null ? "YES"
                 : "NO"));
         if (message == null) {
             // not enough bytes for a message
             // but we know there are bytes so there must be
             // an incomplete message there
             saved.position(position);
             wants_more_data = true;
             has_unparsed_data = false;
         } else {
             wants_more_data = false;
             has_unparsed_data = saved.hasRemaining();
             if (has_unparsed_data) {
                 saved.compact();
                 saved.flip();
             } else {
                 saved.clear();
             }
         }
         return message;
     }


On Mar 18, 2008, at 10:57 , Erik Svensson wrote:

> Howdy all!
>
> I've implemented a protocol parser using Ash2K:s case as a template
> and I
> get the same result. I send a message of somehting like 15k bytes.
> I save the buffer, return wants_more_data = true and I expect the same
> parser to be called again but instead a new one is created and so I
> lose my
> saved buffer.
>
> Running 1.7.2 with the debugger I notice that the return value from
> releaseBuffer() is ignored.
>
> Here's my protocol parser:
>
>
> public class ProtocolParser implements
> com.sun.grizzly.ProtocolParser<SimpleMessage> {
>
> private ByteBuffer saved,retain_buffer;
> private int position;
> private int limit;
> private SimpleMessage mess;
> private boolean wants_more_data = false;
> private boolean has_unparsed_data = false;
>
> public ProtocolParser() {
> System.out.println("Creating a new ProtocolParser");
> retain_buffer = ByteBuffer.allocateDirect(18000);
> }
>
> public boolean isExpectingMoreData() {
> System.out.println("isExpectingMoreData : "+wants_more_data);
> return wants_more_data;
> }
>
> public boolean hasMoreBytesToParse() {
> return has_unparsed_data;
> }
>
> public SimpleMessage getNextMessage() {
> // the call pattern should be:
> // hasNextMessage() == true
> // getNextMessage()
> // but if not:
> if (null == mess) {
> mess = extractMessage();
> }
> SimpleMessage tmp = mess;
> mess = null;
>
> return tmp;
> }
>
> public boolean hasNextMessage() {
>
> if (null == mess) {
> mess = extractMessage();
> }
>
> return ( mess != null);
> }
>
> public void startBuffer(ByteBuffer bb) {
> saved = bb;
> saved.flip(); // flip the byte buffer for reading. (should have
> been
> done already)
>
> if ((saved.remaining() > retain_buffer.remaining())) {
> // grow the retain buffer...
> System.out.println("Grow retain buffer not implemented");
> return;
> }
> retain_buffer.put(saved);
> retain_buffer.flip();
> bb.clear();
> }
>
> public boolean releaseBuffer() {
> System.out.println("releaseBuffer : ");
> if (wants_more_data) {
> System.out.println("releaseBuffer : Doing compact");
> retain_buffer.compact();
> } else {
> retain_buffer.clear();
> }
> return wants_more_data;
> }
>
> private SimpleMessage extractMessage() {
> SimpleMessage message = null;
>
> if (!retain_buffer.hasRemaining()) {
> // there are no more readable bytes in the buffer
> // this should mean that we've read all messages in the buffer
> wants_more_data = false;
> has_unparsed_data = false;
> saved.clear(); // clean up our byte buffer. No one else will do
> it for
> us
> return message;
> }
> // save the position before we send it off to the parser
> position = retain_buffer.position();
> message = SimpleMessage.parse(retain_buffer);
> System.out.println("parsed message is null ? "+(message == null ?
> "YES"
> : "NO"));
> if (message == null) {
> // not enough bytes for a message
> // but we know there are bytes so there must be
> // an incomplete message there
> if (retain_buffer.position() != position) {
> retain_buffer.position(position);
> }
> wants_more_data = true;
> has_unparsed_data = false;
> } else {
> // here we have a complete message
> if (retain_buffer.limit() == retain_buffer.position()) {
> // the end of the buffer is reached.
> wants_more_data = false;
> has_unparsed_data = false;
> } else {
> has_unparsed_data = true;
> // here we can't accuratly set wants_more_data.
> // since we don't know if the bytes in the buffer is a
> // complete message or not
> }
> }
> return message;
> }
> }
>
> cheers
> /Erik
>
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: users-unsubscribe_at_grizzly.dev.java.net
> For additional commands, e-mail: users-help_at_grizzly.dev.java.net
>