Modifying behavior with dynamic crosscutting
99Collecting join point context
➥ Simulated failure occurred Retrying...
Replying Reply is 5
The output shows a few failures, some retries, and eventual success. (Your output may be different due to the randomness introduced.) It also shows the correct assignment to the retVal member in the RemoteClient class, even though the advice returned the Object type.
In chapter 3, we alluded to an important function of a pointcut: collecting join point context. Now is a good time to understand how pointcuts collect context so that you can use it in dynamic crosscutting.
4.4
Collecting join point context
Consider data-driven security. The permission-check logic needs access to the method and objects involved—referred to as join point context—to make a decision if the caller possesses the authority to access the objects. Pointcuts, therefore, need to expose the context at the point of execution to pass it to the advice implementation. Join point context comes in two forms:
■ Objects (including primitives) involved at the join point ■ Annotations associated with the join point
AspectJ provides the this(), target(), and args() pointcuts to collect the objects at the advised join points. It provides @this(), @target(), @args(), @annotation(), @within(), and @withincode() pointcuts to collect annotations associated with the advised join points.
4.4.1 Collecting objects at the join point
You’ll recall from chapter 3 that you can specify each of the context-collecting point- cuts two ways:
■ Using the type of the objects
■ Using ObjectIdentifier, which is the name of the object
When advice needs the join point context, you use a pointcut that uses the Object- Identifier. With this form, you need to remember two things:
■ The advice declares the collected objects in much the same way as a method
would—each argument specifies a type and name. Then, you use pointcuts to bind each argument to a join point context.
For example, in figure 4.4, the anonymous pointcut in the before advice declares all the arguments to be collected by the pointcut. The declaration for collected context specifies the type of each collected object. The target() pointcut specifies account as the object identifier for the collected object, whose type is specified in the pointcut declaration as Account.
■ The matched pointcuts are implicitly restricted to an equivalent pointcut that
uses the type of the object identifier specified.
For example, in figure 4.4, the matched join points are limited to target (Account), because the type of the account identifier is Account. This restricts any matching to join points, where the target object is of the Account type (even if you omitted the call() portion of the pointcut expression). See section 3.6.2 for details of how matching works.
Figure 4.4 shows an example of collecting join point context and using it in an advice. The target() pointcut collects the objects on which the credit() method is being invoked, whereas the args() pointcut col- lects the argument to the method. The part of the advice before the colon specifies the type and name for each of the collected arguments. The body of the advice uses the collected context in the same way that the body of a method would use the parame-
ters passed to it. The object identifiers in the code in the figure are account and amount. When you use named pointcuts, those pointcuts must collect the context and pass it to the advice. Figure 4.5 shows the collection of the same information as in figure 4.4 but uses named pointcut to capture the context and make it available to the advice.
The pointcut creditOperation(), besides selecting join points, collects the context so that the advice can use it. You collect the target object and the argument to the credit() operation. Note that the pointcut declares the type and name of each col- lected element, much like a method call. The names of the arguments in the first part of the advice match those in the pointcut definition.
Let’s look at some more examples of passing context. In figure 4.6, you collect the security annotation associated with the method and use its property to perform authorization.
As we discussed earlier in this section, when you’re collecting context, an implicit restriction on matched join points limits you to only those that can satisfy the col- lected context. In this example, only join points that carry an @Secured annotation
float amount amount creditOperation
pointcut creditOperation Account account float amount
before (Account account, ) : (account , ) { System.out.println("Crediting " + amount + " to " + account ); }
( , ) :
call (void Account.credit(float)) && target ( account )
&& args ( amount );
Figure 4.5 Passing an executing object and an argument captured by a named pointcut. This code snippet is functionally equivalent to figure 4.4 but uses a named pointcut. For the advice to access the join point’s context, the pointcut must collect the context, as opposed to the advice collecting the context when using anonymous pointcuts. Account account amount void Account float amount before ( , ) : call ( .credit(float)) && target ( account )
&& args ( amount ) {
System.out.println("Crediting " + + " to " + account );
}
Figure 4.4 Passing an executing object and an argument context from the join point to the advice body. The target object in this case is collected using the target() pointcut, whereas the argument value is collected using the args() pointcut.
101