Friday, 11 July 2008

AspectJ Cookbook By Russell Miles

Just now i completed reading AspectJ Cookbook By Russell Miles

I wanted to share few important points i found from this book.

1) Cross-Cutting Concerns

The basic premise of aspect-oriented programming is to enable developers to express modular cross-cutting concerns in their software. So what does this mean? A cross-cutting concern is behavior, and often data, that is used across the scope of a piece of software. It may be a constraint that is a characteristic of your software or simply behavior that every class must perform.

The most common example of a cross-cutting concern, almost the "Hello World" of the aspect-oriented approach, is that of logging. Logging is a cross-cutting concern because it affects many areas across the software system and it intrudes on the business logic. Logging is potentially applied across many classes, and it is this form of horizontal application of the logging aspect that gives cross-cutting its name.

2) Aspects

An aspect is another term for a cross-cutting concern. In aspect orientation, the aspects provide a mechanism by which a cross-cutting concern can be specified in a modular way. To fully harness the power of aspects, we need to have some basic concepts in place to allow us to specify and apply aspects in a generic manner. We must be able to:

3) Advice

The code that is executed when an aspect is invoked is called advice. Advice contains its own set of rules as to when it is to be invoked in relation to the join point that has been triggered.

4) Join Points

Join points are simply specific points within the application that may or may not invoke some advice. The specific set of available join points is dependent on the tools being used and the programming language of the application under development. The following join points are supported in AspectJ:

Join when a method is called

Join during a method's execution

Join when a constructor is invoked

Join during a constructor's execution

Join during aspect advice execution

Join before an object is initialized

Join during object initialization

Join during static initializer execution

Join when a class's field is referenced

Join when a class's field is assigned

Join when a handler is executed

5) Pointcuts

Pointcuts are the AspectJ mechanism for declaring an interest in a join point to initiate a piece of advice. They encapsulate the decision-making logic that is evaluated to decide if a particular piece of advice should be invoked when a join point is encountered.

6) Deploying a Java Servlet That Uses AspectJ

a) Create a new asjectj project and create a servlet and also an aspect file which monitors the servlet i.e appending a title for the html page.



import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class AOHelloWorldServlet extends HttpServlet
{
public void doGet (HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException
{
ServletOutputStream out = response.getOutputStream();
out.println("<h1>Hello World from an aspect-oriented Servlet!</h1>");
}

public String getServletInfo()
{
return "Create a page that says <i>Hello World</i> and send it back";
}
}


import java.io.IOException;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public aspect AddHTMLHeaderAndFooter
{
public pointcut captureHttpRequest(HttpServletRequest request, HttpServletResponse response) :
execution(public void AOHelloWorldServlet.doGet(HttpServletRequest, HttpServletResponse)) &&
args(request, response);

// Could have used around with proceed but for performance reasons as described in the pointcut chapters
// it is preferrable wherever thinsg are not too disruptive to use before and after instead.
before(HttpServletRequest request, HttpServletResponse response) throws IOException : captureHttpRequest(request, response)
{
response.setContentType("text/html");
ServletOutputStream out = response.getOutputStream();
out.println("<html>");
out.println("<head><title>Adding a title using AspectJ!</title></head>");
out.println("<body>");
}

after(HttpServletRequest request, HttpServletResponse response) throws IOException : captureHttpRequest(request, response)
{
ServletOutputStream out = response.getOutputStream();
out.println("</body>");
out.println("</html>");
}
}



b) Copy these 2 class files in tomcat projetc classes directory and finally run the servlet using below URL http://localhost:8080/aspectj/AOHelloWorld.


7) A join point is a specific point at which advice can be woven into the code of an application. Pointcuts provide logical definitions for picking the join points that will invoke a piece of advice.

8) Capturing a Method Call

Problem
You want to capture when calls are made to methods that match a specific signature

Solution
Use the call(Signature) pointcut. The syntax of the call(Signature) pointcut is:

pointcut () :
call( .());

The call(Signature) pointcut has two key characteristics:

Advice is triggered on a method call; the context is that of the calling class.

The Signature can include wildcard characters to select a range of join points on different classes and methods.


9) Examples of using wildcards within a method Signature













Signature with wildcardsDescription
* void MyClass.foo(int, float)void MyClass.foo(int, float)Captures join points on a method regardless of the modifier. Can also be achieved by leaving out the visibility entirely.
* * MyClass.foo(int, float)* MyClass.foo(int, float)Captures join points on a method regardless of the modifier or return type.
* * *.foo(int,float)* * foo(int,float)Captures join points on a method regardless of the modifier, return type, or class
* * *.*(int,float)Captures join points on a method regardless of the modifier, return type, class, or method
* * *.*(*,float)Captures join points on a method regardless of the modifier, return type, class, or method where the parameters include anything followed by a float
* * *.*(*,..)Captures join points on a method regardless of the modifier, return type, class, or method where the parameters include at least a single value followed by any number of parameters
* * *.*(..)* *(..)Captures join points on a method regardless of the modifier, return type, class, or method where there are any number of parameters
* mypackage..*.*(..)Captures join points on any method within the mypackage package and subpackages
* MyClass+.*(..)Captures join points on any method on the MyClass class and any subclasses


10) Capturing the Target of a Method Call

Problem
You want to capture the object being called as a method is invoked.

Solution
Create a pointcut that specifies a single parameter of the same type as the target of the method call that you want to capture. Use the call(Signature) and target(Type | Identifier) pointcuts to capture the invocation of a method and then to bind the single identifier to the object that the method is being called upon.


Example

package chapter4_Example3;

public aspect CaptureCallTargetRecipe
{
/*
Specifies calling advice whenever a method
matching the following rules gets called:

Class Name: MyClass
Method Name: foo
Method Return Type: void
Method Parameters: an int followed by a String
*/

pointcut captureCallTarget(MyClass myObject) :
call(void MyClass.foo(int, String)) &&
target(myObject);

// Advice declaration
before(MyClass myObject) : captureCallTarget(myObject)
{

System.out.println(
"------------------- Aspect Advice Logic --------------------");
System.out.println(
"In the advice attached to the call point cut");
System.out.println("Captured target object for the method call: " + myObject);
System.out.println(
"------------------------------------------------------------");
}
}

package chapter4_Example3;

public class MyClass
{
public void foo(int number, String name)
{
System.out.println("Inside foo (int, String)");
}

public static void main(String[] args)
{
// Create an instance of MyClass
MyClass myObject = new MyClass();
// Make the call to foo
myObject.foo(1, "Russ Miles");
}
}


11) The handler(TypePattern) pointcut captures join points where an exception is caught, not where it is raised.

Discussion:

The handler(TypePattern) pointcut has five key characteristics:

The handler(TypePattern) pointcut picks up join points within the scope of where a exception is caught.

The handler(TypePattern) pointcut's advice will only be applied where the type pattern specifies Throwable or a subclass of Throwable.

The TypePattern declares that whenever the matching type of exception, or a subclass of that exception, is caught, then the corresponding advice is to be applied.

Only the before( ) form of advice is supported on handler(TypePattern) pointcuts. This means that you cannot override the normal behavior of a catch block using something like around( ) advice.

The TypePattern can include wildcard characters to select a range of join points on different classes.

Examples of using wildcards within a TypePattern


th>TypePattern with wildcards
td>mypackage..*
td>mypackage+
Description
Captures join points class within the mypackage package and subpackages
Captures join points within the MyClass class and any subclasses


12) Capturing a Call to a Constructor

You want to capture when a call to a constructor that matches a specific signature is invoked.

Solution
Use the call(Signature) pointcut with the additional new keyword as part of the signature. The syntax for using the call(Signature) pointcut in relation to constructors is:

pointcut () :
call( .new());

13) Capturing a Constructor When It Is Executing

Problem
You want to capture a constructor that matches a specific signature when it is executing.

Solution
Use the execution(Signature) pointcut with the additional new keyword as part of the signature. The syntax of the execution(Signature) pointcut when using in relation to constructors is:

pointcut () :
execution( .new());

14) Capturing When a Class Is Initialized

Problem
You want to capture when a class is initialized.

Solution
Use the staticinitialization(TypePattern) pointcut. The syntax of the staticinit-ialization(TypePattern) pointcut is:

pointcut () :
staticinitialization();

15) Capturing the Value of the Field Being Accessed

Problem
You want to capture the value of the field being accessed so that it can be used in your corresponding advice.

Solution
Use the after( ) returning() form of advice with an identifier in the returning( ) part of the declaration to contain the value that has been accessed.


Example:

package chapter8_Example2;

public aspect CaptureAccessedFieldValue
{
pointcut getNamePointcut() : get(String MyClass.name);

// Advice declaration
after() returning(String value) : getNamePointcut()
{
System.out.println(
"-------------- Aspect Advice Logic ---------------");
System.out.println(
"In the advice picked by " + "getNamePointcut()");
System.out.println(
"Signature: "
+ thisJoinPoint.getStaticPart().getSignature());
System.out.println(
"Source Line: "
+ thisJoinPoint.getStaticPart().getSourceLocation());

System.out.println("Value being access is " + value);

System.out.println(
"--------------------------------------------------");
}
}

package chapter8_Example2;


public class MyClass
{
// Just for fun let's also declare a static constant
// Beware, pointcutting will not work on this attribute as it will be
// 'inlined' when the Java compiler is through with it and so therefore
// it will not exist or be accessed in the normal sense.
public static final String CONSTANT = "CONSTANT STRING";

// Two declared fields to exercise the access pointcut
private int number;
private String name;

public String getName()
{
System.out.println("In the getName() method");
return this.name;
}

public void setName(String name)
{
System.out.println("In the setName(String) method");
this.name = name;
System.out.println("The name field has been set");
}

public int getNumber()
{
System.out.println("In the getNumber() method");
return this.number;
}

public void setNumber(int number)
{
System.out.println("In the setNumber(int) method");
this.number = number;
System.out.println("The number field has been set");
}

public static final void main(String args[])
{
// Create an instance of the object
MyClass myObject = new MyClass();

// Exercise all of the calls on the MyClass object
System.out.println("Setting the name");
myObject.setName("Russ Miles");
System.out.println("The name has been set");

System.out.println("Setting the number");
myObject.setNumber(505499);
System.out.println("The number has been set");
System.out.println("Name Stored = " + myObject.getName());
System.out.println("Number Stored = " + myObject.getNumber());

System.out.println("The constant value is " + MyClass.CONSTANT);

}
}


15) Capturing When an Object's Field Is Modified

Problem
You want to capture when an object's field is modified.

Solution
Use the set(Signature) pointcut. The syntax of the set(Signature) pointcut is:

pointcut () :
set( .);

Discussion
The set(Signature) pointcut has four key characteristics:

The set(Signature) pointcut triggers when a field is modified.

The set(Signature) pointcut cannot capture modification of static fields although it is perfectly legal within the syntax of AspectJ to define a pointcut in this way.

The Signature must resolve to an attribute of a particular class.

The Signature can include wildcard characters to select a range of join points on different attributes.

16) Capturing the Value of a Field When It Is Modified

Problem
You want to capture the value of the field after it has been modified so it can be used in your corresponding advice.

Solution
Combine the args([Types | Identifiers]) pointcut with the set(Signature) pointcut to expose the new value of the field being set as an identifier on your pointcut that can be passed to the corresponding advice.

17) Combining Pointcuts Using a Logical AND (&&)

Problem
You want to combine some pointcut declarations, so advice is executed on a join point as long as all conditions within the pointcut declarations evaluate to true.

Solution
Use the && operator. The syntax of the && operator is:

pointcut () :
&&

Below example gives one implementation.


package chapter12_Example1;

public aspect LogicalAndRecipe {
/*
Specifies calling advice whenever a method
matching the following rules gets called:

Class Name: MyClass
Method Name: Any Method
Method Return Type: Any Return Type
Method Parameters: Any Parameters
*/

pointcut callAnyMethodOnMyClass() : call(* MyClass.* (..));

/*
Specifies calling advice whenever a method
matching the following rules gets called:

Class Name: MyClass
Method Name: bar
Method Return Type: void
Method Parameters: None
*/

pointcut callBarPointcut() : call(void MyClass.bar());

/*
Specifies calling advice whenever a join points is
encountered that would be picked by both pointcuts
specified:

Pointcut name: callAnyMethodOnMyClass
Pointcut name: callBarPointcut
Method Return Type: void
Method Parameters: None
*/

pointcut callIntersectionAnyAndBar() : callAnyMethodOnMyClass()
&& callBarPointcut();

// Advice declaration
//This advice will be executed before the pointcut that picks it
before() : callAnyMethodOnMyClass() && !within(LogicalAndRecipe +)
{

System.out.println("------------------- 1 --------------------");

System.out
.println("------------------- Aspect Advice Logic --------------------");
System.out.println("In the advice picked by callAnyMethodOnMyClass");
System.out.println("Signature: " + thisJoinPoint.getSignature());
System.out.println("Source Line: " + thisJoinPoint.getSourceLocation());
System.out
.println("------------------------------------------------------------");
}

// Advice declaration
//This advice will be executed before the pointcut that picks it
before() : callBarPointcut() && !within(LogicalAndRecipe +)
{

System.out.println("------------------- 2 --------------------");

System.out
.println("------------------- Aspect Advice Logic --------------------");
System.out.println("In the advice picked by callBarPointcut");
System.out.println("Signature: " + thisJoinPoint.getSignature());
System.out.println("Source Line: " + thisJoinPoint.getSourceLocation());
System.out
.println("------------------------------------------------------------");
}

// Advice declaration
//This advice will be executed before the pointcut that picks it
before() : callIntersectionAnyAndBar()
&& !within(LogicalAndRecipe +)
{

System.out.println(
"------------------- 3 --------------------");

System.out
.println("------------------- Aspect Advice Logic --------------------");
System.out.println("In the advice picked by callIntersectionAnyAndBar");
System.out.println("Signature: " + thisJoinPoint.getSignature());
System.out.println("Source Line: " + thisJoinPoint.getSourceLocation());
System.out
.println("------------------------------------------------------------");
}
}




package chapter12_Example1;

public class MyClass
{
public void foo(int number, String name)
{
System.out.println("Inside constructional method MyClass.void foo(int, String)");
}

public void foo(float number)
{
System.out.println("Inside constructional method MyClass.void foo(float)");
}

public void bar()
{
System.out.println("Inside constructional method MyClass.void bar ()");
}

public void foo(long longNumber, String name)
{
System.out.println("Inside constructional method MyClass.void foo (long, name)");
}

public static final void main (String args[])
{
// Create instances of the objects
MyClass myObject = new MyClass();

// Exercise all of the calls on the MyClass object
myObject.foo(1,"Russ Miles");
myObject.foo(1.1f);
myObject.bar();
myObject.foo(100000l,"No way my salary");

}
}



18) Declaring Anonymous Pointcuts

You want to declare a simple pointcut anonymously within a named pointcut declaration, or attached directly to an advice.

Solution
Anonymous pointcuts are the building blocks of pointcut declarations. They have been used throughout all the pointcut-based chapters, but this recipe gives anonymous pointcuts more detailed attention.

19) Reusing Pointcuts

Problem
You want to reuse a pointcut expression.

Solution
Declare a pointcut that can be referenced by name in the places where it is to be reused

Below is one example implementation


package chapter12_Example5;

public aspect PointcutReuseRecipe
{
/*
A pointcut definition that is to be used and reused:

Anonymous Pointcuts: call(void MyClass.foo(int,String)

*/

pointcut foundationNamedPointcut() : call(
void MyClass.foo(int, String));

/*
A pointcut definition that is built up from two
pointcuts:

Anonymous Pointcuts: !within(AnonymousPointcutRecipe +)
Named Pointcuts: foundationNamedPointcut()

*/

pointcut reuseNamedPointcut() : foundationNamedPointcut()
&& !within(PointcutReuseRecipe +);

/*
A pointcut definition attached to the advice it will invoke,
built up from simple named and anonymous pointcuts:

Anonymous Pointcuts: !within(LogicalOrRecipe +)
Named Pointcuts: foundationNamedPointcut();
*/

before() : foundationNamedPointcut()
&& !within(PointcutReuseRecipe +)
{
System.out.println(
"------------------- Aspect Advice Logic 1--------------------");
System.out.println(
"In the advice picked by foundationNamedPointcut() and");
System.out.println("!within(AnonymousPointcutRecipe() +");
System.out.println(
"Signature: " + thisJoinPoint.getSignature());
System.out.println(
"Source Line: " + thisJoinPoint.getSourceLocation());
System.out.println(
"------------------------------------------------------------");
}

/*
A pointcut definition attached to the advice it will invoke,
built up from complex pointcuts built reusing other pointcut
definitions:

Named Pointcuts: reuseNamedPointcut
*/

before() : reuseNamedPointcut()
{
System.out.println(
"------------------- Aspect Advice Logic 2--------------------");
System.out.println(
"In the advice picked by reuseNamedPointcut()");
System.out.println(
"Signature: " + thisJoinPoint.getSignature());
System.out.println(
"Source Line: " + thisJoinPoint.getSourceLocation());
System.out.println(
"------------------------------------------------------------");
}
}



20) The thisJoinPoint variable is an object of the JoinPoint class declared within the AspectJ runtime libraries. Different subclasses of the generic JoinPoint class can be instantiated as thisJoinPoint variables depending on the type of the triggering join point.

21) Executing Advice Around a Join Point

Problem
You want advice to execute around the join points that trigger it.

Solution
Use the around( ) type of advice

Example implementation is as follows


package chapter13_Example3;

public aspect AroundAdviceRecipe
{
/*
Specifies calling advice whenever a method
matching the following rules gets called:

Class Name: MyClass
Method Name: bar
Method Return Type: int
Method Parameters:
*/

pointcut callFooPointCut() : call(int MyClass.foo());

/*
Specifies calling advice whenever a method
matching the following rules gets called:

Class Name: MyClass
Method Name: bar2
Method Return Type: int
Method Parameters: int
*/

pointcut callBarPointCut(int value) : call(int MyClass.bar(int))
&& args(value);

/*
Specifies calling advice whenever a method
matching the following rules gets called:

Class Name: MyClass
Method Name: baz
Method Return Type: int
Method Parameters:
*/

pointcut callBazPointCut() : call(int MyClass.baz());

// Advice declaration
// This advice will be executed before the pointcut that picks it
int around() : callFooPointCut() && !within(AroundAdviceRecipe +)
{
System.out.println(
"------------------- Aspect Advice Logic 1 --------------------");
System.out.println(
"Signature: " + thisJoinPoint.getSignature());
System.out.println(
"Source Location: "
+ thisJoinPoint.getStaticPart().getSourceLocation());
System.out.println(
"------------------------------------------------------------");
return proceed();
}

// Advice declaration
// This advice will be executed before the pointcut that picks it
int around(int value) : callBarPointCut(value)
&& !within(AroundAdviceRecipe +)
{
System.out.println(
"------------------- Aspect Advice Logic 2--------------------");
System.out.println(
"Signature: " + thisJoinPoint.getSignature());
System.out.println(
"Source Location: "
+ thisJoinPoint.getStaticPart().getSourceLocation());
System.out.println(
"------------------------------------------------------------");
return proceed(value);
}

// Advice declaration
// This advice will be executed before the pointcut that picks it
int around() : callBazPointCut() && !within(AroundAdviceRecipe +)
{
System.out.println(
"------------------- Aspect Advice Logic 3--------------------");
System.out.println(
"Signature: " + thisJoinPoint.getSignature());
System.out.println(
"Source Location: "
+ thisJoinPoint.getStaticPart().getSourceLocation());
System.out.println(
"------------------------------------------------------------");
return 200;
}
}


package chapter13_Example3;


public class MainClass
{
public static void main(String[] args)
{
// Create an instance of MyClass
MyClass myObject = new MyClass();

System.out.println(
"Returned value from foo: " + myObject.foo());

System.out.println(
"Returned value from bar: " + myObject.bar(20));

System.out.println(
"Returned value from baz: " + myObject.baz());
}
}


package chapter13_Example3;


public class MyClass
{
public int foo()
{
System.out.println("Inside int foo ()");
return 500;
}

public int bar(int value)
{
System.out.println("Inside int bar (int)");
return 500;
}

public int baz()
{
System.out.println("Inside int baz ()");
return 500;
}
}

Prints:

------------------- Aspect Advice Logic 1 --------------------
Signature: int chapter13_Example3.MyClass.foo()
Source Location: MainClass.java:12
------------------------------------------------------------
Inside int foo ()
Returned value from foo: 500
------------------- Aspect Advice Logic 2--------------------
Signature: int chapter13_Example3.MyClass.bar(int)
Source Location: MainClass.java:15
------------------------------------------------------------
Inside int bar (int)
Returned value from bar: 500
------------------- Aspect Advice Logic 3--------------------
Signature: int chapter13_Example3.MyClass.baz()
Source Location: MainClass.java:18
------------------------------------------------------------
Returned value from baz: 200



22) Executing Advice Only After a Normal Return from a Join Point

Problem
You want advice to execute after a specific join point only if that join point returned normally.

Solution
Use the after( ) returning or after() returning( ) types of advice.


Recipe Executing Advice Only After a Normal Return from a Join Point Problem
You want advice to execute after a specific join point only if that join point returned normally.

Solution
Use the after( ) returning or after() returning( ) types of advice.

Discussion
Example shows how to specify that the advice should only be executed after the call to void MyClass.foo(int) if the call to the method returns normally.

Example Executing advice after a method call if that call returns normally


public aspect AfterReturningAdviceRecipe
{
/*
Specifies calling advice whenever a method
matching the following rules gets called:

Class Name: MyClass
Method Name: foo
Method Return Type: void
Method Parameters: an int followed by a String
*/

pointcut callPointCut( ) : call(void MyClass.foo(int));

// Advice declaration
after( ) returning : callPointCut( ) &&
!within(AfterReturningAdviceRecipe +)
{
System.out.println(
"------------------- Aspect Advice Logic --------------------");
System.out.println(
"Source Location: "
+ thisJoinPoint.getStaticPart( ).getSourceLocation( ));
System.out.println(
"------------------------------------------------------------");
}
}


23) Controlling Advice Precedence

Problem
You want to control the precedence of multiple advice blocks as they are applied to the same join point.

Solution
If the same types of advice located in different aspects are applied to the same join point, you can use the declare precedence statement. The syntax of the declare precedence statement is:

declare precedence : TypePattern, TypePattern, ..;

24) Defining Singleton Aspects

Problem
You want to declare that an aspect is to be instantiated as a singleton.

Solution
Use the issingleton() explicit aspect instantiation policy declaration or rely on the default implicit aspect instantiation policy.

25) Defining an Aspect per Instance

Problem
You want to declare that an aspect is to be instantiated on a per-object-instance basis.

Solution
Use the perthis(Pointcut) or pertarget(Pointcut) aspect instantiation policy declarations.


Hope you enjoy reading this book

About the Author

Russell Miles is a senior consultant for SpringSource in the UK where he works with various companies to help them take full advantage of the Spring Framework. To ensure that he has as little spare time as possible, Russ contributes to various open source projects while working on books for O'Reilly.

1 comment:

Faisal said...

nice job done ;) was really helpful