Showing posts with label Articles - Java EJB. Show all posts
Showing posts with label Articles - Java EJB. Show all posts

Friday, November 4, 2011

Validation with pure Java - 5

Finally, you complete the Validator so it can retrieve and analyze the information. It will use a handy method for retrieving a PropertyDescriptor from the BeanInfo by the property's programmatic name. In case the property was not found for any reason, the method will notify the caller with a self-explanatory exception.
import java.beans.*;
/**
 * This class implements generic data-validation mechanism that is based on the external
 * rules defined in the BeanInfo class.
 */
public class Validator implements VetoableChangeListener {
  public static final String MAX_VALUE = "maxValue";
  public static final String MIN_VALUE = "minValue";
  /**
   * The only method required by {@link VetoableChangeListener} interface.
   */
  public void vetoableChange(PropertyChangeEvent evt) throws PropertyVetoException {
    try {
      // here we hope to retrieve explicitly defined additional information
      // about the object-source of the constrained property
      BeanInfo info = Introspector.getBeanInfo(evt.getSource().getClass());
      // find a property descriptor by given property name
      PropertyDescriptor descriptor = getDescriptor(evt.getPropertyName(), info);
      Integer max = (Integer) descriptor.getValue(MAX_VALUE);
      // check if new value is greater than allowed
      if( max != null && max.compareTo(evt.getNewValue()) < 0 ) {
        // complain!
        throw new PropertyVetoException("Value " + evt.getNewValue() + " is greater than maximum allowed " + max, evt);
      }
      Integer min = (Integer) descriptor.getValue(MIN_VALUE);
      // check if new value is less than allowed
      if( min != null && min.compareTo(evt.getNewValue()) > 0 ) {
        // complain!
        throw new PropertyVetoException("Value " + evt.getNewValue() + " is less than minimum allowed " + min, evt);
      }
    } catch (IntrospectionException ex) {
      ex.printStackTrace();
    }
  }
  /**
   * This utility method tries to fetch a PropertyDescriptor from BeanInfo object by given property
   * name.
   * @param name the programmatic name of the property
   * @param info the bean info object that will be searched for the property descriptor.
   * @throws IllegalArgumentException if a property with given name does not exist in the given
   * BeanInfo object.
   */
  private PropertyDescriptor getDescriptor(String name, BeanInfo info) throws IllegalArgumentException {
    PropertyDescriptor[] pds = info.getPropertyDescriptors();
    for( int i=0; i<pds.length; i++) {
      if( pds[i].getName().equals(name) ) {
        return pds[i];
      }
    }
    throw new IllegalArgumentException("Property " + name + " not found.");
  }
}

Well, believe it or not, that's it! You have all the pieces in place now and are ready to test your data validation. This simple driver does just that:
/**
 * A simple application that tries to cause data-validation exception
 * condition.
 */
public class Driver {
  public static void main(String[] args) {
    try {
      BusinessObject source = new BusinessObject();
      source.setNumericValue(10);
      System.out.println("Value accepted: " + source.getNumericValue());
      source.setNumericValue(-10);
      System.out.println("Value accepted: " + source.getNumericValue());
      source.setNumericValue(101);
      // we should never reach this line
      System.out.println("Value accepted: " + source.getNumericValue());
    } catch (Exception ex) {
      ex.printStackTrace();
    }
  }
}

When you run the application, you will see that the line source.setNumericValue(101) causes a PropertyVetoException with the following message: "Value 101 is greater than maximum allowed 100." Frankly, it is not often that exceptions make me happy. I guess this case is an exception.

Thursday, November 3, 2011

Validation with pure Java - 4


That code skeleton is just a starting point, but it demonstrates what is available at the beginning of the validation process. Surprisingly, it has a lot of information. From PropertyChangeEvent you can learn about the constrained property's object source, the name of the property itself, and its existing and proposed values. With that information handy, you can use the power of introspection to inquire about the property's additional information. What information should you look for? Validation rules, of course! A class java.beans.Introspector was designed solely for the purpose of collecting extra information about a target class. The information is provided in the form of a java.beans.BeanInfo object, which in this example, you may request with a simple call like this:
BeanInfo info = Introspector.getBeanInfo(evt.getSource().getClass());

The Introspector may collect BeanInfo in two ways. First it tries to get explicit information about the target class by looking for a class with the same name except for its BeanInfo suffix. In this case, it will look for a class named BusinessObjectBeanInfo. If for any reason such a class is not available, then the Introspector tries its best to dynamically build a BeanInfo object by using low-level reflection.
Although a fascinating subject, the implicit introspection is obviously not what you are going to rely upon in your quest for validation rules. One great thing about the BeanInfo class is its relation to the target class, which can be defined as a "rock-solid loose coupling." The absence of references from the target class makes the coupling very loose. Should the validation rules change, you just need to update and recompile the BeanInfo class without possibly affecting the target class. Clearly defined naming rules that are documented in the core Java API make compiling rock solid. This is where the rules-externalization approach with BeanInfo beats its proprietary-implemented counterparts hands down.
Before you start building the explicit BeanInfo class, I have to mention that you can load it with a variety of descriptors concerning the class itself, or its methods and events, not just the properties. Thanks to the creators of Java, you don't have to provide all that information. The Introspector picks up what is available explicitly and then performs its implicit analysis magic. Therefore, you can concentrate on the property descriptors only.
To describe a property's data-validation rules, you use the java.beans.PropertyDescriptor class. Looking at the documentation, you'll discover that this class has the facilities to provide every imaginable piece of information about the property, including data-validation rules! You use the setValue method to define the rules as a set of named attributes. Here is how you can constrain the bounds for the numeric property numericValue:
PropertyDescriptor _numericValue = new PropertyDescriptor("numericValue", targetClass, "getNumericValue", "setNumericValue");
_numericValue.setValue("maxVal", new Integer(100));
_numericValue.setValue("minVal", new Integer(-100));

The only weak point here is that you cannot rely on the power of the Java compiler to check the spelling of the attribute's names. Using a predefined set of constants can easily fix that. The class Validator is a good candidate for hosting such constants:
public static final String MAX_VALUE = "maxValue";
public static final String MIN_VALUE = "minValue";

So now, you can rewrite the same rules more safely. With all this in mind, let's build a final revision of the BeanInfo class:
import java.beans.*;
public class BusinessObjectBeanInfo extends SimpleBeanInfo {
  Class targetClass = BusinessObject.class;
  public PropertyDescriptor[] getPropertyDescriptors() {
    try  {
      PropertyDescriptor numericValue = new PropertyDescriptor("numericValue", targetClass, "getNumericValue", "setNumericValue");
      numericValue.setValue(Validator.MAX_VALUE, new Integer(100));
      numericValue.setValue(Validator.MIN_VALUE, new Integer(-100));
      PropertyDescriptor[] pds = new PropertyDescriptor[] {numericValue};
      return pds;
    } catch(IntrospectionException ex) {
      ex.printStackTrace();
      return null;
    }
  }
}

Wednesday, November 2, 2011

Validation with pure Java - 3


For the sake of keeping the code compact, the ConstrainedObject provides a method that validates int properties only. You can easily overload this method for the other types as well. If you do so, just make sure to wrap all the primitive types in their respective wrappers since PropertyChangeEvent delivers old and new values as references types. Now you are ready to issue a second revision of the BusinessObject:
import java.beans.*;
/**
 * This example class actually uses data-validation services defined in this framework.
 */
public class BusinessObject extends ConstrainedObject {
  private int numericValue;
  public void setNumericValue(int newNumericValue) throws PropertyVetoException {
    // validate proposed value
    validate("numericValue", numericValue, newNumericValue);
    // the new value is approved, since no exceptions were thrown
    numericValue = newNumericValue;
  }
  public int getNumericValue() {
    return numericValue;
  }
}

As you see, the setter method now looks a bit more complicated than before. It is a small price to pay for having a constrained property. Now you must build a Validator, which is the most interesting part of this exercise. This is where you learn how to externalize data-validation rules using nothing but pure Java.
import java.beans.*;
public class Validator implements VetoableChangeListener {
  public void vetoableChange(PropertyChangeEvent evt) throws PropertyVetoException {
    // do the validation here
  }
}