dev@grizzly.java.net

Re: Grizzly 2.0: Echo sample

From: Jeanfrancois Arcand <Jeanfrancois.Arcand_at_Sun.COM>
Date: Wed, 03 Sep 2008 13:50:17 -0400

Salut,

Oleksiy Stashok wrote:
>
> On Sep 3, 2008, at 17:10 , Jeanfrancois Arcand wrote:
>
>> 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.
> As for instance() I don't agree... Having static method
> TransportManager.createTransport() is not flexible, as doesn't let us to
> have custom TransportManager implementation.
> Having something like:
> TransportManager.instance().createTransport(Transport.TCP);
> looks like good idea :)

I'm still -1 :-) Or at least we need to rename TransportManagerFactory()
and have a getFactory() static method. I do think this is more a common
way of naming factory :-)


>
>>> // 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?
> I just think, it's more clear to have FilterChain, which consists of
> Filters, than ProtocolChain, consisting of ProtocolFilter. In first case
> names look more clear and shorter.

Hum...+0 Might be shorter, but might not tell/describe what they are
for. And that would help backward compatibility...but leave it as it is
for now, this is minor.

>
>
>>> 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.
> Really not sure which way is less error prone :))
> Basically TransportManager could create several Transports and if we
> close just one of them - it doesn't mean we need to release all the
> TransportManager resources (such as ThreadPool), because we have other
> Transports, which may use them.

But should calling transport.stop() should take care at least to destroy
its own resource? I think the example was unclear. You don't necessary
have to invoke the close() when you stop a transport, right?


>
>
>>> }
>>> }
>>> 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
> TransportFilter has wider logic. It knows how to read and write. So,
> IMHO, it makes sense to call it TransportFilter?

OK then :-)

>
>
>> 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.
> Sure, as I told each Transport could have own TransportFilter logic, so
> NIO.2 could have its own logic, which will be used by common
> TransportFilter.

Great.



>
>
>>> 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?
> For echo filter it doesn't matter, we just reply with the same sequence
> of byte, which was received.

But how do you construct the Message and how do you know all bytes has
been consumed from the client?


>
>
>>> /* 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.
> Yes. Connection.write(...) or FilterChain.write() are always blocking,
> for async. write there is different method set:
> Connection.writeAsync(...), FilterChain.writeAsync(...)

OK this is where we should explore the NIO.2 API.


>
>
>>> 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?
> Each Filter could override interceptFilterChainWrite(...) method, which
> is called on each Filter in a FilterChain, when
> FilterChain.write/writeAsync is called. So each Filter (like SSL) will
> be able to transform the original message before it will come to the
> TransportFilter.

OK.

>
>>
>>> 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.
> There is such an approach for readAsync/writeAsync methods of Connection
> and FilterChain, you can pass the CompletionHandler there. Read() and
> write() methods are always sync. and blocking.

OK.

>
>
>>> // 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 :-)
> Sure, I'm trying to write as much javadocs as possible. Once I'll push
> Grizzly2.0 to the maven - there will be JavaDoc generated.

Thanks!

>
>
>> 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.
> I'm 100% agree, we have to have some migration guide. But not sure
> keeping old names, with completely new implementation will help much.

OK let's start the Grizzly war on name :-) Note that I'm not the best
with name, except for Grizzly as project's name.

>
> Thank you for feedback!!!

Thanks!

-- Jeanfrancois

>
> WBR,
> Alexey.
>
>>
>>
>> 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
>>
>> ---------------------------------------------------------------------
>> To unsubscribe, e-mail: dev-unsubscribe_at_grizzly.dev.java.net
>> For additional commands, e-mail: dev-help_at_grizzly.dev.java.net
>>
>
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: dev-unsubscribe_at_grizzly.dev.java.net
> For additional commands, e-mail: dev-help_at_grizzly.dev.java.net
>