Hi guys,

here are some thoughts and ideas I have had those last few days, thoughts that I have experimented in branches. It's not finished yet, but I think I have something likely to work well and be easier to use than the current implementation.

So here are the gathered thoughts, please feel free to give me feedback :


Refactoring the current chain : possible implementations
---------------------------------------------------------

So, guys, after having spent a few days thinking and experiencing, here are the different possibilities I have thought about. I will try to list all of them, with their pros and cons.

1) The chain, and the chain builder
-----------------------------------

We use a chain of filters to process events. An event, once processed by either the Acceptor/Connector or the Processor, is pushed through a chain of filters, up to a handler, and a tail filter which does nothing but terminate the chain. This chain is associated witheach new session, and copied from a template, which is created before we start the client/server (depending on what you are iomplementing).

The chain template is created using a chain builder. The idea is to first create a chain template, which will be copied when a new session is created, so that we will be able to dynamically modify the copied chain within the session without modifying the template (for instance, you can dynamically add a logging filter during a session)

So we have two different vision for the chain, a template, and N instance of this template (one per session).

1.1) Operations on a chain
------------------------
We want to be able to walk the chain, jumping from one filter to the next one. We also want to add or remove filters, dynamically. Both chains (template and instances) dn't support all of these operations : it's obviously useless to process event on the template, we just want to be able to manage the filters into the chain while building it. Also, the copy should be fast and cost as less as memory as possible.

When an event is processed, it goes through events following this logic :

processEventXXX() {
 doPreOperation();
 callNextFilter();
 doPostOperation()
}

the callNexrFilter() is the important part of the problem : how do we jump to the next filter, allow a dynamic modification of the chain, and protect the chain against concurrent modifications ?

It also has to be fast, and easy to debug... Challenging !

2) Proposed solutions
---------------------

2.1) Using List<T>
---------------------------------

We can imagine that filters are stored into a list, and that we process an event through all the filters this way :

for (Filter filter:listOfFilters ) {
 filter.processEventXXX();
}

It will obviously work, be easy to debug, be fast, but we will have two problems here : - we have to use a synchronized list, in order to guarantee that we can't modify the list while jumping from one filter to another
- we can't any more call pre and post processing.

The first point is a bit problematic, because we have to lock all the list. Performance can suffer, but not that much, considering that we don't have so much chain modifications (hopefully) The real issue is more serious. One improvement would be to have such a loop :

for (Filter filter:listOfFilters ) {
 filter.doPreXXX()
 filter.processEventXXX();
 filter.doPostXXX()
}

but this is far from being enough.

How do we handle the list modification ? As it's a list, we have all the LIST API to do that : add, add(index), etc. Easy, as we can access this list from the session object.


2.2) Using a linked list
------------------------

Instead of using a list, we can also link the filters. Passing from one filter to the other will then be just a matter of doing :

F1.processEventXXX()
 pre processing
 nextFilter.processEventXXX()
 post processing

The nextFilter is a field pointing to the next filter in the chain.

This solution has the big advantage of being very simple to implement, allows pre and post processing, whatever it can be, and fast and easy to debug.

The main issue is that it's difficult to protect this chain from concurrent modification, unless you synchronize the nextFilter field :
F1.processEventXXX()
 pre processing
 synchronized( nextFilter ) {
   nextFilter.processEventXXX()
 }
 post processing

This may be acceptable, though. The cost of checking if this synchronization might be not null, as we can see that if the chain contains many filters, we will have as many synchronized sections as we have filter, plus the time during which the section is synchronized is the global execution time.

Modifying the list is quite simple. As each filter has a link to the next filter, it's just a matter of manipulating this list to add or remove a filter. Obviously, adding or removing a filter back in the chain is useless.

2.3 Using a double level structure
----------------------------------
The idea is to separate the filters from the chain. We will store the chain into a list, and each element fo this list is a filter. This list is handled by the session.
Here is the code :

// First invocation
processEvent(XXX) {
 session.getFilter(0).processEventXXX( session, 0 );
}

// Nth invocation
Fx.processEventXXX(session, pos) {
 pre processing
 pos++
 session.getFilter(pos).processEventXXX(session, pos)
 post processing
}

// And of course, the TailFilter won't call the next filter :)

How do we modifiy the list ?

Fx.processEventXXX(session, pos) {
 pre processing
 pos++
 // Add a new filter to the chain
 session.addFiler(pos, filter)
 session.getFilter(pos).processEventXXX(session, pos)
 post processing
}

The session.addFilter() method will just update the list, and then the next call will be done. If we use a CopyOnWriteArrayList, we are thread safe.

So far, using this method, we have all the advantages and none of the inconvenients we have seen in the other methods.

3) Choice
---------

If we cant to keep all the existing functionalites MINA has, solution 2.3 is obviously the way to go. Well implemented, it will also be fast and easy to debug, and, last, not least, it's close to what we curently have, but with less code and a better interface.

--
--
cordialement, regards,
Emmanuel Lécharny
www.iktek.com
directory.apache.org


Reply via email to