Friday, December 9, 2011

Coding null resilient methods using AOP, Java and AspectJ.

 

The last couple of months, I’ve been learning Aspect Oriented Programming, so I decided to write a short post about it. AOP helps a developer separate concerns, in a clean and simple way. No matter how good of a programmer you are, you will always find situations where certain concerns crosscut each other; For example: any method that uses “try/catch”. The developer ends up to tangling the business logic of the method with the error handling code. AOP helps separate these concerns. There are a lot of examples of AOP on logging, security, transactions, etc. For this post we’ll see how we can avoid invoking a method on a null object.

Whenever we use methods with arguments (objects), the developer should worry about checking if the passed object is null. Otherwise, invoking a method using a null object, will cause a runtime exception and crash your program. Making this null check validation has nothing to do with the business logic of the function. Moreover, the validation has to be repeated for every method we code. In addition, sometimes as we add arguments to a function we forget to update the null checks. Wouldn’t it be nice if we can be sure that we can call any method without having to check for nullability? Well it turns out that with aspects, this is rather simple, and even better we only have to code it once.

 

Suppose we have the class:

 

 

package MyTest;

public class MyConsolePrint {

       public void printStringRep(Object obj)

      {

          System.out.println(obj.toString());

      }

      public void printStringHashCode(Object obj)

      {

           System.out.println(obj.hashCode());

      }

      public void printAreEqual(Object obj1, Object obj2)

      {

            System.out.println(obj1.equals(obj2));

      }

}

 

If we pass a null object to any of the methods in this class, we are going to have problems. We’ll see how we can fix this without altering the class. To Test the application this is the code we’ll use.

 

import java.util.*;

import MyTest.MyConsolePrint;

 

public class Main {

    

       public static void main(String[] args) {

             

             MyConsolePrint console = new MyConsolePrint();

             

             ArrayList items = new ArrayList();

             items.add("hello");

             items.add(null);

             items.add(2);

             

             for(Object x: items)

             {

                   console.printStringRep(x);

                   console.printStringHashCode(x);

                   console.printAreEqual("hello", x);

             }

       }

}

 

Now we run the program and BOOM!

 

hello

99162322

hello == hello is true

Exception in thread "main" java.lang.NullPointerException

      at MyTest.MyConsolePrint.printStringRep(MyConsolePrint.java:8)

      at Main.main(Main.java:19)

 

 

The programs breaks when null is sent. However, this is expected. To fix this we can add null checks to our methods in MyConsolePrint. But imagine we have many of these cases. The amount of work would be huge. We’ll see how with one aspect we can intercept all method calls in the package/namespace, and avoid throwing an exception. Here is the Aspect:

 

package MyTest;

 

public aspect NullArgumentSafeAspect {

    

pointcut allMethodCallsWithinMyNamespace() :  

                                           execution(void *.*(..))//get all method calls

                                &&

                                           within(MyTest.*);//within my test namespace)

    

 

void around() : allMethodCallsWithinMyNamespace() {

            //before calling method

            for(Object arg: thisJoinPoint.getArgs())

            {

                  if(arg == null)

                  {

                       System.out.println("null arguments " +

                             "not safe to continue. Abort!");

                       return;//cancel the method call.

                   }

            }

            proceed();//call the method.

      }

}

 

The “pointcut allMethodCallsWithinMyNamespace() groups all method invocations within the MyTest namespace. The “advice” “void around ..” binds the implementation of our validation, to the “pointcut”. The invocation of the method is cancelled if null arguments are sent. This single Aspect works for all void methods with any type of arguments within the namespace of interest.

To conclude, using Aspect Oriented Programming we were able to separate the “null argument check” concern from the logic of the method. The best part is that we did this without altering the original class, and all the code is localized within the Aspect.

 

 

And here is the exception free output  : )

 

hello

99162322

hello == hello is true

null arguments not safe to continue. Abort!

null arguments not safe to continue. Abort!

null arguments not safe to continue. Abort!

2

2

hello == 2 is false