October 7, 2024, Monday, 280

Defensive Programming With AOP

From NeoWiki

Revision as of 09:12, 5 March 2007 by Neo (Talk | contribs)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to: navigation, search
OVal takes the legwork out of writing repetitive conditionals

Andrew Glover, President, Stelligent Incorporated

30 Jan 2007

While defensive programming effectively guarantees the condition of a method's input, it becomes repetitive if it is pervasive across a series of methods. This month, Andrew Glover shows you an easier way to add reusable validation constraints to your code using the power of AOP, design by contract, and a handy library called OVal.


The major downside to developer testing is that the vast majority of tests exercise sunny-day scenarios. Defects rarely occur for these situations -- it's the edge cases that usually cause problems.

What's an edge case? It's the situation where, for instance, someone passes a null value to a method not coded to handle nulls. Many developers fail to test for such scenarios because they don't make much sense. But sense or no sense, these things happen, and then a NullPointerException is thrown and your whole program blows up.

This month, I suggest a multifaceted approach to dealing with the less predictable defects in your code. Find out what happens when you combine defensive programming, design by contract, and an easy-to-use generic validation framework called OVal.

Tools clipart.png Tip: Download OVal and AspectJ

You need to download OVal and AspectJ to implement the programming solution described in this article. See Defensive_Programming_With_AOP#Resources to download these technologies now and follow along with the examples.

Exposing the enemy

The code in Listing 1 builds a class hierarchy for a given Class object (omitting java.lang.Object because everything ultimately extends it). If you look carefully, however, you'll notice a potential defect waiting to be exposed because of assumptions in the method regarding object values.

Listing 1. A method without checks for null

public static Hierarchy buildHierarchy(Class clzz){
  Hierarchy hier = new Hierarchy();
  hier.setBaseClass(clzz);
  Class superclass = clzz.getSuperclass();
  if(superclass != null && superclass.getName().equals("java.lang.Object")){
    return hier; 
  } else {      
    while((clzz.getSuperclass() != null) && 
      (!clzz.getSuperclass().getName().equals("java.lang.Object"))){
      clzz = clzz.getSuperclass();
      hier.addClass(clzz);
    }	        
    return hier;
  }
}     

Having just coded the method, I haven't yet noticed the defect, but because I'm a developer testing fanatic, I write a routine test using TestNG. What's more, I use TestNG's handy DataProvider feature, which allows me to create a generic test case and vary the parameters to it through another method. Running the test case defined in Listing 2 yields two passes! Everything is good to go, right?

Listing 2. A TestNG test verifying two values

import java.util.Vector;
import static org.testng.Assert.assertEquals;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;

public class BuildHierarchyTest {

  @DataProvider(name = "class-hierarchies")
  public Object[][] dataValues(){
    return new Object[][]{
      { Vector.class, new String[] 
          {"java.util.AbstractList", "java.util.AbstractCollection"} },
      { String.class, new String[] {} }
    };
  }
  @Test(dataProvider = "class-hierarchies"})
  public void verifyHierarchies(Class clzz, String[] names) throws Exception{
    Hierarchy hier = HierarchyBuilder.buildHierarchy(clzz);
    assertEquals(hier.getHierarchyClassNames(), names, "values were not equal");
  }
}

I still haven't spotted the defect, but something about the code is bothering me. What if someone inadvertently passes in a null value for the Class parameter? The call clzz.getSuperclass() in the fourth line of Listing 1 would throw a NullPointerException, wouldn't it?

Testing my theory is easy; I don't even have to start from scratch. I simply add {null, null} to the multidimensional Object array in the dataValues method of the original BuildHierarchyTest (in Listing 1) and run it again. Sure enough, I get the nasty NullPointerException shown in Figure 1:

Figure 1. The dreaded NullPointerException