Jan. 2, 2005, 4:09 a.m.
IT

Validating user input

When should you perform input validation, and how thorough should you be? This is a question many developers are struggling with.

This ties back to one of my earlier comments on defensive programming techniques. However, here I will present a more detailed example. Most competent developers will perform the basic input validation to ensure correct input, such as ensuring a date has the correct format, an age is positive etc. However, should you explicitly add a check in your code for null values? Java will generate a NullPointerException in most cases when performing an operation such as method invocation on a null object.

There are two reasons why I prefer to explicitly check the input when data is passed to a method from an external party. Firstly, I do not trust the third party, be that another developer making use of my API, or myself using a class from another package. Secondly, if a null value slips through, I need to show to the world that I have considered such a scenario, and that I have actively coded to handle it. If I depend on NullPointerExceptions to be thrown, I might just one day discover that the API I am using has a side effect that it does not properly handle null values, and thus return nonsense.

Consider the following piece of code in pseudo language:

function newestDate(Date Date1, Date Date2) returns Date
  if Date1 before Date2
    return Date2
  else
    return Date1
end function

Ignoring the scenario where Date1 == Date2, this method is simple yet void of any error checking. You might be asking why it is necessary to perform any error checking? Well, let's implement that pseudo-code in Java:

 6      public Date newestDate(Date pDate1, Date pDate2) {
 7        if (pDate1.before(pDate2))
 8          return pDate2;
 9        else
10          return pDate1;
11      }

This should work fine for valid input. What will happen if I pass in null for pDate1? Or pDate2? Lets see what happens if pDate1 is null:

waldo@waldonbm tmp $ java DateTest
Exception in thread "main" java.lang.NullPointerException
        at DateTest.newestDate(DateTest.java:7)
        at DateTest.main(DateTest.java:14)  

and the code looks as follow:

13      public static void main(String[] args) {
14        System.out.println("The newest date between 2004-10-15 and 2005-01-01 is: "+
15                        new DateTest().newestDate(null,new Date("2005/01/01")));
16      }    

Looking at the exception, we know something was null. The most information we have is that something in DateTest.java line 7 was null. Repeating that line:

 7        if (pDate1.before(pDate2))

it is clear that either pDate1 or pDate2 could have been null. Let's try to run the app again, but this time making pDate2 null:

waldo@waldonbm tmp $ java DateTest
Exception in thread "main" java.lang.NullPointerException
        at java.util.Date.before(Date.java:858)
        at DateTest.newestDate(DateTest.java:7)
        at DateTest.main(DateTest.java:14)

Using this knowledge as well as the previous trial run's knowledge, it is possible to deduce that in the first case, pDate1 had to be null and in the second case, pDate2 had to be null (since the exception is now thrown from within java.util.Date). However, without having internal knowledge of the java.util.Date class, there is no way without trial and error we will be able to safely know from the error message, which date was null. Also, we trust the java.util.Date class to validate the date. Let me enhance the code to the standards I would normally write such a method:

 6      public Date newestDate(Date pDate1, Date pDate2) {
 7        if (pDate1 == null)
 8          throw new RuntimeException("pDate1 was null");
 9        if (pDate2 == null)
10          throw new RuntimeException("pDate2 was null");
11        
12        if (pDate1.before(pDate2))
13          return pDate2;
14        else
15          return pDate1;
16      }

This might seem redundant, but look at what happens with a null pDate1 and pDate2, respectively:

waldo@waldonbm tmp $ java DateTest
Exception in thread "main" java.lang.RuntimeException: pDate1 was null
        at DateTest.newestDate(DateTest.java:8)
        at DateTest.main(DateTest.java:19)

and

waldo@waldonbm tmp $ java DateTest
Exception in thread "main" java.lang.RuntimeException: pDate2 was null
        at DateTest.newestDate(DateTest.java:10)
        at DateTest.main(DateTest.java:19)

This was a trivial example, but the concept sticks - if you do not properly handle all potential garbage a user of your code can throw at you, you are at the mercy of the inner logic of your code as to the result. This is obviously unreliable and non-deterministic, unless you update your code with every release Sun makes to Java.

I prefer to handle any devastating or non-recoverable scenario's like this by explicitly stating my opinion in code. It is called proactive programming.