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 01:01:14 +0200

> Salut,
>
> Oleksiy Stashok wrote:
>> Hi
>>>> 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.
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);
................
Future readFuture = filterChain.readAsync();
HttpMessage response = (HttpMessage) readFuture.get();

IMHO, this will make client side very easy and clear.

>>> Also, the connet() operations returns A Future, where read/write
>>> return a Read/WriteResult. Should read/write return a Future as
>>> well? I would propose we unify the API so any async operations
>>> like connect/read/write return a Future. From the Future, we can
>>> return a Result, which contains the requested information.
>>> Something like:
>> connectAsync is async operation, that's why it returns Future.
>> read/write are blocking - that's why Result.
>> readAsync/writeAsync operations return Future<Result>.
>> So, IMHO, it's we don't have unification problems :)
>
> Yes, but we should not make any difference between blocking or non
> blocking. That will make things complicated and a long API (like we
> currently have in 1.8.x). The shorter API we can have the better we
> will be :-)

Grizzly 1.x has blocking, non-blocking features implemented in
different places: InputReader/OutputWriter, AsyncRead/Write queues.
In Grizzly 2.0 I want to have these features (at least basic part of
it) being implemented under Connection API. Though it will be still
possible to use TemporarySelectorIO.read/write and AsyncRead/Write
queue API separately.

But for me, having common IO features under single interface (like
Connection) looks easier.


>>> Result result = ConnectFuture.get()
>>> Connection connection = result.getResult()
>>>
>>> or
>>>
>>> Connection connection = ConnectionFuture.get(..) //No cast needed
>> that casting was redundant. Fixed.
>>>
>>> ReadResult result = ReadFuture.get();
>>> WriteResult resukt = WriteFuture.get();
>>>
>>> So your example seems to "suffer" 2 issues:
>>>
>>> + Need one single Object to interact with.
>> transport logic is inside Connection. And FilterChain just brings
>> processing logic.
>> The connection.read() in our example will return ByteBuffer, but
>> filterchain.read() will return String (as result of UTFStringFilter).
>
> 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.

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>


>>> + 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! :)

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
>