dev@grizzly.java.net

Re: Grizzly 2.0: Echo sample

From: Jeanfrancois Arcand <Jeanfrancois.Arcand_at_Sun.COM>
Date: Wed, 03 Sep 2008 11:10:08 -0400

Salut,

Oleksiy Stashok wrote:
> Hi,
>
> wanted to share the simple Grizzly 2.0 sample (echo sample) and will
> very appreciate the feedback or better contributions :) to the proposed
> design.
> The complete sources are attached, though to orient better I'll briefly
> describe what is what :)
>
> Let's start from the server.
> 1)
> TCPNIOTransport transport =
> TransportManager.instance().createTCPTransport();

+0 on the instance() method :-) We discussed that a while ago, but I
would think we need to have something similar to the Controller, and get
Transport from the Controller.

Now what I don't like is also (we have the same problem with 1.8 right
now) is the fact that you have the TCP named in the method name. I would
recommend something like:


createTransport(Transport.TCP)

instead.


>
> // Add TransportFilter, which is responsible
> // for reading and writing data to the connection
> transport.getFilterChain().add(new TransportFilter());
> transport.getFilterChain().add(new EchoFilter());

For backward compatibility we can probably still use getProtocolChain().
Do you see any problem?


>
> try {
> // binding transport to start listen on certain host and port
> transport.bind(HOST, PORT);
>
> // start the transport
> transport.start();
>
> System.out.println("Press any key to stop the server...");
> System.in.read();
> } finally {
> // stop the transport
> transport.stop();
>
> // release TransportManager resources like ThreadPool
> TransportManager.instance().close();

Hum :-) Can we find a way to avoid such extra method call:

TransportManager.instance().close()

Instead, can we do something like:


Transport transport = createTransport(Transport.TCP);
TransportManager.addListener(transport,TransportManager.CLOSE);

SO when transport.close() is called, the TransportManager is
automatically invoked. That will be less error prone IMO.



> }
> }
>
> here we just initialize the TCP transport, adding 2 filters:
> TransportFilter and EchoFilter, binding the server to the specific host
> and port and... that's it :)
> It's similar to what we have in 1.x, except may be new term
> TransportFilter.
> TransportFilter is very similar to ReadFilter from Grizzly 1.x, so it
> knows how to read/write the data depending on the transport.

We need to decide if we keep the ProtocolFilter name. I would think
ProtocolFilter defined for 1.8 can still work with 2.0


Each
> transport can implement its own read/write logic, in this case
> TransportFilter will silently forward the control the the specific
> implementation. In our case TCPNIOTransport has own read/write logic
> implementation, which is hidden from us, and TransportFilter uses it in
> order to read/write the bytes on the filter chain.

OK that sound interesting. Now we need to take into account NIO.2 here.
So we probably need to have TCPAIOTransport as well. Switching from
NIO.1 to NIO.2 should be transparent IMO to the user.


>
> 2) How does the EchoFilter look like.
>
> /**
> * Handle just read operation, when some message has come and ready
> to be
> * processed.
> *
> * @param ctx Context of {_at_link FilterChainContext} processing
> * @param nextAction default {_at_link NextAction} filter chain will
> execute
> * after processing this {_at_link Filter}. Could be
> modified.
> * @return the next action
> * @throws java.io.IOException
> */
> @Override
> public NextAction handleRead(FilterChainContext ctx, NextAction
> nextAction)
> throws IOException {
> // Get the read message
> Object message = ctx.getMessage();

Hum...how do you know you have a complete/incomplete message?


>
> /* Send the same message on the connection. The filter chain write
> * will pass each filter on a filter chain (interceptWrite method),
> * before sending the message on a wire. It means each filter
> can modify
> * the message, before it will be sent to the recipient
> */
> ctx.getFilterChain().write(ctx.getConnection(), message);

Here the write operation will be blocking? We probably need some
WriteSessionListener here to get some cue about what's happenning with
the write operation.


>
> return nextAction;
> }
>
> common interface for all filters is Filter, which if very similar to the
> ProtocolFilter from Grizzly 1.x, but, IMHO, it's more suitable to extend
> FilterAdapter class, which lets you separate the logic. For example in
> case of EchoFilter we just want to handle read operation and not
> interested in others.
> You can note how we write the response, using the filter chain, not to
> the connection directly. It means that all the Filters in the chain can
> intercept the write operation and transform the writing buffer before
> TransportFilter will put it on a wire. In our case it looks redundant,
> because we don't have any Filter, which could be interested in
> transforming the response, and "ctx.getConnection().write(message);"
> could be used instead. But as common solution filterchain.write is better.

Can you elaborates more here? I'm not sure I understand properly. How
will you know a Filter is interested in write operations?


>
>
> 3) Client
>
> Connection connection = null;
>
> // Create the TCP transport
> TCPNIOTransport transport = TransportManager.instance().
> createTCPTransport();

Same as above :-) -1 for the instance() :-)



>
> try {
> // start the transport
> transport.start();
>
> // perform async. connect to the server
> ConnectFuture future = transport.connectAsync(EchoServer.HOST,
> EchoServer.PORT);
> // wait for connect operation to complete
> connection = (TCPNIOConnection) future.get(10,
> TimeUnit.SECONDS);
>
> assert connection != null;
>
> // create the message
> ByteBuffer message = ByteBuffer.wrap("Echo test".getBytes());
> // sync. write the complete message using
> // temporary selectors if required
> WriteResult result = connection.write(message);
>
> assert result.getWrittenSize() == message.capacity();

Hum :-) If the message has not been fully written,
result.getWrittenSize() will return the wrong value. I would instead
change your write() signature and have something like NIO.2:

write(msg, CompletionHandler);

http://openjdk.java.net/projects/nio/javadoc/java/nio/channels/CompletionHandler.html

And follow a similar approach for read and write operations.


>
> // allocate the buffer for receiving bytes
> ByteBuffer receiverBuffer =
> ByteBuffer.allocate(message.capacity());
>
> ReadResult readResult = null;
> // read the same number of bytes as we sent before
> while (receiverBuffer.hasRemaining() &&
> (readResult == null || readResult.getReadSize() > 0)) {
> readResult = connection.read(receiverBuffer);
> }
>
> // check the result
> assert message.flip().equals(receiverBuffer.flip());
> } finally {
> // close the client connection
> if (connection != null) {
> connection.close();
> }
>
> // stop the transport
> transport.stop();
> // release TransportManager resources like ThreadPool etc.
> TransportManager.instance().close();
> }
>
> it does similar things as server to initialize/release the transport and
> TransportManager.
> Also you can find, how client is getting connected to the server, writes
> and reads data.
> Want to note, that currently there is no async read and write
> implementations in Grizzly 2.0 (it will be ready soon), so all reads and
> writes are working sync., using temporary selectors if required.

OK this is a great start. I haven't looked at the code yet....what I
recommend is first document the 2.0 core classes and generate the
JavaDoc API...that will be a really good startup for review during our
weekly meeting. Looking at the code directly will makes the task to
harder for external people (nobody except a couple of us will have time
to look at the implementation). So I recommend you javadoc the new
monster :-)

Also we must have in mind that we may have to be backward compatible
with some concept (like ProtocolFIlter/Chain). I say we might :-) We
should dress a list and probably vote. We have to keep in mind that
customer using 1.8 might not have time to re-write their application,
and we don't want to loose them for another framework in case they don't
like our 2.0 design.

Thanks

-- Jeanfrancois


>
> Will appreciate your feedback.
>
> Thanks.
>
> WBR,
> Alexey.
>
>
> ------------------------------------------------------------------------
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: dev-unsubscribe_at_grizzly.dev.java.net
> For additional commands, e-mail: dev-help_at_grizzly.dev.java.net