ONJava.com    
 Published on ONJava.com (http://www.onjava.com/)
 See this if you're having trouble printing code examples


Introduction to Aspect-Oriented Programming

by Graham O'Regan
01/14/2004

Overview of Aspect Oriented Programming

When Object-Oriented (OO) programming entered the mainstream of software development, it had a dramatic effect on how software was developed. Developers could visualize systems as groups of entities and the interaction between those entities, which allowed them to tackle larger, more complicated systems and develop them in less time than ever before. The only problem with OO programming is that it is essentially static, and a change in requirements can have a profound impact on development timelines.

Aspect-Oriented Programming (AOP) complements OO programming by allowing the developer to dynamically modify the static OO model to create a system that can grow to meet new requirements. Just as objects in the real world can change their states during their lifecycles, an application can adopt new characteristics as it develops.

Consider an example: many of you have developed simple web applications that use servlets as the entry point, where a servlet accepts the values of a HTML form, binds them to an object, passes them into the application to be processed, and then returns a response to the user. The first cut of the servlet may be very simple, with only the minimum amount of code required to fulfill the use case being modeled. The code, however, often inflates to three to four times its original size by the time secondary requirements such as exception handling, security, and logging have been implemented. I use the term "secondary requirements" because a servlet should not need to know about the logging or security mechanisms being used; its primary function is to accept input and process it.

AOP allows us to dynamically modify our static model to include the code required to fulfill the secondary requirements without having to modify the original static model (in fact, we don't even need to have the original code). Better still, we can often keep this additional code in a single location rather than having to scatter it across the existing model, as we would have to if we were using OO on its own.

O'Reilly Emerging Technology Conference.

In this article, we will look at a simple application that will hopefully allow you to see the benefits of AOP firsthand, and then we will briefly discuss how AOP could be used in your existing projects.

Terminology

Before we delve too deeply into AOP, let's introduce some standard terminology to help us understand the concepts.

  1. Cross-cutting concerns: Even though most classes in an OO model will perform a single, specific function, they often share common, secondary requirements with other classes. For example, we may want to add logging to classes within the data-access layer and also to classes in the UI layer whenever a thread enters or exits a method. Even though the primary functionality of each class is very different, the code needed to perform the secondary functionality is often identical.

  2. Advice: This is the additional code that you want to apply to your existing model. In our example, this is the logging code that we want to apply whenever the thread enters or exits a method.

  3. Point-cut: This is the term given to the point of execution in the application at which cross-cutting concern needs to be applied. In our example, a point-cut is reached when the thread enters a method, and another point-cut is reached when the thread exits the method.

  4. Aspect: The combination of the point-cut and the advice is termed an aspect. In the example below, we add a logging aspect to our application by defining a point-cut and giving the correct advice.

There are many other facets of AOP, such as introductions (where interfaces/methods/fields can be added to existing classes), that hold tremendous potential for developers, but I'll stick with some of simpler facets in this article. When you are familiar with the concepts presented here, I would recommend that you continue to investigate AOP and see how the other facets may be of use to you in your development environment.

Existing Frameworks

Probably the most mature and fully featured framework available today is AspectJ. While AspectJ sets the standard that most frameworks follow, the architects took the unusual step of adding new keywords to the Java language in their implementation. Though the new syntax isn't too difficult to learn, it does mean that you will have to change your compiler, and potentially reconfigure your editor, in order to use the new syntax. In a large team this may not be feasible, as the whole team could be affected. The modification to the language also increases the learning curve for teams looking to introduce AOP into existing projects.

What we need is a framework that can be easily introduced without severely impacting the existing development and build process. There are several frameworks that fit the bill, such as JBoss AOP, Nanning, and Aspectwerkz (AW). For this article, I've chosen Aspectwerkz because it is probably the easiest framework to learn and integrate into your existing projects.

Aspectwerkz, created by Jonas Boner and Alexandre Vasseur, remains one of the quickest and most fully featured AOP frameworks available. While it may not boast all of the features of AspectJ, it is sufficiently complete to be of great use to most developers in many situations.

One of the most interesting features of Aspectwerkz is its ability to run in two different modes: online and offline. In online mode, AW is hooked into the low-level classloading mechanism part of the JVM, allowing it to intercept all classloading calls and transform the bytecode on the fly. AW provides many options to hook in, and a wrapper script can be used as a replacement for the bin/java command to automatically detect and set a working configuration depending on the Java version and JVM capabilities. This mode holds many benefits to developers, as it can be hooked into any classloader and weave classes at classload time, which means that your class files are not modified manually, but deployed as usual. However, it does require additional configuration of your application server, which may not be possible in some situations.

In offline mode, two phases are required to generate your classes. The first phase is the standard compilation using the javac tool that we all know and love. (In fact, most of us love it so much that we replaced it with an Ant task years ago.) The second phase is where things get interesting, we run the AWcompiler in offline mode and point it to the newly created class files. The compiler will then modify the bytecode of the classes to include your advice at the correct point-cuts, as defined in an XML file. The benefit of using the offline mode is that the classes will run in any JVM from version 1.3 and up. This is the mode that I will use in this article, as it requires no modifications to Tomcat and could be applied to most existing projects with only slight modification to the build process.

Installing

In this article, we are going to write look at a simple application, compile it using Ant, and deploy it on a Tomcat 4+ servlet container. I am going to assume that you have already installed the above, along with a JVM 1.3+, and that your Tomcat installation is set to deploy applications from the webapps folder automatically and expand the WAR files into directories (this is the default behavior, so if you haven't modified Tomcat, this application should work out of the box). We will refer to the location where Tomcat is installed as %TOMCAT_HOME%.

  1. Download Aspectwerkz from its web site and extract the compressed file to your preferred location. We'll refer to this location as %ASPECTWERKZ_HOME%.

  2. Set the %ASPECTWERKZ_HOME% environment variable.

  3. Add the Aspectwerkz compiler to your PATH environment variable; i.e., set PATH=%PATH%;%ASPECTWERKZ_HOME%\bin\aspectwerkz.

  4. Download the sample application that accompanies this article and drop it into your %TOMCAT_HOME%\webapps folder

    .
  5. Add the Aspectwerkz runtime classes to Tomcat's classpath. You could drop the JAR files from %ASPECTWERKZ_HOME%\lib into the WEB-INF\lib folder of the sample application, or into %TOMCAT_HOME%\common\lib.

Compiling the Sample Application

If you want to play with this demo application yourself, extract the contents of the sample application's WAR file. You'll find the aspectwerkz.xml file in the root of the directory, which gets copied to the WEB-INF/classes directory when the application is built. The source files of the servlet and advice classes are in WEB-INF/src; I've included an Ant script that will build these classes for you.

Before you can see the demo in action, you will have to complete the post-compilation phase, too, and here's how:

  1. Navigate on the command line to the directory where you extracted the WAR file.
  2. Type the following command to invoke the AW compiler (all on one line):
aspectwerkz -offline aspectwerkz.xml WEB-INF/classes
-cp %TOMCAT_HOME%\common\lib\servlet.jar

You should see the following if the post-compilation completes successfully:

( 1 s )
SUCCESS: WEB-INF\classes

There is an Ant task in the build file called war that you can use to recreate the WAR file.

Running the Sample Application

  1. (Re)start Tomcat.
  2. Open http://localhost:8080/demo/ in a browser.

When you open your browser, you will see a standard HTML form with two fields: one for the name and one for email address of the contact. Enter some details and press submit, and you will see the contacts details displayed and a link to a file that contains a simple contact list. OK, so the demo isn't going to set the world on fire, but let's take a look under the hood and see what has really happened.

Code Walkthrough

Let's ignore the JSP files, as they there are of very little interest to us right now. Instead, have a look at the code of AOPServlet.

package example;

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class AOPServlet extends HttpServlet {
    public void doGet(HttpServletRequest request,
                      HttpServletResponse response)
        throws ServletException, IOException {
        Person person = new Person();
        if (request.getParameter("name") != null) {
            person.setName(
                request.getParameter("name"));
        }
        if (request.getParameter("email") != null) {
            person.setEmail(
                request.getParameter("email"));
        }
        request.setAttribute("person", person);
        RequestDispatcher rd =
            request.getRequestDispatcher("/view.jsp");
        rd.forward(request, response);
    }
}

In this example, you'll notice that the servlet code is very minimal; it only contains enough code to create an object to bind the request parameters to, and then it passes the object along in the request. There is no persistence code, no additional imports; it simply does what it meant to do.

But our design document stipulates that we must persist all objects of type Person in our application, so we will need to add an aspect to the application. To create an aspect, we first have to create a file called aspectwerkz.xml and place that file in the classpath. I've include a simple example in the sample application that you can open in your favorite editor; I'll explain what it is doing.

The first section defines the different advices that are available to us. We can add as many different advice definitions as we like:

<advice-def name="persist" class="example.PersistenceAdvice"
deployment-model="perJVM"/>

In this snippet, we define a piece of advice called persist, which is of type example.PersistenceAdvice. The last attribute defines the exclusivity of this advice; in this case, it's set to perJVM, which means that only one instance of this advice will be created in each JVM. (See the Aspectwerkz documentation for more information about deployment models.)

The next section defines our aspects. This is where we map our advice to a point-cut to create an aspect.

<aspect name="servlet">
<pointcut-def name="all" type="method"
    pattern="* example.*Servlet.doGet(..)"/>
<bind-advice pointcut="all">
<advice-ref name="persist"/>
</bind-advice>
</aspect>

Let's step through this section line by line:

  1. We are creating an aspect called servlet; we can create as many aspects as we require.

  2. On the second line, we are creating a point-cut called all that will only be applied to methods (type="method").

  3. The third line is where we define, using a regular expression, where we want the advice to be given. In this example, we are stating that we want to apply the advice, regardless of the return type (the first *), to any class in the example package whose name ends with "Servlet" (*Servlet) that contains a method called doGet with any parameter list (doGet(..)).

  4. On the fourth line, we tell the Aspectwerkz compiler that we want to apply the following advice to the all point-cut.

  5. Here we are saying that we want to use the persist advice.

Now that we know how to map point-cuts and advice to create aspects, let's look at an example of a class that provides advice. In our mapping file, we registered an advice of type example.PersistenceAdvice. Here is the source of that file:

package example;

import javax.servlet.http.*;
import org.codehaus.aspectwerkz.advice.*;
import org.codehaus.aspectwerkz.joinpoint.*;

public class PersistenceAdvice extends AroundAdvice {
    public PersistenceAdvice() {
        super();
    }
    public Object execute(final JoinPoint joinPoint)
        throws Throwable {
        MethodJoinPoint jp =
            (MethodJoinPoint) joinPoint;
        final Object result = joinPoint.proceed();
        Object[] parameters = jp.getParameters();
        if (parameters[0] instanceof HttpServletRequest) {
            HttpServletRequest request =
                (HttpServletRequest) parameters[0];
            if (request.getAttribute("person") != null) {
                Person contact =
                    (Person) request.getAttribute("person");
                ContactManager persistent =
                    new ContactManager();
                String fileName =
                    (request.getRealPath("/")+
                     "contacts.txt");
                persistent.save(contact, fileName);
            }
        }
        return result;
    }
}

The first line of this method is self-explanatory: we simply cast to the most specific type we can. The second line is probably the most important: since we want to fire the method and then look at the result, we must call proceed(), which allows the method to complete. In the next section, we are capturing the HttpServletRequest and retrieving the object that is placed in request scope by the servlet (remember, the doGet() method has finished at this point).

Finally, we create a class called ContactManager that persists the person's details to a text file. This class could just as easily save the details to an XML file, a database, or another persistent store. The point to take away is that the servlet has no idea what will happen to the bean when you design/prototype the application; the secondary functionality can be added at any point in the future. This is what I mean when I say that your basic application can learn new behavior as it grows, and adding additional functionality at a later date is trivial.

Where Next?

In the example in the previous section we took a very basic application, deployed it to Tomcat, and ran it in a browser to test the functionality. While the application as it stands isn't very useful, the principles that it demonstrates are very powerful indeed. Imagine being able to quickly prototype functionality and then apply the cross-cutting concerns such as security, logging, persistence, and caching when you've finished. You could feasibly add logging to a whole application, regardless of the number of lines of code, in less than ten minutes!

I hope this you can see beyond the simplicity of this application and see where you can use AOP in your projects. While there might be a reasonably steep curve to overcome to familiarize yourself with the concepts behind AOP, it will definitely pay off, cutting weeks and probably thousands of lines of repetitive code from the average project.

Graham O'Regan is a senior developer with European Technology Consultants (ETC) in London.


Return to ONJava.com.

Copyright © 2009 O'Reilly Media, Inc.