Hi Alexey,
We had internal project in company that got open sourced:
http://preon.sourceforge.net/
Its all about binary formats, quite similar to your stuff here, it even has
conditionals, so you can easily handle multiple versions.
Please take a look at it, as you may avoid righting something that is
already available.
Idea of having binary data described by annotations is great.
BTW Preon can also generate human readable documentation out of annotated
class (spec).
HTH,
Hubert.
On Tue, Dec 16, 2008 at 2:46 PM, Oleksiy Stashok <Oleksiy.Stashok_at_sun.com>wrote:
> 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 {*
> * **_at_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/
>
--
Hubert Iwaniuk