Delicious

AspectWerkz – Declaring war on spaghetti code

I got acquainted with Aspect Oriented Programming (AOP) concepts during my college days. It was my seminar topic and one of the most 'talked about' programming paradigm in those days. Today the concept has grown to a great extend and there are few very good tools that support AOP.

Aspect Oriented Programming

Aspect Oriented Programming (AOP) is a programing paradigm, also referred to as a compliment to Object Oriented Programming (OOP). AOP tries to increase the modularity by helping in separating concerns. Let's start with a sample code with cross-cutting concern.

import org.apache.log4j.Logger;
public class MyClass {

    Logger logger = Logger.getLogger( MyClass.class.getName() );

    public void myMethod(int i) {
        logger.debug( "Entering myMethod, i=" + i );

        // Actual business logic goes here

        logger.debug( "Exiting method myMethod" );
    }
} 

I am sure that majority of the programmers must have seen similar code; unrelated and tangled with each other. In the above code snippet, the 'logger's responsibility is to trace the entry and exit of a method. This get in the way of our actual business logic. It sometimes becomes difficult to manage and understand code. This is called cross-cutting concerns, where entirely different concerns or aspects cut in between each other. This type of code is also known as spaghetti code.

Another similar example is the use of transaction managers. You may find utx.begin() , utx.commit() , utx.rollback() , and lot more code related to transaction management tangled with your business logic. Now imagine transaction management, exception handling, logging, and all sorts of mayhem in a single method. "Ugly", isn't it. That's where AOP comes to rescue.

Aspect Oriented Concepts

I will be demonstrating each of these concepts through examples later on. For now, we can briefly understand various concepts involved in AOP.

Introducing AspectWerkz

AspectJ is the most advanced of all AOP frameworks available in the market. Almost all Java-based AOP frameworks follow the standards set by AspectJ. Aspectwrekz does not provide the same level of features as AspectJ does, but it still will fit in almost all situations.

If you visit AspectWerkz website you might see the introduction of Aspectwerkz as "AspectWerkz is a dynamic, lightweight and high-performant AOP framework for Java". Aspectwrekz is probably the most easy to understand and implement. AspectWerkz support a fine-grained pattern language for matching join points.

The most basic pieces of and AOP based application are:

  1. Business Class - Class that contains the gist of the application.
  2. Aspect - Additional functionality that is needed / required but not part of the actual business.
  3. Aspect Definition File - Tells the weaver which aspects to weave and where. By default aspectwerkz uses META-INF/aop.xml from the classpath.

My First Aspect Example

Now lets start with a small 'Hello World' example to get to know how to use aspectwerkz in a project. Please refer aspectwrekz website for tutorials on how to use the framework.

First the Business class (HelloWorld.java).

package test;
public class HelloWorld {
    public void greet() {
        System.out.println("Greetings!");
    }
    public static void main(String args[]) {
        HelloWorld hw = new HelloWorld();
        hw.greet();
    }
}

Now our Aspect class (MyFirstAspect.java).

package test;
import org.codehaus.aspectwerkz.joinpoint.JoinPoint;

public class MyFirstAspect {
    public void beforeGreeting() {
        System.out.println("before greeting...");
    }
    public void afterGreeting() {
        System.out.println("after greeting...");
    }
}

Finally, the Aspect Definition File (META-INF/aop.xml).

 <aspectwerkz>
    <system id="AspectWerkzExample">
        <package name="test">
            <aspect class="MyFirstAspect">
                <pointcut name="HelloWorldMethods" expression="execution(* test.HelloWorld.*(..))"/>
                <advice name="beforeGreeting" type="before" bind-to="HelloWorldMethods"/>
                <advice name="afterGreeting" type="after" bind-to="HelloWorldMethods"/>
            </aspect>
        </package>
    </system>
</aspectwerkz>

The output when you compile, weave and run the above example would be:

before greeting...
Greetings!
after greeting...

Wow!!! We have written our first aspect...

Logger implemented as an Aspect

Now lets see how we can use AOP to improve our Logger example. First remove all the logging stuff from our Business class (MyClass.java).

package test;
public class MyClass {
    public int multiply(int i, int j) {
        // Only business logic goes here.. 
        return i*j;
    }
    public static void main(String args[]) {
        MyClass obj = new MyClass();
        obj.multiply(2, 3);
    }
}

Now our Aspect class (MethodCallTracer.java).

package test;
import org.codehaus.aspectwerkz.joinpoint.JoinPoint;
import org.codehaus.aspectwerkz.joinpoint.MethodRtti;
import org.apache.log4j.Logger;

public class MethodCallTracer {

    Logger logger;

    public void beforeMethodCall(JoinPoint joinPoint) {
        logger = Logger.getLogger( joinPoint.getCalleeClass().getName() );
        logger.debug("Entering method: " + joinPoint.getCalleeClass().getName() + "." + joinPoint.getSignature().getName());
        Object[] parameters = ( (MethodRtti)joinPoint.getRtti() ).getParameterValues();
        logParameters( parameters );
    }

    public void afterMethodCall(JoinPoint joinPoint) {
        logger = Logger.getLogger( joinPoint.getCalleeClass().getName() );
        logger.debug("Exiting method: " + joinPoint.getCalleeClass().getName() + "." + joinPoint.getSignature().getName());
        MethodRtti rtti = (MethodRtti)joinPoint.getRtti();
        if( !void.class.equals( rtti.getReturnType() ) )
            logger.debug( "Returning: " + rtti.getReturnValue() );
    }

    private void logParameters(Object[] parameters) {
        if(parameters.length > 0)
            logger.debug("Parameters: " + getCSV( parameters ) );
    }

    private String getCSV(Object values[]) {
        if(values == null) { return null; }
        StringBuilder buf = new StringBuilder();
        for (int i = 0; i < values.length; i++) {
            buf.append( values[i] );
            if (i < (values.length - 1)) {
                buf.append( "," );
            }
        }
        return buf.toString();
    }
}

Finally, the Aspect Definition File (META-INF/aop.xml).

<aspectwerkz>
    <system id="AspectWerkzExample">
        <package name="test">
            <aspect class="MethodCallTracer">
                <pointcut name="anyMethod" expression="execution(* test.*.*(..))"/>
                <advice name="beforeMethodCall" type="before" bind-to="anyMethod"/>
                <advice name="afterMethodCall" type="after" bind-to="anyMethod"/>
            </aspect>
        </package>
    </system>
</aspectwerkz>

The output would be something like:

Entering method: test.MyClass.myMethod
Parameters: 2, 3
Exiting method:test.MyClass.myMethod
Returning: 6

I hope you had fun with this sample. More to come!!!


Delicious