Hi all,

Forgive the prepost here: need to report on a off list conversation I just had with Emmanuel. We generated some very nice ideas on these matters. I'm going to be very direct with implementation details rather than being high level and general with these ideas to directly dump the thoughts:

(1)  For each intercepted operation (add, delete, modify etc), the server maintains an ordered list of core aspect descriptors out of the box (normalization, access-control, authorization etc) which must be applied in order to an invocation of an intercepted operation.

To do this we can have an operation expose aspect ordering:

add.getAspects() ... returns order list or array of Aspects like {Normalization, AccessControl, Authorization, ...}.

Dynamic configuration should allow us to change this ordered list on the fly to introduce new aspects or remove old ones. Meaning if we like we can define a new logical aspect like craptastic aspect and have it participate in various operation invocations in the order we like it to participate.

This is our logical aspect ordering model and exposed as a dynamic configuration point in the server.

(2) Each Interceptor must publish one and only one aspect it participates in. The aspect must be in the set of aspects configured in the server via dynamic configuration so we have a list of aspects managed by the server without the need for ordering since ordering is operation specific.

normalization
craptastic
authentication
access-control
foobar 

... 

Interceptor.getAspect()  should return an aspect within this set, and if it does not then it simply is not considered for injection into the chains created.

In the configuration area we also manage operation aspects with order:

bind: normalization, authentication
add: normalization ....
del: ...
modify ....
...

The user is constrained to only include aspects in the set of supported aspects above. No problem though, the user can add his or her custom aspect to the set, then can start adding that aspect to the operation aspect lists in the configuration. If no interceptor exists to supply functionality for that aspect, no problem it's just not injected during actual physical chain construction time. Once an interceptor appears it can be considered for injection when calculating the interceptor ordering in chains ... see below.

This is good for the OSGi model where services (interceptors in this case) can come and go.

(3) The server tracks the actual interceptor implementation execution order by calculating this order using these aspects on a per operation basis. If interceptor Foo.getAspect = Normalization and interceptor Bar.getAspect = Authentication, then for bind the interceptor implementation order would be: 

Foo, Bar 

The result of this calculation will be dynamically configurable as described above by altering configurations for aspect order per operation. This way we can add new aspects, that have not even been conceived of while creating ApacheDS. Users will have this freedom. And at chain construction time the right implementations will be properly selected and injected in the right order.

(3) At session creation time, the server constructs interceptor reference lists with actual references to interceptor instances for each operation. Sessions do not need unique interceptor instances: the interceptors are shared across sessions and chains. The server uses the aspect information from an Interceptor and operation aspect lists to determine this reference list order and construct it.

For Bind operation the interceptor reference list would be Foo, Bar as in the example above. 

Each session has an interceptor reference list calculated for each operation and this is stored within each session.   This way sessions can modify their interceptor reference lists for various operations to have fine grained auditing for example for a single operation in a single connection without impacting all sessions or the entire server.

The overhead for managing separate interceptor reference lists for each operation in each session is minimal.

(4) The interceptor chain changes character somewhat. It is modified to not be statically hardwired but to use the interceptor reference list of the operation invoked from a session to conduct invocations with the correct interceptor order. 

I think this can be done without causing inefficiencies. I don't have suggestions on how best we can do this in the code right now. 

Thoughts? Additions?

On Wed, Nov 2, 2011 at 10:04 PM, Emmanuel Lecharny <elecharny@gmail.com> wrote:
Hi guys,

I had a bit of time and I reviewed the interceptors today. And I had an idea which may be interesting to discuss.

First, here is a table where all the interceptors operation are listed, and for each interceptor, I listed the operation they are processing :

AciAuthorizationInterceptor : ACI
AdministrativePointInterceptor : API
AuthenticationInterceptor : AI
ChangeLogInterceptor : CLI
CollectiveAttributeInterceptor : CAI
DefaultAuthorizationInterceptor : DAI
EventInterceptor : EI
ExceptionInterceptor : EXI
JournalInterceptor : JI
KeyDerivationInterceptor : KDI
NormalizationInterceptor : NI
OperationalAttributeInterceptor : OAI
PasswordHashingInterceptor : PHI
ReferralInterceptor : RI
SchemaInterceptor : SI
SubentryInterceptor : SEI
TriggerInterceptor : TI

               ACI API  AI CLI CAI DAI  EI EXI  JI KDI  NI OAI PHI  RI  SI SEI  TI
add           :  x   x   x   x   x   ?   x   x   x   x   x   x   x   x   x   x   x
bind          :  -   -   x   -   -   -   -   -   -   -   x   -   -   -   -   -   -
compare       :  x   -   x   -   -   -   -   -   -   -   x   -   -   -   x   -   -
delete        :  x   x   x   x   -   x   x   x   x   -   x   -   -   x   -   x   x
getRootDSE    :  -   -   x   -   -   -   -   -   -   -   -   -   -   -   -   -   -
hasEntry      :  x   -   x   -   -   -   -   -   -   -   x   -   -   -   -   -   -
list          :  x   -   x   -   x   x   -   x   -   -   x   x   -   -   x   x   -
lookup        :  x   -   x   -   x   x   -   x   -   -   x   x   -   -   x   -   -
modify        :  x   x   x   x   x   x   x   x   x   x   x   x   x   x   x   x   x
move          :  x   x   x   x   -   x   x   x   x   -   x   x   -   x   -   x   x
moveAndRename :  x   x   x   x   -   x   x   x   x   -   x   x   -   x   ?   x   x
rename        :  x   x   x   x   -   x   x   x   x   -   x   x   -   x   x   x   x
search        :  x   -   x   -   x   x   -   -   -   -   x   x   -   -   x   x   -
unbind        :  -   -   x   -   -   -   -   -   -   -   -   -   -   -   -   -   -

(It's interesting that there are two operations that should be processed by some interceptors, and that are not : it's most certainly a bug. They are marked with a '?' in this table)

Now, we can see that all the operation are only processed by a few interceptors. I was thinking that it could be an option to list the mandatory interceptors for each operation, instead of letting the chain determinate if an interceptor will process this operation.

In the Operation manager, we will then call all the needed interceptors chain for each operation. For instance, for a delete operation, we will proceed this way :

operationManager.delete( DeleteOperationContext deleteContext )
{
   // ThedeleteContext.getOperation() is used to get the correct list of interceptors for the operation,
   // as we may have more than one delete operation (see below)
   InterceptorChain deleteChain = directoryService.getInterceptors( deleteContext.getOperation() );

   deleteChain.delete( deleteContext );
}
The biggest advantage is that we know have a clear list of interceptors for each operation, and we cna expose this list to the user, who can add its own interceptor. We can even compute this chain by checking which operation an interceptor will handle, using reflection, when the interceptor is added in the chain.

For internal operation, I'm quiet sure we should ask the nexus directly, instead of going through the chain again. However, we can perfectly proceed exactly the same way, by defining some other operation, like lookupInternal, which describes the list of needed interceptors.

One other advantage is that we will not go through N useless interceptor for each operation, speeding up (ok, marginaly but still) the operations.

Lats, not least, I'm quite sure that if we expose the list of interceptors involved by each operation, we make it easier for someone who want to add an interceptor, as all those lists will be available through teh configuration, and not in the code, as it is atm. It also remove the need for interceptors bypasses, if we inject the type of operation we want to execute in the context (xxxContext.getOperation()  will return the type of operation).

It may sound a bit crazy, but I think it would work with a little impact on the existing code...

thoughts ?

-- Regards, Cordialement, Emmanuel Lécharny www.iktek.com



--
Best Regards,
-- Alex