Introduction

You are currently browsing the weblog archives for May, 2004.

Archive for May, 2004

Monday, May 10th, 2004

AOP: Compile-Time Checks

A cool mechanism AspectJ offers is to declare compile-time errors and warnings. You can use this mechanism to ensure that certain rules specified in a specification don’t get broken. For example Swing’s single-thread rule. You declare errors using the following syntax:

  1. declare error : <pointcut> : <message>;

and warings using a similar one:

  1. declare warning : <pointcut> : <message>;

Errors as well as warnings will be issued when the compiler detects a join point matching a given pointcut. Errors would abort the compilation process, while warnings would just be displayed.

Example:

  1. declare error : callToHiddenCode() : "You are not allowed to call this code.";

This would produce an error if you’d call a join point captured by the pointcut callToHiddenCode().

Saturday, May 8th, 2004

AOP: Passing Context

One thing I haven’t talked about yet is passing context from a join point to an advice. An advice often needs certain information about the join point. For example the arguments passed to the join point, the class the join point is declared in and so on. All this information is called context. There do exist special pointcuts to capture these context. ApsectJ provides the following: this(), target() and args(). Let’s directly step into it:

We have a package called org.swacker.geom. In this package are by now two classes, Rectangle and Circle. These two classes implement the interface Shape. This interface declares one method: fill(newColor:Color). What we want to do is logging the calls to any class implementing this interface and printing out the name of this class as well as the new color the shape shall be filled with. Our final ouput shall look like this:

  1. About to fill a ‘org.swacker.geom.Rectangle’ instance with the color ‘#000000′.

To achieve this we somehow must get down to the target of the call as well as the arguments passed to the method. What we need is the target() and args() pointcut. We have everything we need now. Thus let’s start coding:

  1. public aspect ShapeAspect {
  2.   before(Shape shape, Color color) :
  3.       call(void Shape+.fill(Color))
  4.       && target(shape)
  5.       && args(color) {
  6.     System.out.println("About to fill a ‘" + shape.getClass().getName() + "’ instance with the color ‘" + color + "’.");
  7.   }
  8. }

We’ll now walk through this step by step.
We have an advice specification: before(Shape shape, Color color). It’s a before advice that collects some context. An instance of type Shape and another of type Color. What we also have are three pointcuts that are connected by && operators. We firstly capture all calls to the method fill() that is implemented in the class Shape or any of its subclass: call(void Shape+.fill(Color)).
Then we say that the target of the call shall be mapped to the context named shape in the advice specification: target(shape).
What we also do is taking the first argument of the method call and say that it shall me mapped to the context named color. This mapping will now be done automatically and we can use the context to build our output: System.out.println(”About to fill a ‘” + shape.getClass().getName() + “‘ instance with the color ‘” + color + “‘.”);.

When we now write a little test which body looks similar to this:

  1. Rectangle r = new Rectangle();
  2. r.fill(new Color(#000000));
  3. Circle c = new Circle();
  4. c.fill(new Color(#0000FF));

and compile the whole thing together with the aspect ShapeAspect we will get the following output:

  1. About to fill a ‘org.swacker.geom.Rectangle’ instance with the color ‘#000000′.
  2. About to fill a ‘org.swacker.geom.Circle’ instance with the color ‘#0000FF’.
Thursday, May 6th, 2004

AOP: Advice

As I already said, an advice is the code to be executed at a join point that has been selected by a pointcut. There exist three types of advices: the before, after and around advice. The before advice executes before the execution of a join point, the after advice after the execution of a join point and the around advice surrounds the join point’s execution. We will now look at each advice in turn.

Before advice:

  • Structure:
    before() : <Pointcut> {

    }
  • Example:
    before() : call(* MyClass.myMethod()) {
    System.out.println(”about to execute MyClass.myMethod()”);
    }

After advice:

  • Structure:
    after() : <Pointcut> {

    }

    after() returning(<ReturnType returnObject>) : <Pointcut> {

    }

    after() throwing(<ExceptionType exceptionObject>) : <Pointcut> {

    }

  • Example:
    after() : call(* MyClass.myMethod()) {
    System.out.println(”executed MyClass.myMethod()”);
    }

    after() returning(int result) : call(* int MyClass.myMethod()) {
    System.out.println(”The method MyClass.myMethod() returned ” + result);
    }

    after() throwing(Exception e) : call(* int MyClass.myMethod() throws Exception) {
    System.out.println(”The method MyClass.myMethod() threw the exception ” + e);
    }

  • Explanation:
    As you can see is it also possible to capture the result of a method call or the exception the method threw. You can deal with these the same way you do with normal arguments/parameters.

Around advice:

  • Explanation:
    The before and after advice are quite simple. But the around advice will need a little more explanation.
    If you do not explicitly call the method proceed() in the around advice the originally called method won’t be executed. The method will then just be bypassed. When calling proceed() you must pass the same number and types of arguments as collected by the advice. The proceed() method will also return the result of the method call.
    Another thing you have to do is specifying the return type the adviced join point has. If you are not sure what the return type of the join point is because you capture multiple different join points independent of their return type use Object. AspectJ will then automatically adjust the type based on the return type of the particular join point.
  • Structure:
    <returnType> around() : <Pointcut> {

    }
  • Example:
    int around() : call(* int MyClass.myMethod()) {
    // doSomething
    int result = proceed(); //
    // doAnotherThing
    return result;
    }
Tuesday, May 4th, 2004

AOP: Lexical-Structure Based Pointcuts

You can capture join points based on their lexical structure, that means on the scope of the code as it was written. There do exist two lexical-structure pointcuts: within(Type) and withincode(Method-/ConstructorSignature). I’ll explain how the function with the help of two examples:

within(MyClass+)
This would capture all join points inside the lexical scope of the class MyClass and all its subclasses.

withincode(* MyClass.myMethod(..))
This would capture all join points inside the lexical scope of any myMethod() method of the class MyClass.

I have used wildcards in the examples to make the explanation simpler. Refer to the article AOP: Wildcards if you do not understand what ‘*’, ‘+’ or ‘..’ mean.

You probably wonder why one would need lexical-scope based pointcuts. The answer is quite simple. When you capture calls to methods like System.out.println() that are probably also used within the aspect itself you must exclude this calls to avoid recursion.

Example:
call (* System.out.println(..)) && !within(MyAspect)
This pointcut will capture all calls to the System.out.println() method excluding the calls from within the aspect MyAspect.

I won’t write any more articles on pointcuts. But there do also exist pointcuts to capture the execution object and the arguments as well as conditional check pointcuts. I have found a comprehensive article on http://dev.eclipse.org called Pointcuts. Just refer to this article to learn about everything you have to know about pointcuts.

Tuesday, May 4th, 2004

AOP: Control-Flow Based Pointcuts

If you wanna catch join points that occur within the control flow of another join point you need control-flow based pointcuts. There do only exist two control-flow based pointcuts: cflow(Pointcut) and cflowbelow(Pointcut). The cflow(Pointcut) pointcut will capture all joint points that occur within the specified pointcut, including the join points matching the pointcut itself. Whereas the cflowbelow(Pointcut) pointcut will only capture the join points that occur within the join points matching the specified pointcut.

Example:
cflow(call(* MyClass.myMethod(..))
This pointcut would capture all join points that occur inside any myMethod() method in MyClass that is called, including the call to myMethod() itself.

cflowbelow(call(* MyClass.myMethod(..))
If you’d change cflow to cflowbelow the call to myMethod() would not be captured.