Quick Start Guide to Enterprise AOP with Aspectwerkz 2.0

by David Teare
04/05/2005

Abstract

Today, aspect-oriented programming (AOP) frameworks are trying to gain a foothold in the enterprise environment. In order for them to be widely adopted, these frameworks must integrate well with the other frameworks that are already being used within enterprise systems. This article shows developers how to integrate the AspectWerkz AOP framework with a few of today's popular frameworks: Log4J, the Atlassian performance profiler, Hibernate, and Tapestry.

This article starts with an existing Tapestry web application that implements two separate concerns: logging and performance profiling. Every project has these needs, with many leveraging Log4J for logging and the Atlassian profiler framework for performance analysis. The original non-AOP implementation is then refactored to use the AspectWerkz framework to separate each concern's implementation. The resulting application code will be simpler, easier to maintain, and, most important, more natural and expressive.

All applications and source code described in this article are available for download.

Introduction

To demonstrate the power of AOP we start with a non-AOP web application and refactor it to use AOP. Having a before and after image of the application will show how easy AOP is to apply and how useful aspects are as extensions to the Java language.

The sample application implements a simple blog that allows users to post new articles and comment on existing articles. It is implemented using a variety of frameworks, including Tapestry for the web tier, Hibernate as the O/R Mapping solution, and the Spring framework to tie everything together. Logging is implemented using the Log4J framework, and performance is monitored using the Atlassian profiler.

For demonstration purposes, the blog application was kept as simple as possible. Despite its simplicity, the blog application contains enough functionality to make it resemble a "real" application, and therefore the code in this article should be applicable to existing enterprise projects.

This article assumes a basic understanding of AOP concepts. Those who are new to AOP should review the articles and tutorials referenced below. All examples will be implemented using AspectWerkz 2.0, running on BEA WebLogic JRockit 1.4.2 SDK. For alternative environments, see the main AspectWerkz site.

Running the Sample Application

To run the sample application you will need a database and a servlet container. This article assumes MySQL will be used.

After installing the requisite software, download and unpack the sample code into a temporary directory. You will find three files in the distribution:

  1. blog-ddl.sql
  2. blog-preaop.war
  3. blog-postaop.war

First, set up MySQL to have the required database. Name the database "blog" and set up a user id "blog" with password "password." Next, define the schema by executing the blog-ddl.sql script (type source blog-ddl.sql from a MySQL prompt).

Now we can deploy the applications. Both versions of the blog application come packaged as a WAR file, so deployment to your servlet container should be straightforward.

You should now be able to run the application by visiting http://localhost:7001/blog-preaop/blog and http://localhost:7001/blog-postaop/blog. Play with the application for a while and check out the source code in the WEB-INF/classes directory. The remainder of the article will dissect both implementations and highlight the strengths of the AOP approach.

Analysis of the Original Blog Application

The blog application implements two cross-cutting concerns: logging and profiling. These concerns needed to be implemented in every class throughout the entire application. Let's see how they were implemented using the standard Java tools that were available before AOP arrived.

Logging

The goal for logging is to be able to debug a production application without turning on the debugger. What has proven most valuable to me on my projects has been including tracing in my code by logging the entry and exit of each method. For example, in the HibernateEntryDao class, we have the following code for finding all the blog entries:

private static final Log log = Log.getLog(EntryHibernateDao.class);

public Entry[] findAll() {

   log.enter("findAll");

   List entries = getHibernateTemplate().find("from Entry");

   log.exit("findAll");

   return (Entry[])entries.toArray(new Entry[] {});

}

When used consistently throughout the code, the following log output is generated for a complete user request:

com.tss.blog.web.ApplicationServlet INFO : >service: '/blog'

com.tss.blog.service.BlogSvcImpl INFO : >findAllEntries

com.tss.blog.persistence.EntryHibernateDao INFO : >findAll

com.tss.blog.persistence.EntryHibernateDao INFO : <findAll

com.tss.blog.service.BlogSvcImpl INFO :   <findAllEntries

com.tss.blog.web.ApplicationServlet INFO : <service: '/blog'

While this is very verbose, it has been very helpful for debugging production systems. Log4J does a good job optimizing the writing of this log, so performance rarely becomes an issue. Since all the enter/exit calls are given the priority of INFO, if performance of the web tier does become an issue, all you have to do is change the logging priority threshold in log4j.properties to WARN or above, and Log4J will discard the trace information.

Profiling

Another concern implemented by the blog application is profiling. In many projects application profiling is done prior to production and is only repeated if a user complains. Thanks to the Atlassian profiler, we can take a much more proactive approach, keeping track of time spent in each method and reporting the results at the end of each request. If any request takes more time than expected, we log the profiling information as an ERROR to raise attention. With Log4J configured to send an email to the development team for any logged errors, we know immediately if the application is running too slowly. In my experience, this has proven invaluable for finding application programming issues such as deadlocks and poorly written transactions.

To implement the profiler, we could follow the approach used by the tracing code. In each method, you surround your code with calls to start and stop the profiler. Combining this with the trace code, we have the following:

public Entry[] findAll() {

   log.enter("findAll");

   Profiler.push("findAll");

   List entries = 

       getHibernateTemplate().find("from Entry");

   Profiler.pop("findAll");

   log.exit("findAll");

   return (Entry[])

       entries.toArray(new Entry[] {});

}

This yields the following log output for a complete user request:

com.tss.common.Profiler INFO : 

   [2373ms] - service: '/blog'

     [150ms] - findAllEntries

       [150ms] - findAll

While this information is helpful, you can see how verbose the code has become. The profiler has made itself so intrusive that it will be nearly impossible to get the entire development team to use it. Since we are not using AOP yet, we have no choice but to introduce a little hack - we incorporate the call to the profiler in the logging code. The following Log methods will now handle the profiling for us:

public void enter(Object method) {

   if (!l.isInfoEnabled()) return;



   l.info(">" + method);

   Profiler.push(method.toString());

}



public void exit(Object method) {

   if (!l.isInfoEnabled()) return;



   l.info("<" + method);

   Profiler.pop(method.toString());

}

While this works, and developers now only need to call the logging code, we have tightly coupled the profiling concern with the logging concern. This tight coupling hampers our flexibility.

Pages: 1, 2, 3

Next Page ยป