Skip to main content

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [List Home]
[aspectj-dev] using classes for pointcut libraries

This is a pattern to use concrete pointcuts in classes
and inheritance to achieve some of the effects Adrian
wants in interface pointcuts.  It's similar to what
I suggested wrt abstract aspects, but is a bit
more modular and less constraining.

----
class PrivateLibrary {
   public pointcut pc() : {definition};
}

// perhaps "final class.."
public class Library extends PrivateLibrary {}

aspect Client {
    before() : Library.pc() { ... }
}

aspect Deployer {
    declare parents: Library extends MyLibrary;
    static class MyLibrary extends PrivateLibrary {
        public pointcut pc() : {re-definition};
    }
}
----
Effects:

- Clients refer to library pointcuts

- A deployer can select from available implementations
  and affect all clients of the library.  Conservatively,
  this requires reweaving the clients.

- Different clients can be made to refer to different
  implementations, depending on the kind of reference
  they make.

The code below shows you can use the current scope as an
implicit class reference for the pointcut.  Because
this scope can be changed using inter-type declarations
of parent class, you can specify implementations and
affect clients differently, depending on the path used
to resolve their reference to a pointcut.  The same
effect holds for clients that use explicit class
references if the referent member is found by
searching the supertypes rather than directly
in the referent supertype.

Some drawbacks:
- single-inheritance: obviously, using a class in Java
limits one's ability to re-use by inheritance.  But I
suspect most uses should be via explicit class reference
rather than inheritance.  (Indeed, some e.g., Bloch think
it bad style to use interfaces to simplify references
to constants; see next.)

One way to require clients use explicit references is
to to mark a pointcut library class final.  However, that
prevents there being two or more implementations in the
same namespace.  If developers want to be able to
have different pointcuts for different clients, they
can instead make client-specific library subtypes
that can be targetted for reimplementation.

----
...
public class Library extends PrivateLibrary {}

public class AppLibrary extends Library {}
public class BeanLibrary extends Library {}

aspect ClientOne {
    before() : AppLibrary.pc() { ... }
}

aspect ClientTwo {
    before() : BeanLibrary.pc() { ... }
}

aspect Deployer {
    declare parents: BeanLibrary extends MyBeanLibrary;
    declare parents: AppLibrary extends MyAppLibrary;
    ...
}
----

To some, a more natural way to acknowledge that there
is a search through supertypes for a referent might
be to use inheritance and unqualified references:

----
aspect BeanClient extends BeanLibrary {
    before() : pc() { ... }
}
----

- Style: using pointcuts is like using static methods.
Some consider it poor style to use unqualified
references to static members or to use static subtype
references to refer to supertype members. I agree with
this style point in regular Java code, but it's not
enough to convince me not to use this idiom.  For
extensible pointcuts, we do need to change the referent,
but at compile/weave time, not at runtime. (Dynamic
aspects would be nice in many ways, but this ain't
that.)  This is better than interface pointcuts in
that they don't lead people to believe it is
something like method polymorphism based on the
executing instance.  (The way to do that in AspectJ
is to have different subaspects of an abstract
aspect.)

One advantage of using classes rather than abstract
aspects is that it's clear the pointcuts are defined
and there is no associated behavior or state, as there
might be when inheriting from an abstract aspect. It's
also not as constraining, since you can inherit from
classes in more places than you can from abstract
aspects.  And I personally believe it is better than
using interfaces for abstract pointcuts.  I would
support interfaces if the pointcut definitions
varied at runtime based on the instance, assuming
that wouldn't prevent good compiler-based
implementations of AspectJ.

If this survives scrutiny, I'll toss it in with
the sample code.

Wes

(btw, this suggests that the algorithm for incremental
compilation needs to consider changes anywhere in the
aspect type hierarchy or to the type hierarchy of a
pointcut referent.  I'm also assuming/hoping that
weaving binaries has the same effect as compiling
wrt the pointcut selected by searching up the type
hierarchy.)

Here's some test code.  This will NOT work in 1.1.0
because of the bug with named pointcuts using cflow,
but does work using the compiler in the current tree.

----------- lib/PointcutLibraryTest.java

package lib;

public class PointcutLibraryTest {
    public static void main(String[] a) {
        new Test().run();
    }
}

class Test {
    public Test() {}
    public void run(){ prun(); }
    private void prun() {
        System.out.println("Test.prun()");
    }
}

/** private default implementation of library */
class PrivatePointcutLibrary {
    pointcut adviceCflow() : cflow(adviceexecution());
    pointcut publicCalls() : call(public * *(..))
        && !adviceCflow()
        ;
}

/** public interface for library */
class PointcutLibrary extends PrivatePointcutLibrary {
}

// ---- different clients of the library

/** use library by inheriting scope in aspect */
aspect AEL extends PointcutLibrary {
    before() : publicCalls() {
        System.out.println("AEL: "
            + thisJoinPointStaticPart);
    }
}

/** use library by inheriting scope in class */
class CEL extends PointcutLibrary {
    static aspect A {
        before() : publicCalls() {
            System.out.println("CEL: "
                + thisJoinPointStaticPart);
        }
    }
}

/** more clients by inheritance */
aspect CELSubAspect extends CEL {
    before() : publicCalls() {
        System.out.println("CSA: "
            + thisJoinPointStaticPart);
    }
}



/** client by external reference to library */
aspect ExternalClientOfLibrary {
  before() : PointcutLibrary.publicCalls() {
      System.out.println("XCL: "
          + thisJoinPointStaticPart);
  }
}

// ---- redefining library pointcuts

//-- affect all clients of PointcutLibrary
// test: XCL advises Test()
class VendorPointcutLibrary extends PrivatePointcutLibrary {
    /** add calls to public constructors */
    pointcut publicCalls() : PrivatePointcutLibrary.publicCalls()
        || (call(public new(..)) && !adviceCflow());
    static aspect A {
        declare parents:
            PointcutLibrary extends VendorPointcutLibrary;
    }
}

//-- only affect CEL, subtypes, & references thereto
// test: CSA does not advise call(void println(String))
// test: CSA advises call(void prun())
class CPlus extends PointcutLibrary {
    /** add calls to private methods, remove calls to java..* */
    pointcut publicCalls() : (PointcutLibrary.publicCalls()
        || (call(private * *(..)) && !adviceCflow()))
        && (!(call(* java..*.*(..)) || call(java..*.new(..))));
    static aspect A {
        declare parents: CEL extends CPlus;
    }
}






Back to the top