dev@grizzly.java.net

Grizzly 2.0: Smart codec

From: Oleksiy Stashok <Oleksiy.Stashok_at_Sun.COM>
Date: Tue, 16 Dec 2008 14:46:07 +0100

Hi,

I'd like to receive some feedback on feature we've just added to
Grizzly 2.0 workspace, named smart codec (smart filter or smart
transformer) :)
I called it "smart", because using this codec we don't need to write
any parsers for custom messages. The codec analyzes class structure
and performs encoding/decoding according to the type structure. There
are some annotations, which could be used in order to change the
default transformation logic.

How it looks like?

1) First of all we define our custom message class. As example I'll
take GIOP message:

public class GIOPMessage {
     private byte G;
     private byte I;
     private byte O;
     private byte P;

     private byte major;
     private byte minor;

     private byte flags;
     private byte value;

     private int bodyLength;

     @Sequence(
         size=_at_Size(ref="bodyLength"),
         limit=_at_Limit(8192)
     )
     private byte[] body;

     public GIOPMessage() {
     }

     public GIOPMessage(byte major, byte minor,
             byte flags, byte value, byte[] body) {
         G = 'G';
         I = 'I';
         O = 'O';
         P = 'P';

         this.major = major;
         this.minor = minor;
         this.flags = flags;
         this.value = value;

         bodyLength = body.length;
         this.body = body;
     }
}

GIOP message contains of header (12 bytes) and body.
The interesting part here, is how we define body byte array. We use
annotation @Sequence, and define, that body length is located in
"bodyLength" field of the same object, and maximum body size is 8192.

2) Server part....

         // Create TCP NIO transport
         TCPNIOTransport transport =
TransportFactory.getInstance().createTCPTransport();

         // Initialize smart Codec
         SmartCodec smartCodec = new SmartCodec(GIOPMessage.class);

         // Add filters to the chain
         transport.getFilterChain().add(new TransportFilter());
         transport.getFilterChain().add(new SmartFilter(smartCodec));

        // GIOPProcessorFilter works with GIOPMessage, not with Buffers
         transport.getFilterChain().add(new GIOPProcessorFilter());


         try {
             // Bind server socket and start transport
             transport.bind(PORT);
             transport.start();

             System.out.println("Press <enter> to exit...");
             System.in.read();
         } finally {
             transport.stop();
             TransportFactory.getInstance().close();
         }
     }

That's it. So, your work will be just write GIOPProcessorFilter, which
knows what to do with GIOP message, and don't bother with possible
packet fragmentation, ByteBuffers or other things.

3) Client part....

         Connection connection = null;
         // Create TCP NIO transport
         TCPNIOTransport transport =
TransportFactory.getInstance().createTCPTransport();

         // Initialize smart Codec
         SmartCodec smartCodec = new SmartCodec(GIOPMessage.class);

         try {
             // start transport
             transport.start();

             // Connect client to the GIOP server
             ConnectFuture future = transport.connect(GIOPServer.HOST,
                     GIOPServer.PORT);

             connection = future.get(10, TimeUnit.SECONDS);

             // Enable standalone mode for this connection
             // (don't use Filter chains or other I/O event processors)
             connection.setPreferableProcessorSelector(new
NullProcessorSelector());

             // Initialize sample GIOP message
             byte[] testMessage = new String("GIOP test").getBytes();
             GIOPMessage sentMessage = new GIOPMessage((byte) 1,
(byte) 2,
                     (byte) 0x0F, (byte) 0, testMessage);

             // Write message
             Future<WriteResult> writeFuture =
                     connection.write(sentMessage,
smartCodec.getEncoder());

             writeFuture.get(10, TimeUnit.SECONDS);

.....................................................................

             // Receive result back
             Future<ReadResult> readFuture = connection.read(null,
                     smartCodec.getDecoder());
             ReadResult result = readFuture.get(10, TimeUnit.SECONDS);
             GIOPMessage reply = (GIOPMessage) result.getMessage();
.....................................

Here [1] you can find working code of simple GIOP client and GIOP echo
server.

Smart codec is able to work with complex message types, which have not
just primitives in it. For example:
public class MyMessage {
        MessageHeader header;
         MessageBody body;
}

public class MessageHeader {
        private int id;
         private int seq;
}

public class MessageBody {
        private int length;
        @Sequence(size=_at_Size(ref="length"))
        private byte[] body;
}


If you have some message, or message member, which encoding/decoding
you want to customize, you can set the custom Transformer, which will
be responsible for encoding it.

public class CustomizedMessage {
        @Encoder("my.own.header.Encoder")
         @Decoder("my.own.header,Decoder")
        CustomizedHeader header;

         NonCustomizedBody body;
}

Will appreciate your feedback!

Thanks.

WBR,
Alexey.


[1] https://grizzly.dev.java.net/source/browse/grizzly/branches/2dot0/samples/framework-samples/src/main/java/org/glassfish/grizzly/samples/smart/giop/