On Aug 26, 2011, at 7:35 PM, Emmanuel Lecharny wrote:
> On 8/27/11 2:04 AM, Alan D. Cabrera wrote:
>> On Aug 26, 2011, at 1:27 PM, Emmanuel Lecharny wrote:
>>
>>> On 8/26/11 8:47 PM, Alan D. Cabrera wrote:
>>>> On Aug 26, 2011, at 11:03 AM, Emmanuel Lecharny wrote:
>>>>
>>>>> On 8/26/11 7:22 PM, Alan D. Cabrera wrote:
>>>>>> On Aug 26, 2011, at 9:33 AM, Emmanuel Lecharny wrote:
>>>>>> What I have in mind is much more something like :
>>>>>>
>>>>>> void messageReceived( context )
>>>>>> {
>>>>>> ... // do something, updating the context, eventually setting a status
>>>>>> controller.callNextFilter( context, currentFilter );
>>>>>> ... // do something after the call
>>>>>> }
>>>>>>
>>>>>> and in controller :
>>>>>> void callNextFilter( Context context, Filter currentFilter )
>>>>>> {
>>>>>> Status status = context.getStatus();
>>>>>> Map<Status, Filter> map = fsmMap.get( currentFilter );
>>>>>> Filter nextFilter = map.get( status );
>>>>>>
>>>>>> nextFilter.messageReceived( context );
>>>>>> }
>>>>>> This strikes me as pretty complex, jmho. Also, I don't like the idea of
>>>>>> forcing the filter to call the controller.callNextFilter() to make
>>>>>> things work.
>>>>> the alternative would be something like :
>>>>>
>>>>> void messageReceived( context )
>>>>> {
>>>>> ... // do something, updating the context, eventually setting a status
>>>>> fsmMap.get( this ).get( status ).messageReceived( context );
>>>>> ... // do something after the call
>>>>> }
>>>>>
>>>>> No controller, but a long list of chained call.
>>>> But we're still forcing the filter to participate in things that really
>>>> should be the domain of the controller not the filter.
>>> If we delegate the transition to the controller, then we can't have post
>>> actions... Obviously, not having post actions makes it easier. (Post action
>>> are really important if we want to do something when we come back from the
>>> application.)
>> I think we're getting to some interesting bits. Can you provide more exact
>> detail?
>
> One very simple example, taken from the ProfilerFilter :
>
> @Override
> public void messageReceived(NextFilter nextFilter, IoSession session,
> Object message) throws Exception {
> if (profileMessageReceived) {
> long start = timeNow();
> nextFilter.messageReceived(session, message);
> long end = timeNow();
> messageReceivedTimerWorker.addNewDuration(end - start);
> } else {
> nextFilter.messageReceived(session, message);
> }
> }
>
> If you depend on the controller to call thenext filter, then you have no easy
> way to compute the time it takes to process the action, as you won't be able
> to execute the post action.
This is a good use case, instrumentation of the protocol stack. Something to
think about.
What I still object to is forcing filters to participate via this code bit:
fsmMap.get( this ).get( status ).messageReceived( context );
>
> Note that the decoding of multiple messages within one PDU won't be possible
> either without being able to process post-actions. (see below)
>>> One clear exemple would be a decoder processing a PDU with more than one
>>> message : you can't anymore loop on the PDU if you delegate the next call
>>> to the controller.
>> What is the format of this PDU and how is it presented to the FSM, i.e. in
>> what kind of pieces does it arrive?
>
> You get an array of bytes, containing potentially more than one message. The
> fact is that you have no way to get a byte array from the socket as soon as a
> complete message has been received : the socket does not know anything about
> messages.
>
> You just have to assume that the PDU you've got can be :
> - either a portion of a message, and you have to wait for more bytes in order
> to produce a message
> - a full message, once decoded
> - more than one message you have to send to the handler one by one
> - some full messages plus a fraction of a message
I had/have the same understanding of how to crack a PDU and pass on translated
messages. This can easily, and I think a fare bit cleaner, be accomplished
using the mechanism that I propose as well.
>>>> With that said I think that an FSM where we have, here letters are filters:
>>>>
>>>> [outside filter] -> {A -> [state change decides to send to] -> B ->
>>>> [state change decides to send to] -> C} -> [outside filter]
>>>>
>>>> is very confusing. What protocol would require that? Just an honest
>>>> question; one does not come to my mind atm.
>>> all of them :) This is what we currently do in MINA.
>> I think that I am hearing a reply that explains that all the protocols are
>> implemented in this way. I'm not hearing an explanation as to why it *must*
>> be that way.
>
> simply because you can't move to the next filter unless you have transformed
> what you've got from the previous filter. If we exclude all the utility
> filters (like the loggers), a filter can only process a certain type of
> message, and produce another type of message. The chain of filters is
> dictated by the transformation done at each step. One good example is LDAP
> processing : in order to decode a LDAP message, you first have to decode the
> TLVs, then you can decode the messages themselves. A LDAP chain would be
> something like :
>
> [outside filter] -> (bytes) -> { TLV decoder filter -> (TLVs) -> LDAP decoder
> filter -> (LDAPmessages) } -> [outside filter]
>
> (add to this the fact that you may loop on the TLV filter *and* the LDAP
> filter, plus the fact that you may have to wait for more bytes)
>
> Now, it makes *no sense* to exchange the filters. It will simply not work.
> It's the very same for any protocol we will process with such a mechanism.
>
> In other words, the order the filters are executed is not random. It's fixed
> by design, and does not change (as soon as we have ruled out the possibility
> to have a dynamic chain).
>
>
> Another way to see this mechanism is that it's a path from one point to
> another : you may have many different paths, but for a set of bytes, you will
> always follow the same path. The only way to follow two different paths is
> when you get two different sets of bytes.
I'm not sure that this needs to be a single FSM. Why not
[outside filter] -> (bytes) -> [TLV decoder filter] -> (TLVs) -> [LDAP decoder
filter] -> (LDAPmessages) -> [outside filter]
I hear "looping" being a justification but it strikes me that using an FSM
obfuscates things needlessly as I believe that a simple filter will do.
Is there some code I can look at to see your above implementation? I think
here is a perfect case for code bits to compare.
Regards,
Alan