directory-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Alex Karasulu <akaras...@apache.org>
Subject Re: Implementing Interceptor Extendibility
Date Tue, 01 Nov 2011 11:15:51 GMT
On Mon, Oct 31, 2011 at 8:52 PM, Emmanuel Lecharny <elecharny@gmail.com>wrote:

> On 10/31/11 10:17 AM, Alex Karasulu wrote:
>
>> On Sun, Oct 30, 2011 at 7:02 PM, Göktürk Gezer<gokturk.gezer@gmail.com>**
>> wrote:
>>
>>  Hi Devs,
>>>
>>> I'd like to do some brainstorming about Interceptor extension mechanism
>>> which we're about to implement. Ideas those will come out from that
>>> thread
>>> will enlighten our way on other extension points of the ApacheDS.
>>>
>>> The first main issue is whether we're going to preserve old standalone
>>> ApacheDS or not?
>>>
>>>  Let's be really careful about the common terminology we will be using
>> for
>> this discussion. When you say "standalone ApacheDS", I automatically think
>> about the standalone ApacheDS maven module that holds the ApacheDS main()
>> application launcher.
>>
>> In a previous thread Pierre recommended keeping such a module but modifing
>> it so that it starts up an OSGi container which launches the ApacheDS
>> bundles. However the user experience does not change much. This merely
>> allows users to start up ApacheDS from the command line as before but
>> behind the scenes an OSGi container is launched as the main() application.
>> This is one option and I think I like this approach because this way we
>> don't have to maintain a monolithic application startup and a OSGi
>> startup.
>>
>>
>>  Preserving API in nonOSGI environments had purpose. And with the very
>>> simple method we've used in API, we can also preserve ApacheDS's nonOSGI
>>> version too.
>>>
>>>  Sure we can do anything but will it add additional complexity and
>> overhead
>> for maintaining code and what's the gain that we get? Will it make
>> debugging easier to maintain this non-OSGi configuration?
>>
>>
>>  Doing it will have pros and cons.
>>>
>>>  Ahh there you go with the pros and cons :-).
>>
>>
>>  The main advantage will be of course letting existing user semantics
>>> unchanged.
>>>
>>>  It appears to me that you're talking about more than just the
>> application
>> launcher verses the OSGi based launch sequence. I guess you're referring
>> to
>> all the configuration mechanism changes that will impact the different
>> cases.
>>
>> To me these usage semantic changes are small compared to the overall gains
>> we will get.
>>
>>
>>  But implementing extendibility without breaking ApaheDS's nonOSGI version
>>> will put some limits on our extension capability on individual
>>> components.
>>> Besides those limitations, we'll also end up having two different runtime
>>> behaviour of ApacheDS. Documentation will be obviously painful. And some
>>> aspects won't be implementable if we choose to preserve nonOSGI version.
>>> For example, OSGI can give us the opportunity to don't restart server
>>> after
>>> some changes on configuration, which will be perfect improvement of
>>> course.
>>> But to make this we must go fully OSGI. List is long.
>>>
>>>  Yeah I prefer to forget about maintaining the non-OSGi version. The
>> "standalone ApacheDS" maven module can launch the OSGi container or Karaf
>> then have the OSGi environment fire up ApacheDS as a full OSGi
>> application.
>>
>>
>>
>>  We've talked about it before but i wanted to ask that question again,
>>> because its an alive topic and subject to change. Maybe some of yours
>>> mind
>>> could have been changed, and i want to know if it did. If we proceed like
>>> what we've been talked earlier, then I'm going to make ApacheDS (not
>>> shared) an OSGI application, and use all of its loveliness.
>>>
>>>
>>>  +1
>>
>>
>>  To talk about Interceptors specifically, here is the design that i'm
>>> thinking. To manage OSGI heavy dynamic nature, we must implement our
>>> Interceptor related code in the closest module that is using
>>> Interceptors,
>>> which is InterceptorChain in our case. First step will be implement a
>>> component called InterceptorHub which is responsible for keeping list of
>>> all Interceptors installed in the container(means installed in OSGI as
>>> bundle). This hub will create an instance of every Interceptor per every
>>> ApacheDS instance and keep it.
>>>
>>> When an InterceptorChain want to iterate through interceptors, it will
>>> get
>>> them from InterceptorHub rather then DirectoryService.**getInterceptors().
>>> So
>>> it will get the most recent list of interceptors every time.
>>>
>>>
>>>  OK
>>
>> Just a question: why not just make the InterceptorChain act as or perform
>> the duties of the hub? I guess the hub is intended as an internal
>> component
>> of the chain that hides some details relating to OSGi from the chain?
>>
>>
>>  Currently in our config.ldif file, there is an interceptors entries. We
>>> can leave those entries there for specifying mandatory interceptors. So
>>> if
>>> any of them is not exists at the time server is starting we can throw an
>>> exception saying necessary interceptor is not exist. But we won't be have
>>> to list additional interceptors in that list. They will be automatically
>>> attached to the active ApacheDS instances.
>>>
>>> We'll also change the Interceptor implementations to be an IPojo
>>> component. Those components will publish two main properties. First is
>>> their ordering and the second is their bypass list.
>>>
>>> Ordering is something we've talked about but i couldn't get a clear
>>> explanation about it. What i suggest is like this: we publish three kind
>>> of
>>> ordering from interceptor: strict, level and relax. "strict" means strict
>>> ordering, this will be published from core interceptors and that strict
>>> order will create a level. If we publish it as level we must also attach
>>> an
>>> index with it, such as level-3. If we publish it as relax it means it can
>>> be called anywhere in the chain after the strict ones. So lets suppose we
>>> documented the core interceptor ordering like:
>>>
>>> 1- InterceptorA
>>> 2- InterceptorB
>>> 3- InterceptorC
>>>
>>> So there is 3 level in interceptor chain. If somebody implement an
>>> additional interceptor named InterceptorD and publish its ordering
>>> as level-2, it is saying it must be called after InterceptorA(level-1).
>>> So
>>> the new list will be:
>>>
>>> 1- InterceptorA
>>> 2- InterceptorD
>>>     InterceptorB
>>> 3- InterceptorC
>>>
>>> I didnt't numbered B as 3 but rather i grouped it with the InterceptorD,
>>> putting InterceptorD above it. Why? What happens if vendor wants to
>>> implement two custom interceptor named InterceptorD and InterceptorE and
>>> want 2 of them to be called right after InterceptorA(after 1th level) but
>>> want InterceptorD to be called first. So we must suborder those 2
>>> interceptors. That's why i treated the first core list as levels rather
>>> than orders ! So the ordering which is published from custom interceptor
>>> is
>>> not sufficient to describe good ordering. We must also publish
>>> subordering
>>> which specifies the ordering between that specific vendor's interceptors.
>>>
>>>
>>>  This is rather interesting. This approach never occurred to me but it's
>> appealing. I'll give this some thought and also would like to hear from
>> others on the list about it.
>>
>
> Jumping on Göktürk idea, some proposal.
>
> We all know that the core interceptors are to be executed in a given
> order. Calling the NormalizationInterceptor after the
> CollectiveAttributeInterceptor does not make any sense.
>
> OTOH, we allow the admin to modify the order, simply by changing the
> configuration, and it could lead to some bad bad things...
>
> What if we create an implicit order by defining which interceptor is
> allowed before each interceptor ? For instance, this is the default order :
>
> 1  normalizationInterceptor
> 2  authenticationInterceptor
> 3  referralInterceptor
> 4  aciAuthorizationInterceptor
> 5  defaultAuthorizationIntercepto**r
> 6  exceptionInterceptor
> 7  operationalAttributeIntercepto**r
> 8  keyDerivationInterceptor
> 9  passwordHashingInterceptor
> 10 schemaInterceptor
> 11 collectiveAttributeInterceptor
> 12 subentryInterceptor
> 13 eventInterceptor
> 14 triggerInterceptor
>
> We could obtain the same order by replacing the ads-interceptororder AT by
> a ads-previousInterceptor :
>
> 1  normalizationInterceptor ( previous : null )
> 2  authenticationInterceptor ( previous : normalizationInterceptor )
> 3  referralInterceptor ( previous : authenticationInterceptor )
> 4  aciAuthorizationInterceptor ( previous : referralInterceptor )
> 5  defaultAuthorizationIntercepto**r ( previous :
> aciAuthorizationInterceptor, referralInterceptor ) (here, the
> aciAuthorizationInterceptor may be disabled)
> 6  exceptionInterceptor ( previous : defaultAuthorizationIntercepto**r )
> 7  operationalAttributeIntercepto**r ( previous : exceptionInterceptor )
> 8  keyDerivationInterceptor ( previous : operationalAttributeIntercepto**r
> )
> 9  passwordHashingInterceptor ( previous : keyDerivationInterceptor )
> 10 schemaInterceptor ( previous : passwordHashingInterceptor )
> 11 collectiveAttributeInterceptor ( previous : schemaInterceptor )
> 12 subentryInterceptor ( previous : collectiveAttributeInterceptor )
> 13 eventInterceptor ( previous : subentryInterceptor )
> 14 triggerInterceptor ( previous : eventInterceptor )
>
> Now, for interceptors that has no specific requirement, we can set the
> previous AT to the previous AT it will follow.
>
> The key is that we must distinguished between critical inteceptors (the
> level-1 interceptors) and others. We enforce the position only within one
> level (ie, level-1 interceptors orders are checked, then level-2, then
> level-3). So we can add a non critical interceptor (ie level-2 or 3) in the
> chain between two critical interceptors, without creating any trouble in
> the critical interceptor order : the check for level-1 interceptors' order
> is done ignoring level-2 interceptors.
>
> thoughts ?


Here's the one big point that sticks out in my mind regarding such ordering
mechanisms. When you have to provide dependency information directly to an
Interceptor (name, identifier, or some other descriptor for the
interceptor), then you're creating an inherent dependency. And on top of
this you need to know a priori the composition of the interceptor chain.

If we want safe variability which can only happen with the proper ordering
of functionality while allowing for swappable interceptors including the
critical ones for a customized version, then we need to have some
indirection. Here's what I had proposed in the past:

(1) Define some general mandatory invocation aspects that must be handled
by one or more Interceptors, either working alone or together. For example
these mandatory aspect descriptors can be:

normalization
authentication
referral-handling
access-control
exception-handling
operational-attribute-handling
schema-checking
subentry-handling
event-handling

These are general mandatory aspect descriptors. They are not dependent on
any specific interceptor implementation. We know how these must be ordered
to have the server operate properly. You just can't do much before
normalization right? What interceptor does this is irrelevant.

So my recommendation is to manage this general invocation aspect descriptor
order and bind interceptors to the aspect descriptors at component
initialization time. What this takes is having an Interceptor announce
which general aspects it is involved with. So if I have FooInterceptor
which manages schema-checking, then when this interceptor initializes, the
chain can determine which aspect it is associated with by asking the
component.  Then it knows where in the chain to inject the component.

This strategy/pattern creates a level of indirection between the aspect
functions performed by Interceptors and their implementations, allowing for
dependency free management of their ordering.

Now Gokturk's idea deals with the non-essential injection and quite frankly
it sounds like a good idea to me to use levels (or hierarchy here) while
preserving essential-mandatory interceptor ordering. That's a separate
matter to consider however it might add additional implementation
complexity for us and make configuration matters a tiny bit more confusing
for our users. That's something we together must evaluate, however the
interceptor chain altering user profile is not the common user profile but
one where they know and understand the pitfalls (one would hope).

-- 
Best Regards,
-- Alex

Mime
View raw message