dev@grizzly.java.net

Re: Grizzly 2.0: Standalone filterchain processing example

From: Oleksiy Stashok <Oleksiy.Stashok_at_Sun.COM>
Date: Tue, 23 Sep 2008 20:25:18 +0200

>>>>>>>> public static final int PORT = 7778;
>>>>>>>> public void testStandaloneFilter() throws Exception {
>>>>>>>> Connection connection = null;
>>>>>>>> TCPNIOTransport transport =
>>>>>>>> TransportManager.instance().createTCPTransport();
>>>>>>>> transport.getFilterChain().add(new TransportFilter());
>>>>>>>> transport.getFilterChain().add(new UTFStringFilter());
>>>>>>>> transport.getFilterChain().add(new EchoFilter());
>>>>>>>
>>>>>>> We still need to discuss the getFilterChain() names ;-)
>>>>>> :) ok
>>>>>>>> try {
>>>>>>>> transport.bind(PORT);
>>>>>>>> transport.start();
>>>>>>>> ConnectFuture future =
>>>>>>>> transport.connectAsync("localhost", PORT);
>>>>>>>> connection = (TCPNIOConnection) future.get(10,
>>>>>>>> TimeUnit.SECONDS);
>>>>>>>> assertTrue(connection != null);
>>>>>>>> String message = "Hello world!";
>>>>>>>> FilterChain filterChain = transport.getFilterChain();
>>>>>>>> WriteResult result = filterChain.write(connection,
>>>>>>>> message);
>>>>>>>> assertEquals(result.getMessage(), message);
>>>>>>>> ReadResult readResult = filterChain.read(connection);
>>>>>>>
>>>>>>> OK I'm not sure about this example. It seems we first works
>>>>>>> with the Transport object and then switch to the FilterChain.
>>>>>>> If the FilterChain handler the read/write logic, why not the
>>>>>>> connectAsync be on the FilterChain as well?
>>>>>> Well, I'm not trying to move transport logic to filterchain.
>>>>>> I'm just showing how it's possible to use filterchain in
>>>>>> standalone mode.
>>>>>> How data could be read from the connection, pass the
>>>>>> filterchain and finally we will have filterchain processed
>>>>>> message (String in our case).
>>>>>
>>>>> That's still confusing IMO. We should really have a single point
>>>>> of entry.
>>>> But filterchain read/write should not be entry point. It's just
>>>> feature.
>>>> Which, IMHO, could be very useful for client side.
>>>
>>> I agree, but we seems to have many doors to enter the same room.
>>> What I'm trying to say is we should reduce the number of doors :-)
>> Well, I don't agree it's the same room :)) Filterchain read is just
>> feature, which could be especially useful for client side code.
>
> Agree...but that API should be on Transport IMO, not on
> FilterChain ;-)
Transport knows just ByteBuffer or byte[], but Transport doesn't know
about higher level protocol. FilterChain is exactly the place, where
higher level protocol logic is, so why not? :)


>>>> On example I showed - we use the filterchain both on server and
>>>> client sides. But client side uses it in standalone mode.
>>>> It is just String example. But, if make, let's say, HttpFilter,
>>>> which will work with HttpMessage, it will mean client side could
>>>> be as simple as:
>>>> Future writeFuture = filterChain.writeAsync(httpMessage);
>>>
>>> Still not convinced that we need readSync/Async method. That will
>>> makes the API way too long (like the 1.8.x Context API...way too
>>> long IMO).
>> I'm looking at this, just as 3 sets of I/O operations:
>> readNow/writeNow
>> readAsync/writeAsync
>> read/write
>
> I disagree. I would like to see read/write and a boolean to blocking/
> non-blocking. This is just too much and too complicated to code
> against. Why do you have 6 methods instead of 2 :-)
IMHO boolean looks bad.
write(buffer, true) vs. writeAsync(buffer)
I will prefer 2nd ;)


>> which, IMHO, is not too much :)
>>>> ................
>>>> Future readFuture = filterChain.readAsync();
>>>> HttpMessage response = (HttpMessage) readFuture.get();
>>>> IMHO, this will make client side very easy and clear.
>>>
>>> +1 For the Future :-) -1 for the readFuture cast (or at least we
>>> should have a Message API).
>> ReadFuture is just subclass of Feature with predefined result:
>> ReadResult.
>
> Yes but why do we need the cast? Implementation wise I suspect we
> can get rid of it.
I'm trying to rid of casting where it's possible. And I agree, that
there should be as less castings as possible.

>>>>>
>>>>> Hum...I need to look at it more. I'm not sure as how can you
>>>>> know which filter will return what? Meaning something like:
>>>>>
>>>>> transport.getFilterChain().add(new TransportFilter());
>>>>> transport.getFilterChain().add(new UTFStringFilter());
>>>>> transport.getFilterChain().add(new LogFilter());
>>>>> transport.getFilterChain().add(new EchoFilter());
>>>>>
>>>>> How can I predict that readResult.getMessage() is coming from
>>>>> which Filter?
>>>> The filterchain.read() processes the chain in regular manner from
>>>> the first to the last. And the result message will be returned.
>>>> In your example, just UTFStringFilter makes some message
>>>> transformation, looks like other filters will just process the
>>>> message (not transform). So String will be returned.
>>>> Example like this:
>>>> transport.getFilterChain().add(new TransportFilter());
>>>> transport.getFilterChain().add(new SSLFilter());
>>>> transport.getFilterChain().add(new LogFilter());
>>>> transport.getFilterChain().add(new EchoFilter());
>>>> ByteBuffer message = (ByteBuffer) filterChain.read().getMessage();
>>>> will return ByteBuffer, the result of SSLFilter decoding. This
>>>> way we can implement SSL clients.
>>>
>>>
>>> But how can the client knows that? How can I change my mind and
>>> instead get the LogFilter results instead of SSLFilter result?
>> Well, you build the FilterChain... you should know :)
>
> Naaa ;-) The FilterChain in 1.x doesn't have that "feature" :-)
I know :)


>> Usually the filter chain represents some ecoding/decoding +
>> processing logics. And usually one single Filter implements either
>> encoding/decoding or processing logic.
>> In your example encoding/decoding is implemented by: SSLFilter.
>> Other filters represent some processing logic: logging, echoing.
>
> If you make such difference between filters, then we should split
> the concept in two:
>
> Decoder/Encoder
> Processor
>
> because with your example it is too complex IMO to know what the
> getMessage() will return. So I'm sticking to my -1 :-) What do you
> think about adding Decoded/Encoder concept, and move them out the
> the FilterChain? We will start looking like MINA ;-)
Well, we always need to improve our API, if MINA has good ideas - why
not :))
Split the Decoder/Encoder and Processor Filters API could be the next
step. We need to collect more feedback on this.


>> The standalone filterchain read/write reuses just encoding/decoding
>> logic of filterchain, which IMHO, could be very interesting
>> (especially for the client side).
>
> But you will cache the getMessages() somewhere, right? In case on
> byte buffer, I'm not sure this is a good idea, unless I've
> misunderstood your example.
It's not required and depends on the usecase.
If I want to construct my CustomReques and send it:

CustomRequest request = constructRequest();
filterChain.writeAsync(customRequest);

and then receive response:

ReadFuture future = filterChain.readAsync();
CustomResponse response = future.get();
processResponse(response);

I don't need to cache something here.


>> Example I showed above with HttpMessage looks interesting, doesn't
>> it? :)
>
> Not yet convinced ;-) HttpMessage will contains what? The header and
> the body? Aille! ;-)
Yep, parsed HTTP header and body :)

Thanks.

WBR,
Alexey.


>
>
> A+
>
> -- Jeanfrancois
>
>> This way Grizzly HTTP client implementation could be very clear and
>> easy.
>>>> the following example:
>>>> transport.getFilterChain().add(new TransportFilter());
>>>> transport.getFilterChain().add(new SSLFilter());
>>>> transport.getFilterChain().add(new HttpFilter());
>>>> transport.getFilterChain().add(new LogFilter());
>>>> transport.getFilterChain().add(new EchoFilter());
>>>> HttpMessage message = (HttpMessage)
>>>> filterChain.read().getMessage();
>>>> will return HttpMessage, the result of HttpFilter, which is
>>>> invoked after SSLFilter. And you see how we can implement HTTPS
>>>> client.
>>>>> [Small thing] -> ReadResult should know what will be returned by
>>>>> doing getMessage(), hence no need to cast IMO.
>>>> In general not. But it's possible to customize the message type
>>>> using ReadResult generics. ReadResult<HttpMessage, SocketAddress>
>>>
>>> +1
>>>
>>>>>>> + Unify API for async operations.
>>>>>> it's not problem :)
>>>>>>>> String responseMessage = (String)
>>>>>>>> readResult.getMessage();
>>>>>>>> assertEquals(message, responseMessage);
>>>>>>>> } finally {
>>>>>>>> if (connection != null) {
>>>>>>>> connection.close();
>>>>>>>> }
>>>>>>>> transport.stop();
>>>>>>>> TransportManager.instance().close();
>>>>>>>
>>>>>>> I still find it dangerous to have 2 operations to execute
>>>>>>> here :-) But maybe it is only me :-)
>>>>>> No, no, you may be right. Just didn't time to revisit this.
>>>>>
>>>>> I think we should start filling issue on 2.0 to not loose track.
>>>>> So far we are only two discussing, but when every body is up to
>>>>> speed we will loose content :-). What do you think?
>>>> I agree! :)
>>>
>>> OK let's agree first and I will file after :-)
>> Sure :)
>> Thank you for feedback!
>> WBR,
>> Alexey.
>>>
>>>
>>> Great work so far!
>>>
>>> A+
>>>
>>> -- Jeanfranncois
>>>
>>>
>>>> Thanks.
>>>> WBR,
>>>> Alexey.
>>>>>
>>>>>
>>>>> A+
>>>>>
>>>>> -- Jeanfrancois
>>>>>
>>>>>
>>>>>> Thanks!
>>>>>> WBR,
>>>>>> Alexey.
>>>>>>>
>>>>>>>
>>>>>>> A+
>>>>>>>
>>>>>>> -- Jeanfrancois
>>>>>>>
>>>>>>>
>>>>>>>
>>>>>>>> }
>>>>>>>> }
>>>>>>>> }
>>>>>>>> ---------------------------------------------------------------------
>>>>>>>> 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
>>>>>
>>>>> ---------------------------------------------------------------------
>>>>> 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
>>>
>> ---------------------------------------------------------------------
>> 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
>