A typical example of cross-cutting concern is logging. The logging aspect of an application is common to many classes across its multiple layers. Besides logging, other examples of cross-cutting concerns are security, caching and transaction. The following diagram, shows the multiple layers of an application (data access, business logic, services interfaces, UI), and some cross-cutting concerns (logging, security, transaction). An aspect is just another term for cross-cutting concern.
What is AspectJ?
AspectJ is one of the most popular aspect-oriented development tools for Java. It is a simple extension to the Java programming language that adds to Java the aspect-oriented programming capabilities.
Downloading and Installing AspectJ
You can download AspectJ from here. Select the latest stable release. At the time of this post, the latest stable release was AspectJ 1.6.1rc1. Just run the downloaded jar file and follow the instructions to install it. It is assumed that you have already Java installed on your machine.
Do not forget to change your PATH and CLASSPATH as suggested at the last screen of the installer application.
AspectJ Concepts and Terms
Before using AspectJ, lets define some important concepts. In AspectJ, cross-cutting concerns can be represented as a special type called aspect. This special type contains two special elements: a pointcut and an advise.
The advice is the code that is executed when an aspect is invoked. For example, if the aspect is logging, the advice will be the code that actually writes some information to a log file.
A pointcut specifies where an aspect will provide an advice, e.g. where the advice code will be executed. The places where this code can be executed are known as joint points, e.g. points within an application that may or may not invoke an advice. Thus, a pointcut binds an aspect to specific join points of the classes. Examples of join points are: when a method is called, when the object is initialized, when an exception is thrown, when a class field is assigned, etc.
Logging Example
The following code snippet represents an aspect to log all exceptions caught by the application.
import java.io.*;
public aspect ExceptionLoggingAspect {
// Specifies the calling advice when any exception is caught.
pointcut exceptionHandlerPointcut(Exception exception) :
handler (Exception) && args(exception);
// Advice declaration.
before(Exception exception) : exceptionHandlerPointcut(exception) {
// Exception logging.
String filename = "log.txt";
try {
String text = "Error: " + exception.toString() + "\n";
FileWriter out = new FileWriter(filename, true);
out.write(text);
out.flush();
out.close();
} catch (IOException ex) {
System.out.println("Failed when logging exception to " + filename);
}
}
}
In this code, the pointcut handler(Exception) binds to join points where an exception is caught, e.g. to code containing catch (Exception ...) statements. Note that this pointcut is for all types of exceptions since we are using the java.lang.Exception class. You could use an specific exception if you want to limit this aspect to be applied to certain exception types. The pointcut
args(exception) allow us to retrieve the exception object and pass it as a parameter to the advice method.
The advice method receives the reference to the exception caught and use the FileWriter class to log its details to a log file called log.txt. The advice method could also throw an exception, for example if the log file cannot be created in the file system. In this situation, an exception thrown inside the advice method will not be logged, since the advice method is not a join point. So, there is no risk of having an infinite loop.
The code above is the only code necessary for exception logging mechanism.
In a traditional approach, we would need to change all the many places where we are catching an exception and add a call to a method to log the exception. This could be a very time consuming task depending on how many places we have to change, and also resulting in duplicating code across the whole application. Using AOP, the joincut will make all the hard work to bind the joint point (catch statements) with the exception logging aspect.
Security Example
This second example is an aspect to detect malicious strings passed to method calls. Web applications are vulnerable to Cross-Site Scripting (XSS) attacks that happen when a user executes a Web page containing script code that has been injected from other sources. A malicious user can take advantage of the lack of proper data validation in data entry page. Fields could be filled with Javascript code that will be persisted in the database when the information is submitted. When this data is rendered in some Web page, the script code will be executed, causing a security flaw in the Web application.
One way to avoid XSS attacks is to validate all strings entered by users and reject those containing malicious scripts. Usually data types use standard method signature to set string fields, for example setSomeString (String text). The following code represents an aspect to validate the string parameter of all setter methods of our data types.
import java.util.regex.*;
public aspect XSSDetectionAspect {
// Specifies calling advice whenever a method from any class
// returns void and receives a string parameter.
pointcut callPointCut (String text) :
call (void *.*(String)) && args(text);
// Advice declaration.
before(String text) : callPointCut (text) {
// Using a regular expression to match valid strings, e.g.,
// containing alphanumeric characters, spaces and the following characters ,;:@().
Pattern pattern = Pattern.compile("^[0-9a-zA-Z\\s.,;:@()]*$");
Matcher matcher = pattern.matcher(text);
if (!matcher.find()) {
throw new IllegalArgumentException("Illegal string parameter: " + text);
}
}
}
In the code above, the pointcut will pickup calls from any method that returns void and accepts a string as a parameter. When a method call satisfies this condition, the advice code is executed. Note that we could restrict to pickup calls from methods belonging to classes of a specific package. For example, if we want to include only classes from com.myapp.datatypes package, we could use the following call pointcut:
call (void com.myapp.datatypes.*.*(String))
The advice method receives the string to be validated. If the string passed does not match the pattern for valid strings (e.g. alphanumeric, space, punctuation, and other characters), then an exception is thrown.
As in the previous example, the code above is everything we need for the string validation mechanism. In a traditional way, we would add validation code to all methods that set fields of string type on the data objects, resulting in duplicated code and maintenance issues.
Sample Code Download
In order to compile the sample code, you will need to install AspectJ (see section above) and then just run ajc *.java at the command prompt window.
For More Information
No comments:
Post a Comment