[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [List Home]
Re: [aspectj-dev] interfaces, pointcuts, and libraries

It is exactly considerations such as these that motivated the proposal for extensible pointcuts. They would allow one to use an abstractly defined pointcut outside the scope of an abstract aspect. They are not quite as powerful as some alternative ideas for separating aspect interface and implementation (e.g., Caesar's), but would be tremendously valuable.

Because it's fairly hard to dig the discussion out of the old users@xxxxxxxxxxx archives, I've attached two emails from the most relevant discussion thread as background reading :-) Jim - as we look forward to AspectJ 1.2, it would be great if you could post the old list of feature candidates for 1.1 (I believe you wrote more detail on this feature that almost made it).

Ron

Ron Bodkin
Chief Technology Officer
New Aspects of Security
m: (415) 509-2895

> ------------Original Message-------------
> From: Wes Isberg <wes@xxxxxxxxxxxxxx>
> To: aspectj-dev@xxxxxxxxxxx
> Date: Fri, Jul-25-2003 10:30 AM
> Subject: Re: [aspectj-dev] interfaces, pointcuts, and libraries
> 
> Wow.  Very interesting discoveries!
> 
> But it's a bug.
> 
>    "Abstract pointcuts may only be declared
>     within abstract aspects"
> 
>    Programming Guide, Semantics appendix
> 
> Here's my speculation as to why; Jim or Erik can
> chime in on the actual reasons.
> 
> Pointcuts are resolved at weave-time, i.e., staticly,
> If advice refers via an interface, it's not clear to
> me which implementation should be used:
> 
>     interface I {
>         abstract pointcut pc();
>     }
>     before () : I.pc() { ... }
> 
>     class A implements I {
>        pointcut pc() : within(A);
>     }
> 
>     class B implements I {
>        pointcut pc() : target(A);
>     }
> 
> If advice has to refer to implementation types,
> then it doesn't seem like it's part of an interface.
> 
> It doesn't seem like this would support incremental
> weaving, if the advice or aspects depended on the
> number of (other) implementing classes.
> 
> For subaspects implementing abstract pointcuts,
> then there is advice for each instance; could this
> be a similar case?  I'm not sure how.
> 
> You could do "one for each implementing class",
> but that would make it hard to understand when aspects
> should be instantiated. If there are two concrete aspects
> using an interface reference to a pointcut that has
> 3 implementations, then are there 6 aspect instances?
> That are created when the interface-implementation classes
> are loaded?  What if these are cflow aspects?
> 
> The nice thing about putting abstract pointcuts in aspects
> is that the crosscutting specification is local to that
> aspect hierarchy.  One answer to my objections is to
> restrict the scope of the interface pointcuts to the
> implementations themselves, but that seems contrary to
> the purpose.  That can be done with abstract aspects
> (except that classes can't extend them).
> 
> Wes
> 
> Adrian Colyer wrote:
> > AspectJ lets us declare pointcuts within aspects, classes and interfaces. 
> > To create 'library' pointcuts I need to be able to declare pointcuts in an 
> > interface (and let users of the library program to the interface), and 
> > then have implementers of the interface provide concrete implementations 
> > of those pointcuts. Exploring the behaviour of AspectJ 1.1 I see that we 
> > are part of the way there, but not fully. What I am about to describe is 
> > partially bug and partially feature request...
> > 
> > Today I can write:
> > 
> > public interface Foo {
> > 
> >    public pointcut bar();
> > 
> > }
> > 
> > This compiles happily, and I can refer to Foo.bar() in advice (it doesn't 
> > match any joinpoints).
> > 
> > If I write
> > 
> > class C implements Foo {}
> > 
> > this does not cause a compilation error (I believe it should, since C does 
> > not define pointcut bar which it's interface contract says it should).
> > 
> > If I write 
> > 
> > class C implements Foo {
> >   public pointcut bar() : execution(... ...);
> > }
> > 
> > this compiles happily. Writing advice against Foo.bar does not match 
> > anything, writing advice against C.bar() matches the execution joinpoints. 
> > The desired behaviour is that writing advice against Foo.bar should match 
> > against the C definition.
> > 
> > If I write 
> > 
> > aspect A implements Foo {}
> > 
> > this does not cause a compilation error (I believe it should, since C does 
> > not define pointcut bar()).
> > 
> > 
> > If I change the interface definition to
> > 
> > public interface Foo {
> >   public abstract pointcut bar();
> > }
> > 
> > then compilation of A fails with "inherited abstract pointcut Foo.bar() is 
> > not made concrete in A" (good, but tells me that the pointcut is not being 
> > implicitly made abstract when defined in an interface). Compilation of the 
> > empty C declaration still does not produce the compilation error. 
> > 
> > How I think I would like this to behave is that pointcuts declared in 
> > interfaces are implicitly abstract (just like method definitions in 
> > interfaces). If a class or aspect declares that it implements the 
> > interface without providing a concrete definition of the pointcut then 
> > this is a compilation error.  Clients should be able to write advice 
> > against the interface, and the advice will apply to joinpoints matching 
> > any of the  concrete implementations of interface in the system (same 
> > rules as for abstract / concrete aspect pairs). 
> > 
> > Why this is important: 
> > * I  can create a standard interface that clients program to
> > * Multiple parties can implement the interface to provide concrete 
> > implementations that make sense within their system. These can even be 
> > binary so that implementation details are never exposed to clients.
> > 
> > What do others think?
> > 
> > -- Adrian
> > Adrian_Colyer@xxxxxxxxxx
> 
> _______________________________________________
> aspectj-dev mailing list
> aspectj-dev@xxxxxxxxxxx
> http://dev.eclipse.org/mailman/listinfo/aspectj-dev
> 
--- Begin Message ---
Hello,
 
Here is a more concrete example, ...
What I have created by simplifing actual code from one of our finished projects.
I replaced all the real interactions with the ODBMS through system.out.println() calls.
 
Note: In the real project, there were a bunch of  classes like ServiceElements, 
(more complex calculations, more methods, more dataelements, 
all stored persistently in an Objectivity database).
Each having its corrsponding <>PersistenceAdapter.
 
Note1:
The actual example does not work as expected, see the previous mails in this thread, ...
However I hope it gives you an good understanding about the intention.
 
Note 2:
I found the pattern also in other scenarious, e.g. at the ORB integration, etc.
 
public class AbstractPointCutReuserTest {
 
    public static void main(String[] args) {
        ServiceElement Element=new ServiceElement();
        Element.setActive(true);
        System.out.println("The Element is Active:"+Element.isActive());
    }
}
 
class ServiceElement{
    public void setActive(boolean Active){
        this.Active=Active;
    }
 
    public boolean isActive(){
        return Active;
    }
 
    boolean Active=false;
}
 
abstract aspect AbstractPersistenceAdapter{
 
    public abstract pointcut WritingMethods();
 
    public abstract pointcut ReadingMethods();
 
    public pointcut ExternalWMethods();
 
    public pointcut ExternalRMethods();
 
    before(): WritingMethods(){
        System.out.println("Must execute markModified on"+thisJoinPoint.getTarget());
        System.out.println("Transaction Must be open");
    }
 
    before(): ReadingMethods(){
        System.out.println("Must execute markModified on"+thisJoinPoint.getTarget());
        System.out.println("Transaction Must be open");
    }
}
 
aspect ServiceElementPersistenceAdapter extends AbstractPersistenceAdapter{
    public pointcut WritingMethods():execution(* ServiceElement.setActive(boolean));
 
    public pointcut ReadingMethods():execution(* ServiceElement.isActive());
}
 

abstract aspect TransactionAdapter dominates PersistenceAdapter{
 
    pointcut pc():AbstractPersistenceAdapter.ExternalWMethods()||AbstractPersistenceAdapter.ExternalRMethods();
 
    Object around(): pc(){
        System.out.println("Open TransAction");
        Object obj=proceed();
        System.out.println("Close Transaction");
        return obj;
    }
}
 

abstract aspect RetryFailingTransactionHandler dominates TransactionAdapter{
    public static int MAXCOUNT=10;
 
    pointcut pc():AbstractPersistenceAdapter.ExternalWMethods()||AbstractPersistenceAdapter.ExternalRMethods();
 
    Object around(): pc(){
        int count=0;
        Object obj=null;
        while(true){
            try{
                obj=proceed();
                break;
            }catch(RuntimeException ex){
                count++;
                ex.printStackTrace();
                if (count>=MAXCOUNT)
                    throw ex;
            }
        }
        return obj;
    }
}
 
abstract aspect BenchmarkTransactions dominates RetryFailingTransactionHandler{
    public static int MAXCOUNT=10;
 
    pointcut pc():AbstractPersistenceAdapter.ExternalWMethods()||AbstractPersistenceAdapter.ExternalRMethods();
 
    Object around(): pc(){
        long first=System.currentTimeMillis();
        Object obj=proceed();
        long later=System.currentTimeMillis();
        System.out.println("Method needed: "+(later-first)+" Milliseconds");
        return obj;
    }
}
 
 
regards
   Arno
 

-----Ursprüngliche Nachricht-----
Von: Gregor Kiczales [mailto:gregor@xxxxxxxxx]
Gesendet: Montag, 7. Januar 2002 19:51
An: Arno.Schmidmeier@xxxxxxxxxxxxxx; hugunin@xxxxxxxxxxxxxx
Betreff: Re: Problem with reusing (abstract) pointcuts


For me, examples work best when they are "real", or an abstraction of real.
 
So, for example, an aspect like the DisplayUpdating aspect we show in "Getting Started with AspectJ"
is what I like to see.
 
Aspects with names like foo and bar are not as useful for me, because all I can see from them is
what the mechanism should do.  I can't glean the essence of what you are trying to say.  So examples
like that aren't as helpful in trying to think about other ways of saying the same thing.
 
 

----- Original Message ----- 
From: HYPERLINK "mailto:Arno.Schmidmeier@xxxxxxxxxxxxxx"Arno.Schmidmeier@xxxxxxxxxxxxxx 
To: HYPERLINK "mailto:gregor@xxxxxxxxx"gregor@xxxxxxxxx ; HYPERLINK "mailto:hugunin@xxxxxxxxxxxxxx"hugunin@xxxxxxxxxxxxxx ; HYPERLINK "mailto:Arno.Schmidmeier@xxxxxxxxxxxxxx"Arno.Schmidmeier@xxxxxxxxxxxxxx 
Sent: Monday, January 07, 2002 5:08 AM
Subject: AW: Problem with reusing (abstract) pointcuts


Hello,
 
What should the example look like?
A short description of a whole application, some design patterns or some classes and aspects, demonstrating the expected use?
For the last, I expect my sample at the beginning of this thread would be a good starting point.
If not what is missing?
 
regards
    Arno

-----Ursprüngliche Nachricht-----
Von: Gregor Kiczales [mailto:gregor@xxxxxxxxx]
Gesendet: Freitag, 28. Dezember 2001 09:44
An: HYPERLINK "mailto:rbodkin@xxxxxxxxxxxxxx"rbodkin@xxxxxxxxxxxxxx; HYPERLINK "mailto:hugunin@xxxxxxxxxxxxxx"hugunin@xxxxxxxxxxxxxx; HYPERLINK "mailto:Arno.Schmidmeier@xxxxxxxxxxxxxx"Arno.Schmidmeier@xxxxxxxxxxxxxx; users@xxxxxxxxxxx
Betreff: Re: Problem with reusing (abstract) pointcuts


I agree there is a whole in the language here.  As Jim said, it would be nice to have
some more examples of how people want to use it to know how best to fix it.
 
The following might help people come up with such examples.  Its the closest thing to
the mechanism Jim described that I can do in the current language.  It isn't super-pretty,
but it does give the flavor of pointcuts that are both extensible and globally advisable.
It works by using multiple pointcuts, and it requires using aspects that extend a base one.
 
Warning, it isn't beautiful!  But its easy, when reading code written this way, to imagine
its trivial conversion to a language semantics like Jim described.
 
The general idea is as follows:
 
 
 
abstract aspect BaseAspect {
 
    /* add to this */
    abstract pointcut when();
 
    before(): when() { beforeHook(); }
    after():  when() { afterHook();  }
 
    private void beforeHook() { }
    private void afterHook()  { }
 
    /* define advice on these */
    pointcut beforeHook(): call(void Moving.beforeHook());
    pointcut afterHook():  call(void Moving.afterHook());
    
}
 
aspect A extends BaseAspect {
    
    pointcut when(): call(public * *.*(..));
}
 
aspect B extends BaseAspect {
    
    pointcut when():  call(private * *.*(..));
}
 
aspect Foo {
    
    before(): BaseAspect.beforeHook() { }
    after():  BaseAspect.afterHook()  { }
 
}
    
 
 

----- Original Message ----- 
From: HYPERLINK "mailto:rbodkin@xxxxxxxxxxxxxx"";>Bodkin, Ron <rbodkin@xxxxxxxxxxxxxx> 
To: HYPERLINK "mailto:hugunin@xxxxxxxxxxxxxx"";>Hugunin, Jim <hugunin@xxxxxxxxxxxxxx> ; HYPERLINK "mailto:'Arno.Schmidmeier@xxxxxxxxxxxxxx'"'Arno.Schmidmeier@xxxxxxxxxxxxxx' ; HYPERLINK "mailto:users@xxxxxxxxxxx"users@xxxxxxxxxxx 
Sent: Thursday, December 20, 2001 2:23 PM
Subject: RE: Problem with reusing (abstract) pointcuts


I think extensible pointcuts would be very valuable for building aspect libraries. 
 
I think there are two, related, problems doing so today:
1) You have to directly extend an aspect to apply pointcuts to your system
2) You can't refer to an extensible (or abstract) pointcut definition in an aspect (or class) that's external to it

These imply a need to extend all the aspects in a library to use it, thereby violating encapsulation (you need to extend implementation aspects). A preferable alternative would be allowing one to write configuration aspects.
 
The extensible pointcut proposal could fix both of these problems. I would expect advice in another aspect to be able to get the most-extended version of a pointcut. In Jim's example, one could write advice in a DebugDisplay aspect that refers to Display.move(), which would intercept any call in the fully extended version.
 
This would allow one to define all the "public interface" pointcuts for a library in a single configuration aspect, and a user could apply the library by simply extending the pointcuts in one aspect. 
 
I wrote more about this on Nov. 1 ("perthis and abstract pointcuts question"), but I like adding extensible aspects rather than changing abstract aspects to behave this way.
 
Ron

-----Original Message-----
From: Hugunin, Jim <hugunin@xxxxxxxxxxxxxx> [mailto:hugunin@xxxxxxxxxxxxxx] 
Sent: Thursday, December 20, 2001 12:02 PM
To: 'Arno.Schmidmeier@xxxxxxxxxxxxxx'; users@xxxxxxxxxxx
Subject: RE: Problem with reusing (abstract) pointcuts


In AspectJ-1.0, the correct behavior when given this program should be to produce a compile-time error, "AbsAbs.pc2() references an abstract pointcut, so it can't be used in a static context".  This will be fixed for 1.0.2.
 
However, there's something very interesting that you're trying to do here.  There might be a way to extend the language in AspectJ-1.1 to support this sort of functionality.  A short compelling example for why this feature is important might help us to better understand it.
 
I've occasionally wanted a variant of this feature that completely ignores the aspect hierarchy.  Here's an extremely rough example of what that might look like (new syntax is in all-caps to make it stand out and emphasize the fact that this is a very rough idea):
 
class Display {
  EXTENSIBLE public pointcut move();
  ...
}
 
class Point {
  EXTEND pointcut Display.move(): call(void Point.setX(int)) || call(void Point.setY(int));
  ...
}
 
class Line {
  EXTEND pointcut Display.move(): call(void Line.set  ... 
}
 
Just like in our current figure example (see CACM article), this Display.move() pointcut can be very useful to give a name to a particular group of join points.  The change here is that the pointcut can be specified in pieces rather than as a monolithic entity.  The value of specifying the pointcut separately here is that it feels like it maps better on to the existing modularity of the system.  The developer of the Point class can update the appropriate pointcut whenever a new method is added that makes a point move.  I think that this idea is similar to what you're looking for with your version of abstract pointcuts.
 
I don't know if this is related to your desired use of abstract pointcuts or not, or at this point even if this idea would help improve the quality of AspectJ code.  My proposal above is intended mainly to provide a rough example for a semi-concrete way that I'd like to be able to discuss these sorts of features.
 
-Jim
 

-----Original Message-----
From: Arno.Schmidmeier@xxxxxxxxxxxxxx [mailto:Arno.Schmidmeier@xxxxxxxxxxxxxx]
Sent: Wednesday, December 12, 2001 7:52 AM
To: users@xxxxxxxxxxx
Subject: Problem with reusing (abstract) pointcuts



Hello, 

Following sample: 

public class AbstractPointCutReuserTest { 

    public AbstractPointCutReuserTest() { 
    } 
    public static void main(String[] args) { 
        AbstractPointCutReuserTest abstractPointCutReuserTest1 = new AbstractPointCutReuserTest(); 
        abstractPointCutReuserTest1.foo(); 
        abstractPointCutReuserTest1.bar(); 
    } 

    public void foo(){ 
        System.out.println("foo"); 
    } 

    public void bar(){ 
        System.out.println("bar"); 
    } 

} 

abstract aspect AbsAbs{ 
    pointcut pc2():pc(); 

    abstract pointcut pc(); 

    pointcut pc3():execution(* AbstractPointCutReuserTest.foo()); 

} 

aspect absfoo extends AbsAbs{ 

    pointcut pc():execution(* AbstractPointCutReuserTest.foo()); 

    before(): pc(){ 
        System.out.println("absfoo"); 
    } 
} 

aspect absfoo2 extends AbsAbs{ 
    pointcut pc():execution(* AbstractPointCutReuserTest.bar()); 

    before(): pc(){ 
        System.out.println("absfoo2"); 
    } 
} 


aspect XY{ 
    before ():AbsAbs.pc2(){ 
        System.out.println("pc2 XY"); 
    } 

    before ():AbsAbs.pc3(){ 
        System.out.println("pc3 XY"); 
    } 

} 
compiling and running it produces following output: 
pc3 XY 
absfoo 
foo 
absfoo2 
bar 


However what I expected would be: 
either a compile time error, because pc2, is made up from an abstract pointcut, or something like 
pc3 XY 
pc3 XY 
absfoo 
foo 
pc2 XY 
absfoo2 
bar 

regards 
    Arno 

P.S. I am using aspectJ 1.0 

--- End Message ---
--- Begin Message --- I think it would be really helpful to allow using abstract pointcuts that are implemented by a derived aspect outside that derived aspect. Another case where it would be useful is if you'd like to have one configuration class/aspect determine how a library/framework of abstract aspects applies to your concrete use, rather than having to extend each implementation aspect. This reduces coupling and unscatters your configuration.

How about the following proposal, as a starting point for discussion:

Whenever using a pointcut (such as in advice) it should be instantiated for ALL (known) pointcuts derived from that pointcut (i.e., pointcuts that extend the base definition in a derived class or aspect).

This would let you use the library/framework in different ways for different layers of your application (e.g., logging in the view and model layers or error handling). It's important that pointcuts are only useful at weave time to generate woven code - they aren't usefully "invoked" polymorphically. So it's appropriate to identify all the known derived pointcuts at weave time (unlike in OO inheritance where you need to be open to new code that's not known at compile time).

A potential drawback is a combinatorial explosion of pointcuts, but I see that as unlikely in practice.

Thoughts?
Ron

Hugunin, Jim wrote:
<snip>
Your version below won't work because a qualified reference to a pointcut, like "TransactionalObjects.persistent()" means use the persistent() pointcut as it is declared in the aspect TransactionalObjects -- and in that aspect you've declared this pointcut to match nothing.  If you make this pointcut declaration explicitly abstract, i.e. "abstract pointcut persistent();" you'll get a decent error message out of ajc for this sort of mistake.  The only way to use an overriden pointcut is in a subtype of the declaring aspect where the reference can be resolved relative to the subaspect.

Let me know if this works for you - Jim



-----Original Message-----
From: Frank Sauer [mailto:Frank.Sauer@xxxxxxxxxx]
Sent: Wednesday, October 31, 2001 11:41 AM
To: 'AspectJ User List'
Subject: perthis and abstract pointcuts question


I'm not sure if I'm going to be able to fully explain what
I'm trying to do,
but
I'll give it a shot:

I'm trying to construct a persistence layer that can turn
make any given set
of
domain classes transactional. It keeps track of all
modifications to those
classes
and at transactional boundaries commits the transaction. If any
transactional
method throws an exception, all modifications are rolled
back. I've got most
of this
working with the following aspects:

package persistence;
abstract aspect TransactionalObjects {
pointcut persistent(); // defines classes to be tracked
pointcut transactional(); // defines transactional methods
pointcut dirty(Object o): (persistent() && this(o) &&
set(* *.*));

// various advices
}

in client:

aspect MyTransactionalObjects extends TransactionalObjects {
pointcut persistent(): within(domain.*);
pointcut transactional():
call(* Client.createCustomer(..)) ||
...
}

In other words, the clinet ONLY has to make some pointcuts concrete
in order to use the persistence framework. So far so good, works like
a charm, but now I want to introduce a UUID into all
persistent objects.

There's two choice that I can see right now:

make the user add the following to his MyTransactionalObjects:

aspect MyTransactionalObjects extends TransactionalObjects {

declare parents: domain.Person || domain.Address extends UUID;

pointcut persistent(): within(domain.*);
pointcut transactional():
call(* Client.createCustomer(..)) ||
...< br>}

The problem with this is that the framework is not guaranteed
to always have
a
UUID for each persistent object because whoever is using it
has to properly
declare the parents, so I tried:

package persistence;

aspect UUIDObject extends UUID
perthis(TransactionalObjects.persistent()) {
}

This way, I thought, there would always be a UUID as long as
there are any
persistent classes defined. However, when I try to get to the
UUID using:

UUIDObject.aspectOf(<an instance of a class in domain
package>),

I get:

org.aspectj.lang.NoAspectBoundException
at persistence.UUIDObject.aspectOf(UUIDObject.java:3)

Now, if I change the above to:

aspect UUIDObjects extends UUID
perthis(MyTransactionalObjects.persistent())
{
}

In other words it's now using a concrete pointcut, it works fine.

The problem with this is that now the framework has to refer to a
non-framework aspect
(MyTransactionalObjects) wich would break the decoupling...

Is this just another manifestation of bug 559, or is this something
different entirely?

Frank Sauer
The Technical Resource Connection, Inc.
a wholly owned subsidiary of Perot Systems
Tampa, FL
http://www.trcinc.com
-----------------------------------------------
Java: The best argument for Smalltalk since C++

__________________________________________________
AspectJ users mailing list - users@xxxxxxxxxxx
To be removed send mail to users-admin@xxxxxxxxxxx
or visit http://aspectj.org/lists

__________________________________________________
AspectJ users mailing list - users@xxxxxxxxxxx
To be removed send mail to users-admin@xxxxxxxxxxx
or visit http://aspectj.org/lists


-- 
Ron Bodkin
h: (415) 285-8211
m: (415) 509-2895
f: (208) 692-5729


--- End Message ---