Skip to main content

EJB best practices: Validation helper classes

Get a little help(er) to avoid code redundancy in data-format validation

Brett McLaughlin (brett@newInstance.com), Author and Editor, O'Reilly Media Inc.
Photo of Brett McLaughlin
Brett McLaughlin has been working in computers since the Logo days (remember the little triangle?). He currently specializes in building application infrastructure using Java and Java-related technologies. He has spent the last several years implementing these infrastructures at Nextel Communications and Allegiance Telecom, Inc. Brett is one of the co-founders of the Java Apache project, Turbine, which builds a reusable component architecture for Web application development using Java servlets. He is also a contributor of the EJBoss project, an open source EJB application server, and Cocoon, an open source XML Web-publishing engine. Contact Brett at brett@oreilly.com.

Summary:  Well-designed validation procedures can increase data integrity, ensure your applications run smoothly, and make future changes in data easier to handle. In this edition of EJB best practices, Brett McLaughlin expands upon the validation techniques discussed in the last tip, and improves upon the initial concepts.

View more content in this series

Date:  01 Jan 2003
Level:  Intermediate
Activity:  605 views
Comments:  

In the last installment, we started a discussion about data validation, which is one of the essential components of enterprise application design. After a quick review of the two types of data validation, data-format validation and business-specific validation, we discussed the most beneficial placement of each type of validation logic in your application code. We ended up with good, workable solutions for both data-format and business-specific validation, but we didn't really address the larger complexities you might face in this area of programming.

Specifically, we decided the best way to deal with data-format validation code was to keep it close to the client, thus keeping the processing overhead to a minimum. Because our example application included a business delegate class, we placed the validation logic there. The problem with this placement is that it could introduce a lot of redundancy into your code.

Change management and data validation

Changing data formats is a fairly common occurrence in enterprise applications. While the format for ISBN and social security numbers is fairly static, IP addresses and UPC codes may need to change over time. If your validation code is spread throughout your application, you'll have to track down and change each instance one by one, a procedure that is tedious and prone to error. And, if you make the common mistake of leaving one or two instances unchanged, your data could end up being corrupted by your own validation logic!

If you've consolidated your validation logic you only need to make the format change in one place and your entire application will benefit.

In this tip, we'll revisit data-format validation, introducing a data validation helper class, which will let us keep the validation procedures close to the client without introducing any unnecessary code.

Consolidating validation logic

It's quite common to have the same data type used as an argument to multiple business methods, across multiple business delegates. For example, a book's ISBN might be passed to the search method of an Inventory delegate and the purchasing method of a Payment delegate. If our validation logic were tied to the business delegates, as it was in the last tip, we would end up with ISBN validation code in both of these methods.

The first step to improving our data-format validation procedure is to move all the validation logic into a single helper class, from which other methods can invoke it as needed. This type of consolidation both reduces code redundancy and makes it possible to maintain and change our application's data formats when we need to.

To achieve consolidation, we'll use a Validator class, which simply provides static methods for the various data types we need to validate. Listing 1 shows the shell for such a class.

Note that the simple methods in Listing 1 can be called from other classes, such as our business delegates, to perform validation logic as needed. Also note that the methods do not return boolean values.


Listing 1. A Validator skeleton

package com.ibm.validation;

import java.util.Iterator;
import java.util.List;

public class Validator {

    public static void validateISBN(String isbn)
	    throws InvalidDataException {
	
	    // Check the data type, and throw an error if
		//   needed	
    }
	
	public static void validateIPAddress(String ipAddress)
	    throws InvalidDataException {
		
		// Check data type
	}

    public static void validateUPC(String upc)
	    throws InvalidDataException {
		
		// Check data type
    }
	
	public static void validateUPC(float upc)
	    throws InvalidDataException {
		
		validateUPC(new String(upc));
	}
	
	public static void validateList(List list, Class class)
	    throws InvalidDataException {
		
		for (Iterator i = list.iterator(); i.hasNext(); ) {
		    Object obj = i.next();
			if !(obj instanceof class) {
			 throw new InvalidDataException("This list only " +
				    "accepts objects of type " + 
				    class.getName());
			}
		}
        }
}

Listing 2 shows the kind of cluttered code that would result from using boolean return values.


Listing 2. Using boolean return values in the Validator

    public boolean checkout(List books) throws ApplicationException {
	if (Validator.validateList(books, Book.class)) {	
                try {
                    return library.checkout(books);
                } catch (RemoteException e) {
                    throw new ApplicationException(e);
                }
        }
    }
	
	public Book lookup(String isbn) throws ApplicationException {
	    if (Validator.validateISBN(isbn)) {
		    try {
		   	    return library.lookup(isbn);
	        } catch (RemoteException e) {
		        throw new ApplicationException(e);
		    }
		}
	}

In more complex methods, the above nesting could get even more cluttered. We've avoided this extra level of complexity and kept our code much cleaner by simply allowing exceptions to be thrown. In addition, note that InvalidDataException extends ApplicationException. This allows the signature of all of the delegate methods to stay the same, and therefore also allows any validation exceptions to be thrown through the same mechanism. We avoid having to either add another throws clause to the method signature, or having to add another try/catch block to the method body. In short, it keeps our code clean and simple rather than riddled with brackets, application exceptions, validation exceptions, and if/then and try/catch blocks.


Using the Validator

With the Validator in place and set up for the specific data types we need to validate, it's a snap to use it in our application, and specifically in our business delegate methods. Listing 3 shows the delegate from the last tip, reworked to use the new Validator class.


Listing 3. Data-format validation in the business delegate

package com.ibm.library;

import java.rmi.RemoteException;
import java.util.Iterator;
import java.util.List;
import javax.ejb.CreateException;
import javax.naming.NamingException;

import com.ibm.validation.Validator;
import com.ibm.validation.InvalidDataException;

public class LibraryDelegate implements ILibrary {

    private ILibrary library;

    public LibraryDelegate() {
        init();
    }
    
    public void init() {
        // Look up and obtain our session bean
        try {
            LibraryHome libraryHome = 
                (LibraryHome)EJBHomeFactory.getInstance().lookup(
                    "java:comp/env/ejb/LibraryHome", LibraryHome.class);
            library = libraryHome.create();
        } catch (NamingException e) {
            throw new RuntimeException(e);
        } catch (CreateException e) {
            throw new RuntimeException(e);
        } catch (RemoteException e) {
            throw new RuntimeException(e);
        }
    }
    
    // No validation required for accessor (getter) methods
        
    public boolean checkout(Book book) throws ApplicationException {
	    // No validation required here; the object type
		//   takes care of it
	
        try {
            return library.checkout(book);
        } catch (RemoteException e) {
            throw new ApplicationException(e);
        }
    }
    
    public boolean checkout(List books) throws ApplicationException {
	    // Validate list
		Validator.validateList(books, Book.class);
	
        try {
            return library.checkout(books);
        } catch (RemoteException e) {
            throw new ApplicationException(e);
        }
    }
	
	public Book lookup(String isbn) throws ApplicationException {
	    // Validate ISBN
		Validator.validateISBN(isbn);
		
		try {
		    return library.lookup(isbn);
	    } catch (RemoteException e) {
		    throw new ApplicationException(e);
		}
	}
        
    // And so on...    
    
    public void destroy() {
        // In this case, do nothing
    }
}

Using a stand-alone Validator has rendered our code more modular and maintainable. Additionally, we've moved all our validation logic into one place, avoiding redundancy in the code. The result is a better, less bug-prone application.

In the next tip in this series, we'll examine the other side of validation: exception handling. Until then, I'll see you online.


Resources

About the author

Photo of Brett McLaughlin

Brett McLaughlin has been working in computers since the Logo days (remember the little triangle?). He currently specializes in building application infrastructure using Java and Java-related technologies. He has spent the last several years implementing these infrastructures at Nextel Communications and Allegiance Telecom, Inc. Brett is one of the co-founders of the Java Apache project, Turbine, which builds a reusable component architecture for Web application development using Java servlets. He is also a contributor of the EJBoss project, an open source EJB application server, and Cocoon, an open source XML Web-publishing engine. Contact Brett at brett@oreilly.com.

Comments



Trademarks

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=Java technology
ArticleID=10745
ArticleTitle=EJB best practices: Validation helper classes
publish-date=01012003
author1-email=brett@newInstance.com
author1-email-cc=