[abc] Open modules revised

From: Eric Bodden <eric.bodden@mail.mcgill.ca>
Date: Mon Mar 27 2006 - 14:41:51 BST

Hi all!

I just wanted to report on a few insights and ideas I got from attending the Software Composition workshop at ETAPS. Besides the fact that they had much more presentations which were interesting to me than I originally thought, in the end we had a longer discussion about the possible conflicting ideas of AOP and component software (aspects breaking up components and inspecting their internals, not adhering to their public interface).

It turned out that particularly for this community Open Modules is apparently *the* mechanism that is necessary for aspects to interact with components at all in a sound way. The overall consensus was that without such modules aspects just break encapsulation and one really cannot speak about a piece component software system any more.

A talk by Eric Tanter inspired even more the way I thought about Open Modules: Thy proposed a mechanism for defining "Context aware aspects". This allows to expose context to an aspect (similar to "let" - and indeed some of there examples mapped quite well to a Tracematch with "let") and to have the aspect reconfigure its behavior depending on this context.

The idea I got now was to combine this with the notion of Open Modules. The problem I see with OM right now is that yes, they do provide a public interface for a module which aspects can access, however any aspect using that interface still has to be at least somewhat aware of the internals of *every* module it advises. This is because the module spec only restricts the set of visible joinpoints, not more. What I propose now, looks as follows... (may sound a bit wild, but - hey - Gregor said, whe should go wild! :-) )

1.) An aspect consists of a public interface and an implementation.
2.) The aspect interface consists only of abstract pointcuts and (optionally) some temporal constraints over those abstract pointcuts in the form of a tracematch or something equivalent. The abstract pointcuts say what this aspect *requires* to apply to a module and the constraints say in *what order* the related joinpoints may occur in order to have the aspect function correctly.
3.) Any advice in the aspect may only use combinations (in the form of Boolean cominations, cflow and tracematches) of those abstract pointcuts - no concrete pointcuts at all!

By providing such an interface, the whole abstract control flow and data flow of the aspect should be pretty regorously defined. However, of course the aspect does not do anything yet. That's where modules come into play...

Now each module, an aspect should apply to explicitly states, what each abstract pointcut means *in the context of this module*. The simplest statement could be that the module is not interested in the aspect and hence all pointcut are empty. This could possibly be the default. However, the module can conretize the pointcut by advertising or exposing concrete pointcuts and at the same time binding those to the abstract ones. If the set of exposed pointcuts adheres to the interface of the aspect can be checked via the constraints of the aspect interface.

Ok, maybe sounds nice, but what does it buy us? Well, the good thing in my p.o.v. about this approach is that you not only have a mechanism of hiding the internals of a component from aspects and exposing only what you need. Also, you can not have a truly GENERIC aspect, which can be deployed onto any given module that is compatible the aspect's interface. That way, the module can really be replaced by another one that shares the same interface. Moreover, also the aspect can be replaced by another one that shares the same aspect interface.

An example:

Imagine you have a generic observer aspect based on tracematches. So here we only say that we have types which we call observers and subjects and pointcuts which we call register, unegister and update. Then we declare a method which is required to be available on any module that wishes to use this aspects. Also we give temporal constraints which say in what temporal relations those three pointcuts are. (this is optional but recommended)

aspect ObserverPattern {

//this is the exposed part
        interface Observer{}
        interface Subject{}

        //feature does not exist yet, but was proposed as a lacking feature
        //in the BOF on reusable aspects
        abstract typepattern observers;
        abstract typepattern subjects;

        declare parents: observers extends Observer;
        declare parents: subjects extends Subject;

        abstract pointcut register(Observer o, Subject s);

        abstract pointcut unregister(Observer o, Subject s);

        abstract pointcut update(Subject s);

        //need this in order to decouple the call to notify
        //from the internals of the concrete observer;
        //the method must be provided by the module!
        require {
                void notify(Observer,Subject);
        }

//temporal constraints on abstract pointcuts
        //never unregister(o,s) before register(o,s), etc ...

//actual aspect implementation
//here we can use any of the above pointcuts but no concrete ones

        tracematch(Observer o, Subject s) {

                sym reg after: register(o,s);
                sym ureg after: unregister(o,s);
                sym upd after: update(s);

                reg upd {

                        module.notify(o,s);

                }

        }

}

So a module which uses this aspect could no look as follows:

module WantsToBeObserved {

        apply ObserverPattern {

                //this does th actual binding between what the aspect
                //wants to do and what this means for this module
                typepattern observers: com.org.whatever.Observer+;
                typepattern subjects: com.org.whatever.Subject+;

                pointcut register(Observer o, Subject s):
                        expose call(* com.org.whatever.Observer.register(
                                com.org.whatever.Subject)) && target(o) && args(s);
                pointcut unregister(Observer o, Subject s):
                        expose call(* com.org.whatever.Observer.deregister(
                                com.org.whatever.Subject)) && target(o) && args(s);
                pointcut update(Subject s):
                        advertise call(* com.org.whatever.Subject.add*(..))
                                && target(s);

                notify(com.org.whatever.Observer o,
                        com.org.whatever.Subject s) {
                        o.wasUpdated(s);
                }

        }
        
}

So the benefit in this situation is that the aspect does not care about how an observer or subject is implemented AT ALL! It only says that there exist pointcuts which must match and in what order they must match. Also, the aspect can only call operations on the module, i.e. on the exposed public interface. The module itself can and must then state what this operation actually means for this module.

That way you still have the benefit of being able to consistently implement a crosscutting concern, plus you do not break encapsulation plus the aspect becomes truly generic plus you can verify if it is used correctly (by the constraints).

So if you read up till here, thanks for taking the time! I woul of course be grateful for any comments. I really like the idea, but maybe I am missing something.

Eric

--
Eric Bodden
Sable Research Group
McGill University, Montréal, Canada
Received on Mon Mar 27 14:42:53 2006

This archive was generated by hypermail 2.1.8 : Tue Mar 06 2007 - 16:13:27 GMT