users@grizzly.java.net

Re: Should I use Context to store the clients references ?

From: Oleksiy Stashok <Oleksiy.Stashok_at_Sun.COM>
Date: Tue, 15 Jul 2008 16:52:43 +0200

Thanks for sources.
In your implementation you define ClientConnectionHandler to represent
one single client connection during its life-cycle.
Probably in this case I would suggest to use attachments
(ThreadAttachment).
Here what I'm proposing...

     private ClientConnectionHandler retrieveConnectionHandler(Context
context) {
        ClientConnectionHandler clientConnectionHandler = null;

                AttributeHolder connectionAttrs =
ctx.getAttributeHolderByScope(AttributeScope.CONNECTION);
        if (connectionAttrs != null) {
                clientConnectionHandler = (ClientConnectionHandler)
connectionAttrs.getAttribute(CLIENT_CONNECTION_HANDLER_ATTR);
        } else {
                WorkerThread workerThread = (WorkerThread)
Thread.currentThread(); // Detach the current Thread data.
                connectionAttrs = workerThread.getAttachment();
                // Attach it to the SelectionKey so the it can be resumed latter.
                key.attach(connectionAttrs);
        }
        
        if (clientConnectionHandler == null) {
                clientConnectionHandler = new ClientConnectionHandler(manager,
context.getSelectionKey(), context.getSelectorHandler());
                connectionAttrs.setAttribute(CLIENT_CONNECTION_HANDLER_ATTR,
clientConnectionHandler);
        }

        return clientConnectionHandler;
     }

this could be the way how to get ClientConnectionHandler associated
with the specific connection or create new if required.
See complete QuoteQueryManagerFilter source.

Not sure this will work as it is... as I didn't try to compile it -
but hope it's clear enough to understand what I meant :)

Thanks.

WBR,
Alexey.

package org.sample.nio.connection.handler.filter;

import java.io.IOException;
import java.net.SocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.DatagramChannel;
import java.nio.channels.SelectableChannel;
import java.nio.channels.SelectionKey;
import java.util.logging.Level;

import org.sample.nio.QuoteManager;
import org.sample.nio.connection.handler.ClientConnectionHandler;

import com.sun.grizzly.Context;
import com.sun.grizzly.Controller;
import com.sun.grizzly.ProtocolFilter;
import com.sun.grizzly.ProtocolParser;
import com.sun.grizzly.SelectorHandler;
import com.sun.grizzly.TCPSelectorHandler;
import com.sun.grizzly.filter.ReadFilter;
import com.sun.grizzly.util.OutputWriter;

public class QuoteQueryManagerFilter implements ProtocolFilter {
     private static final String CLIENT_CONNECTION_HANDLER_ATTR =
"connection-handler";
        
     private QuoteManager manager;

     public QuoteQueryManagerFilter(QuoteManager manager) {
         this.manager = manager;
     }

     public boolean execute(Context context) throws IOException {
         String query = (String)
context.removeAttribute(ProtocolParser.MESSAGE);

         if(query==null || query.trim().length()==0){
                 return false;
         }

         System.out.println("query = " + query);

         //here should be the parsed query command string with
         // which query should be able to create a command
         // I have no time to rewrite ClientConnectionHandler
         // Basically you have in context a link to client connection
         //with that and for example TCPSelectorHandler your commands
can
         //send data to client

         /*
          * For now instead of keeping whole Context, you can just
keep SelectionKey as reference to the client,
          * and send response back using:
selectorHandler.getAsyncQueueWriter().write(SelectionKey, ...);
          */

         // on va chercher le ClientConnectionHandler si il existe

         ClientConnectionHandler clientConnectionHandler =
retrieveConnectionHandler(context);

         manager.processQuery(clientConnectionHandler, query);


// // Depending on protocol perform echo
// //SelectableChannel channel =
context.getSelectionKey().channel();
// try {
//
// ByteBuffer writeBuffer =
ByteBuffer.allocateDirect(query.length());
//
// writeBuffer.put(query.getBytes());
//
// writeBuffer.flip();
//
// SelectionKey key = context.getSelectionKey();
//
// SelectorHandler selectorHandler = context.getSelectorHandler();
//
//
selectorHandler.getAsyncQueueWriter().write(key,writeBuffer);
//
// /*
// if (context.getProtocol() == Controller.Protocol.TCP) { // TCP
case
// OutputWriter.flushChannel(channel, writeBuffer);
// }
// */
// //
context.getAsyncQueueWritable().writeToAsyncQueue(writeBuffer, null,
null, true);
// } catch (IOException ex) {
// throw ex;
// }

         /*
          * The former will block until all the bytes are written.
When I say block,
          * I mean the calling Thread will block. The OutputWriter
internally use a pool of
          * temporary Selector to make sure writes are always executed
entirely.

                With async queue write (the later), the calling thread will not
block. instead,
                the async queue mechanism will make sure your write operation is
successful.
                Depending on the protocol, picking one or the other make a big
difference.
                HTTP use OutputWriter, SIP use async queue.

          */

         return true;
     }

     public boolean postExecute(Context context) throws IOException {
         return true;
     }

     private ClientConnectionHandler retrieveConnectionHandler(Context
context) {
        ClientConnectionHandler clientConnectionHandler = null;

                AttributeHolder connectionAttrs =
ctx.getAttributeHolderByScope(AttributeScope.CONNECTION);
        if (connectionAttrs != null) {
                clientConnectionHandler = (ClientConnectionHandler)
connectionAttrs.getAttribute(CLIENT_CONNECTION_HANDLER_ATTR);
        } else {
                WorkerThread workerThread = (WorkerThread)
Thread.currentThread(); // Detach the current Thread data.
                connectionAttrs = workerThread.getAttachment();
                // Attach it to the SelectionKey so the it can be resumed latter.
                key.attach(connectionAttrs);
        }
        
        if (clientConnectionHandler == null) {
                clientConnectionHandler = new ClientConnectionHandler(manager,
context.getSelectionKey(), context.getSelectorHandler());
                connectionAttrs.setAttribute(CLIENT_CONNECTION_HANDLER_ATTR,
clientConnectionHandler);
        }

        return clientConnectionHandler;
     }
}

On Jul 15, 2008, at 15:00 , Survivant 00 wrote:

>
>
> 2008/7/15 Oleksiy Stashok <Oleksiy.Stashok_at_sun.com>:
> Ok :))
>
> Can you pls. attach all Grizzly related sources - it will help a
> lot :)
>
> Thanks.
>
> WBR,
> Alexey.
>
> On Jul 15, 2008, at 14:52 , Survivant 00 wrote:
>
>> euh, sorry it was the only way that I found a sample. That why I
>> asked the questions.. maybe the problem is there, but like I didn't
>> know other way..
>>
>>
>>
>> 2008/7/15 Oleksiy Stashok <Oleksiy.Stashok_at_sun.com>:
>>> I'll explain my needs from the start.
>>>
>>> it's for a stock quote simulator.
>>>
>>> when a client connect to the server, it will send requests.
>>>
>>> like that
>>>
>>> client 1 -> - want the feed for the symbol | aaa
>>> - want the feed for the symbol | bbb
>>>
>>> client 2 -> - want the feed for the symbol | aaa
>>>
>>>
>>> In the server using grizzly :
>>>
>>> I have 2 filters in the chains.
>>>
>>> - the first for parsing the messages
>>> - the second for processing the message
>>>
>>> I need to keep a reference for all the clients and know which feed
>>> they register to.
>>>
>>> When I process to message, I'll receive the response from a 3th
>>> party later, and I 'll receive it on a callback method, and I'll
>>> send back the response to the client register as soon as the
>>> response arrive.
>>>
>>>
>>> The main problem that I had was that in the second filter, I need
>>> to know if the client already ask for a feed and I need to know
>>> how to send it back to data. That why I wanted to use a
>>> ClientConnectionHandler ( before using grizzly, I created the
>>> cleintconnectionhandler just after the socketserver.accept() ) to
>>> keep the selectionKey and the channel to send the data back.
>>>
>>>
>>> if I loop into your sample.. it will create a pool each time the
>>> client send a message.
>> Why? You're using stateful ProtocolChain? In other words you have
>> protocol chain, associated with each client connection?
>>
>> WBR,
>> Alexey.
>>
>>>
>>>
>>>
>>>
>>> 2008/7/15 Oleksiy Stashok <Oleksiy.Stashok_at_sun.com>:
>>> Hi,
>>>
>>> your implementation is ok.
>>> Though if having the map of all client connections is not must -
>>> then you can create ClientConnectionHandler pool like inline...
>>>
>>>> public class QuoteQueryManagerFilter implements ProtocolFilter {
>>>>
>>> ConcurrentLinkedQueue<ClientConnectionHandler> pool = new
>>> ConcurrentLinkedQueue<ClientConnectionHandler>();
>>>> private QuoteManager manager;
>>>>
>>>> public QuoteQueryManagerFilter(QuoteManager manager) {
>>>> this.manager = manager;
>>>> }
>>>>
>>>> public boolean execute(Context context) throws IOException {
>>>> String query = (String)
>>>> context.removeAttribute(ProtocolParser.MESSAGE);
>>>>
>>>> if(query==null || query.trim().length()==0){
>>>> return false;
>>>> }
>>>>
>>>> System.out.println("query = " + query);
>>>>
>>>> //here should be the parsed query command string with
>>>> // which query should be able to create a command
>>>> // I have no time to rewrite ClientConnectionHandler
>>>> // Basically you have in context a link to client
>>>> connection
>>>> //with that and for example TCPSelectorHandler your
>>>> commands can
>>>> //send data to client
>>>>
>>>> /*
>>>> * For now instead of keeping whole Context, you can just
>>>> keep SelectionKey as reference to the client,
>>>> * and send response back using:
>>>> selectorHandler.getAsyncQueueWriter().write(SelectionKey, ...);
>>>> */
>>>>
>>>> // on va chercher le ClientConnectionHandler si il existe
>>>>
>>> ClientConnectionHandler clientConnectionHandler =
>>> pool.poll();
>>> if (clientConnectionHandler != null) {
>>> clientConnectionHandler.init(manager,
>>> context.getSelectionKey(), context.getSelectorHandler());
>>> } else {
>>> clientConnectionHandler = new
>>> ClientConnectionHandler(manager, context.getSelectionKey(),
>>> context.getSelectorHandler());
>>> }
>>>
>>>> // ClientConnectionHandler clientConnectionHandler = null;
>>>>
>>>> //
>>>> if(manager.selectionKeyMap.containsKey(context.getSelectionKey())){
>>>> // clientConnectionHandler =
>>>> manager.selectionKeyMap.get(context.getSelectionKey());
>>>> // } else {
>>>> // clientConnectionHandler = new
>>>> ClientConnectionHandler(manager, context.getSelectionKey(),
>>>> context.getSelectorHandler());
>>>> // }
>>>>
>>>> manager.processQuery(clientConnectionHandler, query);
>>>>
>>>>
>>>
>>> and then return ClientConnectionHandler to the pool from
>>> processing thread.
>>> In example above single ClientConnectionHandler could be
>>> associated with different client connections, but not at the same
>>> time.
>>>
>>> If you need to associate ClientConnectionHandler with just one
>>> client connection during its life-cycle - may be it makes sense to
>>> use ThreadAttachment's attributes.
>>> Let me know if this is required.
>>>
>>> Thanks.
>>>
>>> WBR,
>>> Alexey.
>>>
>>>
>>>>
>>>> 2008/7/15 Oleksiy Stashok <Oleksiy.Stashok_at_sun.com>:
>>>> Hi,
>>>>
>>>>
>>>> I have a chain of 2 filters.
>>>>
>>>> #1 - parsing the request
>>>> #2 - processing the request
>>>>
>>>> and loop until the client quit.
>>>>
>>>> I want to kept the info about the connection like :
>>>> context.getSelectionKey(), context.getSelectorHandler() in a
>>>> Object like :
>>>>
>>>> ClientTracker(SelectionKey key, SelectorHandler selectorHandler)
>>>>
>>>> and I'll send data back to the client using the key and the
>>>> selectorhandler.
>>>>
>>>> but I don't know where to create that class (ClientTracker) and
>>>> where to put it to use it later.
>>>>
>>>> if I create the ClientTracker into the 2 filters.. a new
>>>> reference will be created each time the client send a request..
>>>> it's not what I want.
>>>>
>>>> I tought of putting the ClientTracker in the context and check if
>>>> the class exist in the context.. but like the context could be
>>>> invalidate, I could lose the info..
>>>>
>>>> Normally, I would have created the ClientTracker equivalent
>>>> class, after the socketserver.accept();
>>>>
>>>> Do you want to store somewhere complete map of clients
>>>> (ClientTracker), which are currently connected?
>>>> Also, can you pls. provide more details how you want to use
>>>> ClientTracker class? Some code snippets...
>>>>
>>>> Thank you.
>>>>
>>>> WBR,
>>>> Alexey.
>>>>
>>>>
>>>>
>>>>
>>>> ---------------------------------------------------------------------
>>>> To unsubscribe, e-mail: users-unsubscribe_at_grizzly.dev.java.net
>>>> For additional commands, e-mail: users-help_at_grizzly.dev.java.net
>>>>
>>>>
>>>
>>>
>>
>>
>
>
> <
> demo3
> .zip
> >---------------------------------------------------------------------
> To unsubscribe, e-mail: users-unsubscribe_at_grizzly.dev.java.net
> For additional commands, e-mail: users-help_at_grizzly.dev.java.net