Production Aspects

This section presents examples of aspects that are inherently intended to be included in the production builds of an application. Production aspects tend to add functionality to an application rather than merely adding more visibility of the internals of a program. Again, we begin with name-based aspects and follow with property-based aspects. Name-based production aspects tend to affect only a small number of methods. For this reason, they are a good next step for projects adopting AspectJ. But even though they tend to be small and simple, they can often have a significant effect in terms of making the program easier to understand and maintain.

Change Monitoring

The first example production aspect shows how one might implement some simple functionality where it is problematic to try and do it explicitly. It supports the code that refreshes the display. The role of the aspect is to maintain a dirty bit indicating whether or not an object has moved since the last time the display was refreshed.

Implementing this functionality as an aspect is straightforward. The testAndClear method is called by the display code to find out whether a figure element has moved recently. This method returns the current state of the dirty flag and resets it to false. The pointcut move captures all the method calls that can move a figure element. The after advice on move sets the dirty flag whenever an object moves.

aspect MoveTracking {
    private static boolean dirty = false;

    public static boolean testAndClear() {
        boolean result = dirty;
        dirty = false;
        return result;
    }

    pointcut move():
        call(void FigureElement.setXY(int, int)) ||
        call(void Line.setP1(Point))             ||
        call(void Line.setP2(Point))             ||
        call(void Point.setX(int))               ||
        call(void Point.setY(int));

    after() returning: move() {
        dirty = true;
    }
}

Even this simple example serves to illustrate some of the important benefits of using AspectJ in production code. Consider implementing this functionality with ordinary Java: there would likely be a helper class that contained the dirty flag, the testAndClear method, as well as a setFlag method. Each of the methods that could move a figure element would include a call to the setFlag method. Those calls, or rather the concept that those calls should happen at each move operation, are the crosscutting concern in this case.

The AspectJ implementation has several advantages over the standard implementation:

The structure of the crosscutting concern is captured explicitly. The moves pointcut clearly states all the methods involved, so the programmer reading the code sees not just individual calls to setFlag, but instead sees the real structure of the code. The IDE support included with AspectJ automatically reminds the programmer that this aspect advises each of the methods involved. The IDE support also provides commands to jump to the advice from the method and vice-versa.

Evolution is easier. If, for example, the aspect needs to be revised to record not just that some figure element moved, but rather to record exactly which figure elements moved, the change would be entirely local to the aspect. The pointcut would be updated to expose the object being moved, and the advice would be updated to record that object. The paper An Overview of AspectJ (available linked off of the AspectJ web site -- http://eclipse.org/aspectj), presented at ECOOP 2001, presents a detailed discussion of various ways this aspect could be expected to evolve.

The functionality is easy to plug in and out. Just as with development aspects, production aspects may need to be removed from the system, either because the functionality is no longer needed at all, or because it is not needed in certain configurations of a system. Because the functionality is modularized in a single aspect this is easy to do.

The implementation is more stable. If, for example, the programmer adds a subclass of Line that overrides the existing methods, this advice in this aspect will still apply. In the ordinary Java implementation the programmer would have to remember to add the call to setFlag in the new overriding method. This benefit is often even more compelling for property-based aspects (see the section Providing Consistent Behavior).

Context Passing

The crosscutting structure of context passing can be a significant source of complexity in Java programs. Consider implementing functionality that would allow a client of the figure editor (a program client rather than a human) to set the color of any figure elements that are created. Typically this requires passing a color, or a color factory, from the client, down through the calls that lead to the figure element factory. All programmers are familiar with the inconvenience of adding a first argument to a number of methods just to pass this kind of context information.

Using AspectJ, this kind of context passing can be implemented in a modular way. The following code adds after advice that runs only when the factory methods of Figure are called in the control flow of a method on a ColorControllingClient.

aspect ColorControl {
    pointcut CCClientCflow(ColorControllingClient client):
        cflow(call(* * (..)) && target(client));

    pointcut make(): call(FigureElement Figure.make*(..));

    after (ColorControllingClient c) returning (FigureElement fe):
            make() && CCClientCflow(c) {
        fe.setColor(c.colorFor(fe));
    }
}

This aspect affects only a small number of methods, but note that the non-AOP implementation of this functionality might require editing many more methods, specifically, all the methods in the control flow from the client to the factory. This is a benefit common to many property-based aspects while the aspect is short and affects only a modest number of benefits, the complexity the aspect saves is potentially much larger.

Providing Consistent Behavior

This example shows how a property-based aspect can be used to provide consistent handling of functionality across a large set of operations. This aspect ensures that all public methods of the com.bigboxco package log any Errors they throw to their caller (in Java, an Error is like an Exception, but it indicates that something really bad and usually unrecoverable has happened). The publicMethodCall pointcut captures the public method calls of the package, and the after advice runs whenever one of those calls throws an Error. The advice logs that Error and then the throw resumes.

aspect PublicErrorLogging {
    Log log = new Log();

    pointcut publicMethodCall():
        call(public * com.bigboxco.*.*(..));

    after() throwing (Error e): publicMethodCall() {
        log.write(e);
    }
}

In some cases this aspect can log an exception twice. This happens if code inside the com.bigboxco package itself calls a public method of the package. In that case this code will log the error at both the outermost call into the com.bigboxco package and the re-entrant call. The cflow primitive pointcut can be used in a nice way to exclude these re-entrant calls:

after() throwing (Error e):
        publicMethodCall() && !cflow(publicMethodCall()) {
    log.write(e);
}

The following aspect is taken from work on the AspectJ compiler. The aspect advises about 35 methods in the JavaParser class. The individual methods handle each of the different kinds of elements that must be parsed. They have names like parseMethodDec, parseThrows, and parseExpr.

aspect ContextFilling {
    pointcut parse(JavaParser jp):
        call(* JavaParser.parse*(..))
        && target(jp)
        && !call(Stmt parseVarDec(boolean)); // var decs
                                              // are tricky

    around(JavaParser jp) returns ASTObject: parse(jp) {
        Token beginToken = jp.peekToken();
        ASTObject ret = proceed(jp);
        if (ret != null) jp.addContext(ret, beginToken);
        return ret;
     }
}

This example exhibits a property found in many aspects with large property-based pointcuts. In addition to a general property based pattern call(* JavaParser.parse*(..)) it includes an exception to the pattern !call(Stmt parseVarDec(boolean)). The exclusion of parseVarDec happens because the parsing of variable declarations in Java is too complex to fit with the clean pattern of the other parse* methods. Even with the explicit exclusion this aspect is a clear expression of a clean crosscutting modularity. Namely that all parse* methods that return ASTObjects, except for parseVarDec share a common behavior for establishing the parse context of their result.

The process of writing an aspect with a large property-based pointcut, and of developing the appropriate exceptions can clarify the structure of the system. This is especially true, as in this case, when refactoring existing code to use aspects. When we first looked at the code for this aspect, we were able to use the IDE support provided in AJDE for JBuilder to see what methods the aspect was advising compared to our manual coding. We quickly discovered that there were a dozen places where the aspect advice was in effect but we had not manually inserted the required functionality. Two of these were bugs in our prior non-AOP implementation of the parser. The other ten were needless performance optimizations. So, here, refactoring the code to express the crosscutting structure of the aspect explicitly made the code more concise and eliminated latent bugs.