users@grizzly.java.net

Re: XML Input over TCP with Grizzly

From: Erik Svensson <erik_at_phlogiston.se>
Date: Thu, 20 Mar 2008 21:21:58 +0100

> Hi,
> I am using Grizzly for an application at work. The goal is to
> accept an
> XML message over a TCP socket and then pass it on to another part
> of the
> application. The problem I am having is that my XML message gets split
> up if the XML message isn't super small. The test message I am
> using is
> 26 kb and gets split up into several pieces. I've read that I can use
> context to keep track of information for further processing. And I was
> planning on using SAX to get an event for the end of the document so I
> would know when it has finished. I'm not set on this solution, but I
> can't seem to figure out how to do this. Any advice will be much
> appreciated.

This has been a bit of a topic lately.
The thread ' Need help implementing ProtocolParser' was about this.
I'm attaching a protocol parser that seems to be able to handle
messages spread out over several reads ('seems to' because
I've only tested it with a few test cases). It uses Oleksiys idea of
allocating a new byte buffer and attaching it to the current
worker thread.

Anyhoo, here's the code:

package se.phlogiston.grizzly.overflow;

import se.phlogiston.grizzly.tutorial2.SimpleMessage;

import java.nio.ByteBuffer;

import com.sun.grizzly.util.ByteBufferFactory;
import com.sun.grizzly.util.WorkerThread;

/**
  * Created by IntelliJ IDEA.
  * User: erik
  * Date: Mar 14, 2008
  * Time: 10:05:24 PM
  * To change this template use File | Settings | File Templates.
  */
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() {
     System.out.println("Creating a new ProtocolParser");
   }

   public boolean isExpectingMoreData() {
     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() {
     if (wants_more_data) {
       saved.compact();
       // check to see if we made room in the buffer...
       // compact sets the position at the end of the written/moved
bytes so remaining()
       // shows how much more we can stuff into the buffer.
       if (saved.remaining() == 0 ) {
         System.out.println("No space left in the buffer. Extendening
the buffer.");
         reallocateSaved();
       }
     } else {
       saved.clear();
     }
     // Grizzly doesn't seem to care (and doesn't when you look at
the code) but we return a factual value anyhoo
     return wants_more_data;
   }


   private void reallocateSaved() {
     saved.position(position);
     ByteBuffer tmpBuffer =
             ByteBufferFactory.allocateView(saved.capacity() * 2,
false);
     System.out.println("reallocateBuffer newly created : pos :
"+tmpBuffer.position()+" limit: "+tmpBuffer.limit()+"" +" cap: "+
             tmpBuffer.capacity());

     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);

     if (message == null) {
       // not enough bytes for a message
       // but we know there are bytes so there must be
       // an incomplete message there
       if (saved.position() != position) {
         saved.position(position);
       }
       wants_more_data = true;
       has_unparsed_data = false;
     } else {
       // here we have a complete message
       if (saved.limit() == saved.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