## Advanced Aspect-Oriented Programming with AspectJ

### Organization

This tutorial consists of the attendees solving AspectJ programming tasks (with unit tests) in pairs or triples and discussing the answers with the group and with the tutorial presenters. The exercises work with a figure editor together with JUnit test cases. They progress, as most users do in their adoption of AspectJ, from non-functional, development-only aspects to aspects which augment a deployed program with crosscutting features.

These notes consist of four sections of exercises, a quick reference to AspectJ syntax, and a UML diagram of a figure editor program. While you should feel free to have a quick look through these notes before the tutorial, please do not try to seriously read or do the exercises; you'll learn a lot more by working through it in groups.

### Command-line usage

At the beginning of the tutorial we will make available a binary package that includes the tests, the base code, JUnit, and a distribution of AspectJ. All it needs is information about where Java lives (so set your JAVA_HOME environment variable). It assumes that you unzip it in c:\ (on Windows) or in your home directory (on Linux): If you put it somewhere else, edit setpaths or setpaths.bat, as appropriate. Once all this is done, run `setpaths.bat` or `source setpaths` to export some other needed environment variables.

All the files in the program are listed in base.lst, including test cases and an empty answer aspect, `answers/Answer.java`. Therefore, if you write your answers there, all you need to do is compile base.lst, either in an IDE or with

>     $ ajc -argfile base.lst

Before you move onto another exercise, though, make sure to copy your answer into a different file so we can discuss the answers together:

>     > copy answers/Answer.java answers/2a.java  (Windows)
>     $ cp answers/Answer.java answers/2a.java    (Unix)

After building the system, you should invoke Java on the compiled test class. On the command-line, this this would be

>     $ java tests.Test2a

(For these exercises, when we give examples of execution we will show the command-line use, but of course if you are using JBuilder, Forte/NetBeans, Emacs, or Eclipse, use the appropriate compile and execute tools.)

The default test, `tests.Test`, performs some rudimentary tests on figure elements, and so is a useful test to run periodically. Looking at the JUnit tests for each exercise may also be helpful.

Again, we will be looking at some solutions and having discussion, which is much more difficult without incremental solutions. So when you go from one exercise to the next, make sure to save your work in a file and go on to work in a different file, even if you plan to duplicate some code.

### Environment

You may use whatever editor or environment you choose to work through these exercises. We provide a simple code-browser that can work well as an editor for these short exercises, in addition to providing better visualization of how aspects affect the system:

>     $ ajbrowser base.lst

With the browser you can edit code (including the `answers/Answer.java` file), and after saving hit the build button to start an ajc compile. We recommend you start up another shell, though, to run the JUnit tests (and don't forget to run the `setpaths` script when you open the new shell): You could set up the run button to run a test through the Options menu, but we've found this is fairly cumbersome.

## 1. Static Invariants

The easiest way to get started with AspectJ is to use it to enforce static invariants.

### a. Catch old tracing

Sample Exercise: The main point of this exercise is to make sure your configuration works. Type in the answer below into your answer file, make sure you get the desired compile-time error, remove the offending line, and make sure you pass the JUnit test.

Task: Signal an error for calls to `System.out.println`.

The way that we are all taught to print "hello world" from Java is to use `System.out.println()`, so that is what we typically use for one-off debugging traces. It's a common mistake to leave these in your system longer than is necessary. Type in the aspect below to force an error at compile time if this mistake is made.

When you use this on the given system, you'll find one incorrect trace in `SlothfulPoint`.

>     $ ajc -argfile base.lst
>     ./figures/SlothfulPoint.java:38 illegal access to System.out

>     1 error

Remove the illegal tracing call.

Make sure your program still passes the JUnit test `tests.Test` (which it should also pass at the beginning of all exercises) before continuing.

>     $ java tests.Test
>     ....
>     Time: 0.03

>     OK (4 tests)

Answer:

>     package answers;

>     import figures.*;

>     aspect Answer1a {
>         declare error
>             : get(java.io.PrintStream System.out) && within(figures..*)
>             : "illegal access to System.out";
>     }

Note that this answer does not say that the call to the `println()` method is incorrect, rather, that the field get of the `out` field is illegal. This will also catch those users who bind System.out to a static field to save typing.

### b. Mandate setters

Task: Signal a warning for assignments outside of setter methods.

Tools: `set`, `withincode`, signature `void set*(..)`

One common coding convention is that no private field should be set outside of setter methods. Write an aspect to warn at compile time when such an illegal assignment expression exists.

This is going to look like

    aspect A {
        declare warning: <pointcut here> : "bad field set";
    }

where the pointcut picks out join points of private field sets outside of setter methods. "Outside", here, means that the code for the assignment is outside the text of the setter.

Make sure your program still passes the JUnit test `tests.Test` before continuing. Make sure you get 11 warnings from this. Wait to fix them until the next exercise.

### c. Refine setters mandate

Task: Allow assignmnents inside of constructors.

Tools: signature `new(..)`

Look at some of the warnings from the previous exercise. Notice that a lot of them are from within constructors. Actually, the common coding convention is that no private field should be set outside of setter methods or constructors. Modify your answer to signal an actual error at compile time (rather than just a warning) when such an illegal assignment expression exists.

You'll want to add another `withincode` primitive pointcut to deal with the constructors.

After you specify your pointcut correctly, you'll still find that the convention is violated twice in the figures package. You should see the following two errors:

    .\figures\Point.java:37 bad field set
    .\figures\Point.java:38 bad field set

    2 errors

Rewrite these two occurrences so as not to violate the convention. Make sure your program still passes the JUnit test `tests.Test` before continuing.

### d. Congratulations

You've taken your first steps. At this point, check the people to your left and right. If they're stuck somewhere, see if you can help them.

## 2. Dynamic invariants

The next step in AspectJ adoption is often to augment a test suite by including additional dynamic tests.

Tutorial attendees typically progress at different speeds through these questions. Throughout this tutorial, if you finish early, see what the people around you are doing and if they need help. Feel free to help them out of naked self-interest; we promise you'll learn a lot about AspectJ by explaining it.

### a. Check a simple precondition

Sample Exercise: We've provided the answer to this exercise below to get you started.

Task: Pass `tests.Test2a`.

Tools: `args`, `before`

Write an aspect to throw an `IllegalArgumentException` whenever an attempt is made to set one of `Point`'s `int` fields to a value that is less than zero.

This should make the test case of `tests.Test2a` pass, which wouldn't without your aspect. So before compiling in the aspect,

>     $ java tests.Test2a
>     .F..F....
>     Time: 0.04
>     There were 2 failures:
>     1) testTooSmall(tests.Test2a)junit.framework.AssertionFailedError: should have thrown IllegalArgumentException
>     2) testMove(tests.Test2a)junit.framework.AssertionFailedError: should have thrown IllegalArgumentException

>     FAILURES!!!
>     Tests run: 7,  Failures: 2,  Errors: 0

But after compiling in the aspect...

>     $ ajc -argfile base.lst answers/Answer.java

>     $ java tests.Test2a
>     .......
>     Time: 0.04

>     OK (7 tests)

Answer:

>     package answers;

>     import figures.*;

>     aspect Answer2a {
>         before(int newValue): set(int Point.*) && args(newValue) {
>             if (newValue < 0) {
>                 throw new IllegalArgumentException("too small");
>             }
>         }
>     }

### b. Check another precondition

Task: Pass `tests.Test2b`.

Tools: `call`.

`Group` is a `FigureElement` class that encapsulates groups of other figure elements. As such, only actual figure element objects should be added to `Group` objects. Write an aspect to throw an `IllegalArgumentException` whenever `Group.add()` is called with a `null` value.

Look at `tests/Test2b.java` to see exactly what we're testing for.

### c. Check yet another precondition

Task: Pass `tests.Test2c`.

Tools: `target`

Another constraint on a well-formed group is that it should not contain itself as a member (though it may contain other groups). Write an aspect to throw an `IllegalArgumentException` whenever an attempt is made to call `Group.add()` on a `null` value, or on the group itself.

You will want to use a `target` pointcut to expose the `Group` object that is the target of the `add` call.

### d. Assure input

Task: Pass `tests.Test2d`.

Tools: around advice

Instead of throwing an exception when one of `Point`'s `int` fields are set to a negative value, write an aspect to trim the value to zero. You'll want to use `around` advice that exposes the new value of the field assignment with an `args` pointcut, and `proceed` with the trimmed value.

This is going to look something like

    aspect A {
        void around(int val): <Pointcut> {
            <Do something with val>
            proceed(val);
        }
    }

### e. Check a postcondition

Task: Pass `tests.Test2e`

Tools: around advice

A postcondition of a `Point`'s `move` operation is that the `Point`'s coordinates should change. If a call to move didn't actually move a point by the desired offset, then the point is in an illegal state and so an `IllegalStateException` should be thrown.

Note that because we're dealing with how the coordinates change during move, we need some way of getting access to the coordinates both before and after the move, in one piece of advice.

### f. Check another postcondition

Task: Pass `tests.Test2f`

Tools: the `Rectangle(Rectangle)` constructor, the `Rectangle.translate(int, int)` method.

`FigureElement` objects have a `getBounds()` method that returns a `java.awt.Rectangle` representing the bounds of the object. An important postcondition of the general `move` operation on a figure element is that the figure element's bounds rectangle should move by the same amount as the figure itself. Write an aspect to check for this postcondition -- throw an `IllegalStateException` if it is violated.

## 3. Tracing

Tracing is one of the classic AspectJ applications, and is often the first where AspectJ is used on deployed code.

### a. Simple logging

Task: Pass `tests.Test3a`.

Tools: `Log.log(String)`, `thisJoinPoint.toString()`, `execution`, `within`

Write an aspect to log the execution of all public methods in the figures package. To do this, use the utility class `Log` (with an import from `support.Log`) and call `Log.log(String)`

### b. Exposing a value

Task: Pass `tests.Test3b`.

Tools: `target`

AspectJ can expose the target object at a join point for tracing. In this exercise, you will print not only the join point information, but also the target object, with the form

>     thisJoinPointInfo at targetObject

### c. More specialized logging

Task: Pass `tests.Test3c`.

Tools: `args`.

Write an aspect to log whenever a `Point` is added to a group. The `args` pointcut allows you to select join points based on the type of a parameter to a method call.

Look at the test case for details about the tested log message.

### d. Logging extended to checking an invariant

Task: Pass `tests.Test3d`.

Tools: `inter-type field declaration`

Make sure that a Point is never added to more than one Group. To do so, associate a boolean flag with each Point using an inter-type declaration, such as

>     boolean Point.hasBeenAdded = false;

Check and set this flag with the same kind of advice from your answer to problem (c). Throw an `IllegalStateException` if the point has already been added.

### e. Better error messages for 3d

Task: Pass `tests.Test3e`.

Tools:

Extend your solution to problem (d) by using the string representation of the Point's containing group as the `msg` part of the `IllegalStateException`.

## 4. Caching

Computation of the bounding box of `Group` objects needs to deal with all aggregate parts of the group, and this computation can be expensive. In this section, we will explore various ways of reducing this expense.

Optional: In all of these exercises, you should only deal with points that are added directly to Groups, rather than those that are added "indirectly" through Lines and Boxes. You should handle those points contained in Lines and Boxes only if time permits.

### a. Make a constant override

Task: Pass `tests.Test4a`.

Tools: `around`, `FigureElement.MAX_BOUNDS`

`Group`'s `getBounds()` method could be understood to be a conservative approximation of the bounding box of a group. If that is true, then it would be a legal (and much faster) implementation of `getBounds()` to simply always return a rectangle consisting of the entire canvas. The entire canvas is returned by the static method `FigureElement.MAX_BOUNDS`.

Write an aspect to implement this change. You can override `Group`'s `getBounds()` method entirely with around advice intercepting the method.

### b. Make a constant cache

Task: Pass `tests.Test4b`.

Tools: inter-type field.

Instead of making the (very) conservative approximation of `getBounds()` from part (a), write an aspect instead that remembers the return value from the first time `getBounds()` has been called on a `Group`, and returns that first `Rectangle` for every subsequent call.

Hint: You can use an inter-type declaration to keep some state for every `Group` object.

### c. Invalidate, part 1

Task: Pass `tests.Test4c`.

Tools: `before`

While caching in this way does save computation, it will lead to incorrect bounding boxes if a `Group` is ever moved. Change your aspect so that it invalidates the cache whenever the `move()` method of `Group` is called.

### d. Invalidate, part 2

Task: Pass `tests.Test4d`.

Of course, part (c) didn't really solve the problem. What if a `Point` that is part of a `Group` moves? Whenever either of a Point's fields are set it should invalidate the caches of all enclosing groups. Use your solution to problem 3c to modify your invalidation criteria in this way, but note that this is slightly different than the problem in 3c: Here you care about fields, where there you cared about method calls.

### e. Invalidate, part 3

Task: Pass `tests.Test4e`.

Tools: You're on you're own

Did you really do part (d) correctly? Run the JUnit test `tests.Test4e` to see. If you pass, congratulations, now go help other people. Otherwise, you have fallen prey to our cruel trap: Remember that whenever a point moves it should invalidate the caches of all enclosing groups.