On Nov 26, 2009, at 7:51 AM, Emmanuel Lecharny wrote:
About the Filters and the Chain, I also have some initial thoughts
(even if they are not recent). Here they are.
First, let's start with what we really need :
o A chain has a start
o A chain has an end
o Both start and end are Filter instances
o We have as many filters as we need in the chain
o Filters can be added or removed dynamically
o A chain is instanciated for each session (if we have 10K sessions,
we have 10K instances of chain)
o A filter can be activated (enabled) or disabled, dynamically
That being said, we have many ways to implement such a mechanism.
let's consider we are in a Filter, and we want to call the next
filter. We have three options here :
* The NextFilter is already computed, and is a part of the Filter's
data. This resolves to a direct call :
...
nextFilter.<action>( params );
...
* The NextFilter is computed dynamically :
...
nextFilter = computeNext( params );
nextFilter.<action>( params );
...
* The nextFilter is computed by the caller, and is a parameter :
public void <action>( Filter nextFilter, <other params>... ) {
...
nextFilter.<action>( params );
...
The current version (2.0) implements the third form. From the
debugging PoV, it's a nightmare, as you have to step through many
intermediate calls (in fact, an other call for each filter). The
StackTrace is heavy. The next filter is not known when you are in a
Filter, as it's computed outside of the current method.
The first approach is way simpler, as is the second one.
Let's go a bit deeper : if there is an executor in the chain,
suddenly things get a bit muddy. The "one session = one chain
instance" is still correct, but we may have two (or more) messages
being processed on one session on the same chain. If the chain is
dynamically modified, then it may be ok for the thread modifying the
chain, but not for the other threads. Here is a picture describing
this mechanism :
Thread1 /--->
[Filter3]--->[tail]
Thread2 [head]---> [filter1]--->[filter2]--->[executor]-*---->
[Filter3]--->[tail]
Thread3 \--->
[Filter3]--->[tail]
Whoa. Look at that ascii art. No wonder your Dell had an aneurism. ;)
Now, if the Thread 2 modify the chain to add a Filter4, then there
is no guarantee that Thread1 will call it. That means we must
protect the chain against concurrent modifications.
Another drawback of the first approach is that you must declare an
instance of a Filter for your chain, as it carries some information
specific to your session. We would rather have a stateless Filter...
The second approach require an external system to give you the next
filter. It can be the session, which is passed as a parameter for
each filter call.In this case, if the external system is protected
against concurrent modifications, then we are safe (except if we
want each thread to define its own chain in the previous example...
In this case, the executor must be responsible for the chain
duplication, and probably store it in a ThreadLocal variable )
In the second approach, won't we end up with the same problems as Mina
2.0, "From the debugging PoV, it's a nightmare, as you have to step
through many intermediate calls (in fact, an other call for each
filter). The StackTrace is heavy. The next filter is not known when
you are in a Filter, as it's computed outside of the current method"?
To make things clear in my mind, where would we need such a feature?
Couldn't we just load all the filters in the chain and they decide
whether or not to do anything?
Regards,
Alan