ONJava.com -- The Independent Source for Enterprise Java
oreilly.comSafari Books Online.Conferences.

advertisement

AddThis Social Bookmark Button

Introduction to Aspect-Oriented Programming
Pages: 1, 2

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.