Introduction
Aspect-oriented programming (AOP) is a new paradigm and technology that allows solving complex problems in software that cannot be simply solved using object-oriented programming. The concepts of AOP have first been defined and explored by Gregor Kiczales' team at Xerox Parc in the late 1990s. Since then, many AOP frameworks have emerged in the Java landscape and many experts in the software industry talks about AOP as something that will revolutionize the way we design and write software.Want to learn more about Enabling Aspect-Oriented Programming? Download (2.48 KB) additional resources, which accompany this article.
In this article, you will learn the basic AOP concepts as well as how to apply AOP to your J2EE applications using AspectWerkz, a dynamic AOP framework. The hands-on tutorial explains how to use AOP to track EJB CMP's container generated JDBC queries.
You will learn how to integrate AOP in your BEA environment, using a standard offline class post-processing mechanism, class-load time integration in BEA WebLogic Server 8.1 and a dedicated BEA JRockit Management API module that delivers best performance with a minimal integration effort.
To practice the tutorial you will need:
- Jakarta Ant 1.5 or later
- BEA WebLogic Platform 8.1 or BEA WebLogic Server 8.1 and BEA JRockit 8.1
- AspectWerkz AOP 0.10RC2
- WebLogic Server sample domain installed (see FAQ)
What is Aspect-Oriented Programming?
Object-oriented analysis and design has given us tools to reduce the complexity in software by introducing concepts like inheritance, abstraction and polymorphism. There is however some problems in software design that developers are faced with everyday that is not possible to solve in a pleasant way using object-oriented software development. One of these shortcomings is how to handle cross-cutting concerns in the application.Cross-cutting concerns
A concern is a particular concept or area of interest. For example in an ordering system the core concerns could be order processing and manufacturing while the system concerns could be transaction handling, security management etc.
A cross-cutting concern is a concern that affects several classes or modules, a concern that is not well localized and modularized.
Symptoms of a cross-cutting concern are:
- Code tangling - when a module or code section is managing several concerns simultaneously
- Code scattering - when a concern is spread over many modules and is not well localized and modularized
These symptoms will affect the software in various ways, it will for example make it harder to maintain and reuse, harder to write and understand.
Separation of concerns
Aspect-oriented programming tries to solve these problems by introducing the concept of separation of concerns in which the concerns can be implemented in a modular and well localized way. It solves this by adding an extra dimension to the design space and introduces constructs that allows us to define the cross-cutting concerns, to lift them out into the new dimension and to package them in a modular way.
New constructs introduced by AOP
Aspect-oriented programming introduces some new constructs. First we have the Join Point construct which allows us to pick out a well defined point in the program flow, for example where a method is invoked or where an exception is catched. Then we have the Pointcut construct which allows us to pick out Join Points that matches a certain criteria. We also have the Advice construct that allows us to add code that should be executed at matching Join Points, as well as the Introduction construct that allows us to add additional code to existing classes, for example add methods, fields or interfaces to an existing class. Finally it introduces the Aspect construct which is AOP’s unit of modularity. An Aspect is defined in terms of Join Points, Pointcuts, Advice and Introductions.
The AspectWerkz AOP framework
AspectWerkz is an open source framework targeted towards dynamic AOP in J2EE environments. It has a very dynamic runtime model which allows you to redefine your system at runtime. You can for example hot-deploy aspects as well as add, remove or reorder advice at runtime. It utilizes runtime bytecode modification to weave in the aspects transparently at runtime. It is vendor and platform independent but works especially good with the BEA JRockit JVM since it utilizes the Management API in JRockit to allow class enhancement at load-time and still let the application to run at full speed.AspectWerkz also provides a variety of hooking options that enable class load-time weaving under standard JVM, Java 1.3 or 1.4 version.
Here is an illustration of how advice with different deployment models live in different memory spaces with their own life-cycles. It illustrates a method invocation that is intercepted by four different advice that are invoked through a join point instance:
Example: Tracing SQL statements in EJB CMP beans using AOP
Sample problem: Tracing SQL statements in EJB CMP beansIn this example we will look into how to implement an aspect that allows us to trace the SQL queries in EJB CMP beans. The SQL queries for CMP are automatically generated under the hood by the EJB container, based on the XML deployment descriptor. We can obtain this information through several means (use a JDBC driver or a JDBC wrapping driver that allows tracing, turn on JDBC logging at server level, use a SQL tracing solution), so it’s a good practice to evaluate how much effort is required using an AOP approach.
A CMP EJB (EJB local interface and local home interface omitted)
public abstract class ProjectCMP implements EntityBean {
public abstract Integer getId();
public abstract String getName();
public abstract void setId(Integer id);
public abstract void setName(String name);
public Integer ejbCreate(Integer id, String name)
throws CreateException {
setId(id);
setName(name);
return null;
}
}
Excerpt of
weblogic-cmp-rdbms-jar.xml deployment descriptor mapping a CMP EJB to a database table:
<weblogic-rdbms-jar>
<weblogic-rdbms-bean>
<ejb-name>Project</ejb-name>
<data-source-name>aspectwerkz/ds/default</data-source-name>
<table-map>
<table-name>>PROJECT</table-name>
<field-map>
<cmp-field>id</cmp-field>
<dbms-column>ID</dbms-column>
</field-map>
<field-map>
<cmp-field>name</cmp-field>
<dbms-column>NAME</dbms-column>
</field-map>
</table-map>
<weblogic-query>
...
</weblogic-query>
</weblogic-rdbms-bean>
</weblogic-rdbms-jar>
As we see, there is no SQL code in the programmer written CMP EJB. The SQL generation is a task handled by the WebLogic EJB container. To have a better understanding, we have to think about how an SQL statement is handled in Java through any JDBC driver.
First we need to trace all method calls where a
java.sql.Statement is created and retrieve the SQL query for this statement right before it is created. Unfortunately this will not be sufficient since when using java.sql.PreparedStatement the values are not part of the query string but set dynamically afterwards. This means that we also have to trace all method calls where a value is set in a java.sql.PreparedStatement instance and retrieve the index and value pair passed to the method.Here is an example of how a statement is created and the values set:
PreparedStatement pStmt = connection.prepareStatement("update project set name=? where id=?");
pStmt.setString(1, "AspectWerkz"); // index start at 1
pStmt.setInt(2, 1);
pStmt.executeUpdate();
Writing the tracing aspect
We start by implementing the aspect. Aspects in AspectWerkz are implemented in plain Java. We start by creating a class called
LoggingAspect this aspect extends the Aspect base class.Here we add two advice; one that logs all JDBC queries in all the
java.sql.Statement instances and one that logs all values that are set in the java.sql.PreparedStatement instances, these are implemented as regular member methods in the aspect class. Then define them with an attribute that specifies the type of the advice, in our case around advice, as well as the pointcut that we want to bind the advice to, e.g. the pointcut that picks out the join points that we want the advice to be executed at.Pointcuts can be both named and anonymous. In this example we are using anonymous pointcuts and are specifiying the pattern matching that join points that we want to be picked out for the pointcut directly in the advice definition.
For a more detailed explanation of the constructs and syntax used in this example see the AspectWerkz documentation.
public class LoggingAspect {
/**
* @Around execution(* java.sql.Connection+.prepare*(..))
*/
public Object logJdbcQueries(final JoinPoint joinPoint) throws Throwable {
MethodRtti rtti = (MethodRtti)joinPoint.getRtti();
String query = (String)rtti.getParameterValues()[0];
System.out.println("===> query: " + query);
return joinPoint.proceed();
}
/**
* @Around execution(void java.sql.PreparedStatement+.set*(..))
*/
public Object logValuesInPreparedStatement(final JoinPoint joinPoint)
throws Throwable {
MethodRtti rtti = (MethodRtti)joinPoint.getRtti();
Integer index = (Integer)rtti.getParameterValues()[0];
Object value = rtti.getParameterValues()[1];
System.out.println(" index=" + index.intValue() +
" value=" + value.toString());
return joinPoint.proceed();
}
}
Packaging the tracing aspect
Once written, we need to package the aspect. As explained, the aspect is self-defined through the help of meta-data attribute in its JavaDoc. The aspect must then be first compiled as a regular Java class. Then it needs to be processed so that the meta-data is incorporated in the class file. This post processing phase of the aspect class file is required as long as we don’t have Java 1.5 metadata support (JSR-175).
Under Java 1.4 and Java 1.3, we have to use the AspectC tool that comes with the AspectWerkz distribution. This can easily be incorporated in the project build phase with an Ant script.
<!-- =================================================== -->
<!-- compiles aspects with AspectC -->
<!-- =================================================== -->
<target name="aspectc" depends="compile">
<exec executable="java">
<arg line="-cp . . . org.codehaus.aspectwerkz.definition.AspectC srcDir classDir"/>
</exec>
</target>
The Ant build file provided in the code sample will generate a dev2dev-example.jar file in the dist directory with the prepared tracing aspect.
> set ASPECTWERKZ_HOME=C:\src\aspectwerkz
> ant clean jar
Once post compiled, we can bundle our tracing aspect in a jar file if needed but we still need to tell AspectWerkz that we want to use this aspect. This is done by writing a small XML descriptor. The descriptor will then be used at class weave time by the AOP framework.
Here we also define the deployment model (scope or life-cycle) of the aspect. In this case we are using the
perThread deployment model which means that we will have one instance of this aspect per thread of execution, thus cross-cutting across several class instances. (This could have been defined using attributes as well, see the docs for details.)
<!DOCTYPE aspectwerkz PUBLIC
"-//AspectWerkz//DTD//EN"
"http://aspectwerkz.codehaus.org/dtd/aspectwerkz.dtd">
<aspectwerkz>
<system id="dev2dev-example">
<package name="example.logging">
<aspect class="LoggingAspect" deployment-model="perThread"/>
</package>
</system>
</aspectwerkz>
Weaving the aspectMost AOP frameworks would at this point require you to post-compile all your JDBC driver(s) jars to apply the tracing aspect to the class that matches the defined pointcuts, i.e. that implements
java.sql.Connection and java.sql.PreparedStatement, given that those ones are interfaces part of JDBC API and that your drivers implements them.This would lead to usability problems, since it would dramatically increase the complexity of AOP integration in your environment. While still providing this offline class post-compilation mechanism, AspectWerkz comes with many options that allow us to integrate it in our BEA WebLogic Server instance to be able to apply the aspects at class load-time, thus not dependant on driver we decide to use. The next section explains the required integration steps.
Enabling AOP in WebLogic Server
This part explains how we can now apply the Aspect on our target classes. The Management API in BEA JRockit JVM can bring us full speed seamless integration of AOP at class load time, but first we have to understand the more basic options we have to apply AOP in BEA WebLogic Server.Offline class post-compilation
AspectWerkz allows class post-compilation through the help of the
AspectWerkzC command line tool. Which transforms the classes according to the aspect definitions and the XML aspect deployment descriptor. Then we can deploy the transformed classes as usual, providing the jars needed for AspectWerkz in the WebLogic classpath.As explained this is not a suitable approach to our CMP SQL tracing problem. For further reference on the subject, refer to the AspectWerkz documentation.
Integrating AOP with WebLogic Server using the BEA JRockit Management API
To solve our problem, we need to be able to transform the classes at runtime and we can use a very simple and efficient way to allow class-load time weaving by running our BEA WebLogic Server instance under BEA JRockit JVM by using BEA JRockit Management API.
The JMAPI provides a JVM level class pre-processing mechanism and is a standardized and documented feature of BEA JRockit. To better understand how the JMAPI works, you could read a previous dev2dev article on the topic. The main idea is that a
ClassPreProcessor component is activated using the -Xmanagement:class=... JVM option and that is it. The BEA JRockit JVM will run at full speed and will call the component directly. Here is an excerpt of AspectWerkz's implementation of BEA JRockit MAPI's ClassPreProcessor class:
public class JRockitPreProcessor implements com.bea.jvm.ClassPreProcessor {
...
/**
* Concrete AspectWerkz weaver.
*/
private static ClassPreProcessor preProcessor;
/**
* The JMAPI ClassPreProcessor must be self registrating.
*/
public JRockitPreProcessor() {
JVMFactory.getJVM().getClassLibrary().setClassPreProcessor(this);
}
/**
* Weaves a class.
*
* @param caller classloader
* @param name of the class to weave
* @param bytecode original
* @return bytecode weaved
*/
public byte[] preProcess(ClassLoader caller, String name, byte[] bytecode) {
...
return preProcessor.preProcess(name, bytecode, caller);
}
}
Now we need to adapt the WebLogic sample domain startup script to use both BEA JRockit JVM and the JMAPI module:
...
@rem change VENDOR to BEA instead of Sun
set JAVA_VENDOR=BEA
@rem change JAVA_HOME to the JRockit installation
@rem Set JAVA_HOME to java virtual machine you want to run on server side.
set JAVA_HOME=C:\bea\jrockit81sp2_141_05
...
set EXAMPLE_APP_DIR=C:\temp\dev2dev\example
set ASPECTWERKZ_HOME=C:\src\aspectwerkz2
set AW_OPT=-Daspectwerkz.definition.file=%EXAMPLE_APP_DIR%\aspectwerkz.xml
set CLASSPATH=%WEBLOGIC_CLASSPATH%;%EXAMPLE_APP_DIR%\dist\dev2dev-example.jar;%CLASSPATH%
@rem set AspectWerkz environment
Call "%ASPECTWERKZ_HOME%\bin\setEnv.bat"
@rem set BEA JRockit JMAPI implementation path
set CLASSPATH=%ASPECTWERKZ_HOME%\lib\aspectwerkz-extensions-0.10.RC2.jar;%CLASSPATH%
set CLASSPATH=%ASPECTWERKZ_HOME%\lib\aspectwerkz-core-0.10.RC2.jar;%CLASSPATH%
set CLASSPATH=%ASPECTWERKZ_HOME%\lib\bcel.jar;%CLASSPATH%
@rem activate the JMAPI module for AspectWerkz
set JAVA_OPTIONS=-Xmanagement:class=org.codehaus.aspectwerkz.extension.jrockit.JRockitPreProcessor %JAVA_OPTIONS%
@rem standard %JAVA_HOME%\bin\java invocation
%JAVA_HOME%\bin\java %JAVA_VM% %MEM_ARGS% %JAVA_OPTIONS% %AW_OPT%
-Dweblogic.Name=%SERVER_NAME% -Dweblogic.ProductionModeEnabled=%PRODUCTION_MODE%
-Djava.security.policy="%WL_HOME%\server\lib\weblogic.policy" weblogic.Server
Once the server has started we can use the Ant client to call a CMP EJB from the sample domain and see our aspect at work.
During the startup sequence, you might see unusual SQL messages in the output window. This is our tracing aspect which is already at work. For sake of simplicity we did not use the
cflow AOP construct (which allows us to pick out control flows in the program execution), but the optimal solution to only trace SQL generated by a specific CMP bean would have make use this powerful construct (refer to AspectWerkz AOP documentation). After the WebLogic sample domain startup the following message appears in the output window:
<WebLogicServer> <BEA-000355> <Thread "ListenThread.Default" listening on port 7001, ip address *.*>
We now can call the CMP EJB from an Ant client. A client application is included in the WebLogic sample domain client part that comes with WebLogic Server 8.1:
> cd C:\bea\weblogic81\samples\domains\examples
> setExamplesEnv
> cd %EXAMPLES_HOME%\src\examples\ejb20\basic\containerManaged
> ant all
> ant run
You should then see the tracing aspect's output in the stdout log window of the example domain:
...
===> query: INSERT INTO ejbAccounts (id, bal, type) VALUES (?, ?, ?)
index=1 value=ID: 4
index=2 value=4000.0
index=3 value=Savings
===> query: SELECT WL0.id, WL0.type, WL0.bal FROM ejbAccounts WL0 WHERE ( WL0.id = ? )
index=1 value=ID: 5
...
Examples of real-world use
Tracing is the most simple and easy to understand example on how to use AOP, it is however not limited to that. Here is a (very incomplete) list of concerns that are very suitable to be implemented with AOP:- role-based security
- transaction demarcation
- persistence
- lazy loading
- eager loading (loading policies)
- asynchronous calls
- synchronization
- caching
- virtual mock objects for unit testing
- performance optimizations
- design patterns
- business rules