4/27/2012

Avoid Null Parameters With Annotation


In JEE6 there is a NotNull annotation which is of very limitted functionality. In my recent project I came across a requirement to check methods parameters for null. Writing if blocks in all the methods is not a good solution. So I decided to go with an AOP solution. I used AspectJ for all my AOP stuff in this blog. Here is what I did, I created an annotation called NotNull and an Aspect with Around pointcut to intercept method calls which has its parameters annotation with NotNull annotation.

Now I will walk through the code.
Code Snippet for NotNull Annotation:
 package org.naveen.maven.research.annotations;

 import java.lang.annotation.ElementType;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.lang.annotation.Target;

 @Retention(RetentionPolicy.RUNTIME)
 @Target(ElementType.PARAMETER)
 public @interface NotNull {
  /**
   * Message set in the exception thrown when parameter is null.
   */
  String message() default "Parameter value is not nullable.";
  
  /**
   * Will specify how null values have to be treated. 
   */
  NotNullPolicy policy() default NotNullPolicy.THROW_EXCEPTION;
  
  /**
   * If you want to throw a custom exception. 
   * By default this will throw an IllegalArgument Exception.
   */
  Class<? extends Throwable> exception() default IllegalArgumentException.class;
 }
 

Here is the NotNullPolicy enumeration:
 package org.naveen.maven.research.annotations;

 public enum NotNullPolicy {
  /** Irrespective of method body, method will return null if all arguments with this policy is null. */
  RETURN_NULL, 
  /** Will throw an exception. [Default] */
  THROW_EXCEPTION, 
  /** Don't do anything and will continue with method body */
  CONTINUE,
  /** Irrespective of method body, method will return null. */
  RETURN_NULL_IMMEDIATE
 }
 

And here comes the backbone, the Aspect.
 package org.naveen.maven.research.aspects;

 import java.lang.annotation.Annotation;
 import java.lang.reflect.Constructor;
 import java.lang.reflect.Method;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.logging.Level;
 import java.util.logging.Logger;

 import org.aspectj.lang.ProceedingJoinPoint;
 import org.aspectj.lang.Signature;
 import org.aspectj.lang.annotation.Around;
 import org.aspectj.lang.annotation.Aspect;
 import org.aspectj.lang.reflect.MethodSignature;
 import org.naveen.maven.research.annotations.NotNull;
 import org.naveen.maven.research.annotations.NotNullPolicy;

 @Aspect
 public class NotNullAspect {
  
  private static final Logger logger = Logger.getLogger(NotNullAspect.class.getName());

  @Around("execution(* org.naveen..*(..,@NotNull (*),..)) && " +
    "(!within(org.naveen.maven.research.annotations.*))")
  public Object doNullParamCheck(ProceedingJoinPoint jp) throws Throwable {
   List<Boolean> lazyNullRtValue = new LinkedList<Boolean>();
   boolean hasToContd = false;
   Object[] args = jp.getArgs();
   Signature sig = jp.getSignature();
   if(sig instanceof MethodSignature) {
    MethodSignature mtdSig = MethodSignature.class.cast(sig);
    Method mtd = mtdSig.getMethod();
    logger.log(Level.INFO, String.format("Checking parameters for method --> %s", mtd.getName()));
    Annotation[][] anns = mtd.getParameterAnnotations();
    
    for(int i=0;i<anns.length;++i) {
     if(anns[i].length > 0) {
      if(anns[i][0] instanceof NotNull) {
       NotNull ntnAnn = (NotNull) anns[i][0];
       logger.log(Level.INFO, String.format("NotNull handling policy is set to %s", ntnAnn.policy()));
       if(args[i] == null) {
        switch(ntnAnn.policy()) {
        case THROW_EXCEPTION:
         Class<? extends Throwable> expCls = ntnAnn.exception();
         logger.log(Level.INFO, String.format("Exception class set is %s", expCls.getName()));
         if(expCls == IllegalArgumentException.class) {
          throw new IllegalArgumentException(
           String.format("In Method %s --> %s", 
             mtd.getName(), ntnAnn.message()));
         } else {
          Constructor<? extends Throwable> ct = expCls.getConstructor(String.class);
          Throwable t = ct.newInstance(String.format("In Method %s --> %s", 
                  mtd.getName(), ntnAnn.message()));
          throw t;
         }
        case RETURN_NULL_IMMEDIATE:
         return null;
        case RETURN_NULL:
         logger.log(Level.WARNING, "One NotNull annotated argument has null value. Continuing with lazy checking.");
         lazyNullRtValue.add(false);
         break;
        case CONTINUE:
         hasToContd = true;
         lazyNullRtValue.add(true);
         break;
        }
       } else {
        hasToContd = true;
        if(ntnAnn.policy() == NotNullPolicy.RETURN_NULL) 
         lazyNullRtValue.add(true);
       }
      }
     }
    }
   }
   logger.log(Level.INFO, String.format("Argument length is %d and LazyReturn list is %s and hasToContd %s", 
            args.length, lazyNullRtValue, hasToContd));
   if(hasToContd && checkLazyNull(lazyNullRtValue)) 
    return jp.proceed();
   return null;
  }
  
  private boolean checkLazyNull(List<Boolean> list) {
   if(list.size() == 0) return true; 
   for(Boolean b : list)
    if(b) return b;
   return false;
  }
 }
 

 @Around("execution(* org.naveen..*(..,@NotNull (*),..)) && " +
   "(!within(org.naveen.maven.research.annotations.*))")
 
Above code snippet will instruct AOP weaver to weave classes which satifies following conditions:
  • Which has methods which is declared inside classes which are declared in org.naveen package or sub package of it.
  • Which has methods whoose paramters are annotated with NotNull annotation.
  • Dont weave classes inside org.naveen.maven.research.annotations package or sub package of it.

Different Usage examples:
Throws custom exception:
 public String test(String message, @NotNull(exception=ParameterException.class) String name) {
  return message + name;
 }
 

Throws default exception with custom message:
 private String privateTest(@NotNull String message, @NotNull(message="Name cannot be null.") String name) {
  return message + name;
 }
 
Method exists with Null:
 private String privateTest(@NotNull(policy=NotNullPolicy.RETURN_NULL_IMMEDIATE) String greet) {
  System.out.println("Message received is " + greet);
  return "Hello World " + greet;
 }
 
If you call above privateTest() method with a null argument, you don't see that System out getting executed.

Suppose if you have a scenario like this:
  • You have a method which will accept 2 arguments.
  • Method returns null, if both arguments are null.
  • Execute method, if one of the argument is not null.
You can use NotNullPolicy.RETURN_NULL policy to instruct NotNull aspect to do a lazy null checking.
 private String privateTest(@NotNull(policy=NotNullPolicy.RETURN_NULL) String message, 
          @NotNull(policy=NotNullPolicy.RETURN_NULL, message="Name cannot be null.") String name) {
  System.out.println("Message received is " + message);
  return "Hello World " + name;
 }
 

You can combine both NotNullPolicy.RETURN_NULL and NotNullPolicy.RETURN_NULL_IMMEDIATE together.
 private String privateTest(@NotNull(policy=NotNullPolicy.RETURN_NULL) String message, 
          @NotNull(policy=NotNullPolicy.RETURN_NULL_IMMEDIATE) String greet,
                      @NotNull(policy=NotNullPolicy.RETURN_NULL, message="Name cannot be null.") String name) {
  System.out.println("Message received is " + message);
  return "Hello World " + name;
 }
 

In the above example, we are saying :
  • Execute method body, if either of the arguments 'message' or 'name' is not null AND greet is not null.
  • Return Null, if both arguments 'message' and 'name' is null
  • Return Null, if argument 'greet' is Null


All Resources used in this project are available in My GitHub Repo.


4/25/2012

Templatize Java Objects


In my recent project, there is so many places we are using templates. Like web pages with user based custom messages and information, email and letter templates etc. In Java we have MessageFormat class to do some simple templating. But in our case we need to fill these templates from pre-populated java objects. Something similar to JasperReports will do with jrxml templates.
I know we can write a utility to do this in java using Reflection. If I'm using reflection, then I will end up in writing so many lines of code, which I really hate. Groovy has this magical MOP which comes very handy. So I choose groovy to build this utility.

Here is the groovy code for the utility:
 @Log4j
 class Templatizer {
      
        def static final PATTERN = ~/[\$]{1}\{([^}]*)\}/
 
        def static String toString(final String template, def target) {
            def tempStr = template
            PATTERN.matcher(template).findAll {it ->
                try {
                    tempStr = tempStr.replace(it[0], target."${it[1]}")
                } catch(MissingPropertyException e) { }
            }
            tempStr
        }

        def static String toString(String template, List target) {
            toString(template, target, "\n")
        }
 
        def static String toString(String template, List target, String seperator) {
            def output = new StringBuilder()
            target.each { output.append(toString(template, it)).append(seperator) }
            output.toString()
        }
 }
 

Here is how we use it (Groovy Version):
 EmployedPerson p = new EmployedPerson([name: "David Foo", dob: new Date(), salary: 90000.00])
 println Templatizer.toString("Templatizing Test: My Name is ${name}. I born in ${dob} and I am earning a salary of ${salary}", p)
 

Java Version:
 EmployedPerson p = new EmployedPerson("David Foo", new Date(), 90000.00);
 System.out.println(Templatizer.toString("Templatizing Test: My Name is ${name}. I born in ${dob} and I am earning a salary of ${salary}", p));
 


You can compile this class and you can use with in your java objects. You can use Ant to compile groovy code. For more details on groovy-ant compilation, refer my previous blog


2/09/2012

Another Java-Database Mapping Utility

Another Java-Database Mapping Utility



In one of my previous blog I discussed about one ResultSet Mapping utility. After using that utility in couple of my projects, I came across some of the shotfalls of this utility. Like:

  • There is no support for primitive data types. If you want to retrieve only one column from database also you need to create an Object.
  • Need to use sql classes like Connection, Statement and Resultset objects explicitly.
After looking into Groovy, I was amazed by its dynamic nature and Meta Programming model. Meta Programming model can be used as an alternative to remove lot of boiler plate java reflcetion code. So I decided to go with Groovy for my new Utility. There is another reason to choose groovy, once groovy classes are compiled it is compiled to java byte code. So you can use it in your java applications also.

@Log4j
 class DBUtil<T> {
  
  def static final primitives = [Integer.class, String.class, Long.class, Double.class, Float.class]
  
  /**
   * Will execute given query and convert result to a collection of specified class.
   * 
   * @param dataSource - JNDI Name of the Datasource 
   * @param query - Select query
   * @param params - List of parameters in query
   * @param clazz - Return type
   * @return List of objects of clazz argument type
   */
  static def executeQuery(String dataSource, String query, List params, Class clazz) {
   log.debug "Executing query ${query} with parameters ${params} using datasource ${dataSource}"
   
   def objList = [], obj, colName = "", field, sql
   try {
    sql = Sql.newInstance(lookup(dataSource))
    if(primitives.find {it == clazz}) {
     sql.eachRow(query, params) {row ->
      colName = row.getMetaData().getColumnName(1)
      objList << row."$colName"
     }
    } else {
     def fieldMap = mapFieldNames(clazz)
     sql.eachRow(query, params) {row -> 
      obj = clazz.metaClass.invokeConstructor()
      (0..row.getMetaData().columnCount-1).each {
       colName = row.getMetaData().getColumnName(it+1)
       field = fieldMap.find {it.key.compareToIgnoreCase(colName) == 0}
       if(field) obj."$field.value" = row."$colName"
      }
      objList << obj
     }
    }
   } catch(Exception e) {
    log.error e.message, e
    throw new DAOException(e)
   } finally {
    sql?.close()
   }
   objList
  } 
  
  private static Map mapFieldNames(Class<?> clazz) {
   def flds = [], fld, fldMap = [:]
   def metaProps = clazz.metaClass.properties
   def fields = clazz.getDeclaredFields()
   metaProps.each {metaProp -> 
    fld = fields.find {metaProp.name == it.name}
    if(fld) flds << fld
   }
   flds.each{it-> 
    def fldName = it.name
    if(fldName != 'metaClass') {
     if(it.isAnnotationPresent(Column.class)) {
      def colAnn = it.getAnnotation(Column.class)
      fldMap[colAnn.name()] = fldName
     } else fldMap[fldName] = fldName
    }
   }
   fldMap
  }
  
  /**
   * Will lookup for a datasource with given JNDI Name in the JNDI Context
   */
  private static def lookup = {jndiName ->
   def ctx, ds
   try {
    log.debug "Looking up for a datasource with JNDI name ${jndiName}"
    ctx = new InitialContext()
    ds = ctx.lookup(jndiName)
   } finally {
    ctx?.close()
   }
   return ds
  }.memoize()
 }
 
Now let me go through the code.

  • mapFieldNames() method process the Class which we need to map. This method will find all fields in the Class which needs to be mapped and will find whether any name overriding is needed. I reusing the same Column annotation declared in my old ResultSet Mapper blog to resolve the colum and variable name difference.
  • executeQuery() method will lookup the datasource and execute the given query. After that it will try to map field names returned by mapFieldNames() with resultset.

Please refer to Groovy SQL documentation to use it in a stand alone environment where a datasource is not available.

Usage:

In Java:
DBUtil.executeQuery("jdbc/MyDBJNDI", 
         "select empno, lname, fname, birth, deptno from employees where upper(lname) like ?", 
         Arrays.asList(new String[]{"AND"}), Employee.class);
 

In Groovy:
DBUtil.executeQuery("jdbc/MyDBJNDI", 
         "select empno, lname, fname, birth, deptno from employees where upper(lname) like ?", 
         ['AND'], Employee.class);
 

Suppose if your query returns only one String, then you could use this utility as

def emps = DBUtil.executeQuery("jdbc/MyDBJNDI", 
         "select lname from employees where empno = ?", 
         [120L], String.class);
 println "Last Name of Employee with empno 120 is ${emps[0]}"
 

Suppose if your query returns multiple String values, then you could use this utility as

def emps = DBUtil.executeQuery("jdbc/MyDBJNDI", 
         "select lname from employees where deptno = ?", 
         [100], String.class);
 println "Employees working in department 100 are ${emps}"
 



Since executeQuery() return type is Object you need an explicit cast to java.util.List when using from Java. To avoid this you can add one more function in DBUtil as below



static def <T> List<T> toList(String dataSource, String query, List<?> params, Class clazz) {
  log.debug "Executing query ${query} with parameters ${params} using datasource ${dataSource}"
  executeQuery(dataSource, query, params, clazz)
 }
 

Now it is really easy to use this Utility from Java also.
List<Employee> emps = DBUtil.toList("jdbc/MyDBJNDI", 
         "select empno, lname, fname, birth, deptno from employees where upper(lname) like ?", 
         Arrays.asList(new String[]{"AND"}), Employee.class);
 
 List<String> empLNames = DBUtil.toList("jdbc/MyDBJNDI", 
         "select lname from employees where empno = ?", 
         Arrays.asList(new Long[]{120L}), String.class);
 System.out.println(String.format("Last Name of Employee with empno 120 is %s", empLNames.get(0)));
 


To compile this groovy class, First you need to download Groovy. Then you can use either groovy eclipse plugin or ANT to build it. If you are using ANT, here is the snippet which you need to add to your build.xml for compiling groovy scripts.