Using Apache Commons JEXL for Java Object Validation

Apache Commons JEXL is a simple generic  expression language that can be used for dynamic scripting. Among many possible applications, we apply it for dynamic Java object validation.

Object validation is rather common in many applications and there are numerous ways to do it. But via JEXL, you can put validation rules in a configuration file or database table and JEXL can load them up at runtime to validate a given object.  Here is an example on how to do it.

1. A Simple Validation Problem
Assuming a Person class with four attributes: SSN, firstName, lastName and birthYear. It has two different validation rules per different environment:
(1) Rule 1:  SSN != null and birthYear < 1990 when a Person object is processed in Bonus-prize-draw component.
(2) Rule 2: firstName != null  and lastName != null when a Person object is processed in Guest-book sign-up component.

The Person class:

public class Person {
  private String ssn;
  private String firstName;
  private String lastName;
  private Integer birthYear;

  public Person(String ssn, String firstName, String lastName,Integer birthYear) {
    this.ssn = ssn;
    this.firstName = firstName;
    this.lastName = lastName;
    this.birthYear = birthYear;
  }
  public String getSsn() {
    return ssn;
  }
  public String getFirstName() {
    return firstName;
  }
  public String getLastName() {
    return lastName;
  }
  public Integer getBirthYear() {
    return birthYear;
  }
}

Then an instance of Person class called p is subjected to two validation rules defined above. We can denote above rules as four expression-syntax Strings :
“p.ssn != null”, “p.birthYear < 1990″, “p.firstName != null”, and “lastName != null”

2. The Validation Class
First we define a base abstract class called JexlObjectValidator

import java.util.List;
public abstract class JexlObjectValidator {
 // A list of JEXL expressions
 protected List<String> jexlExprRules;

 public JexlObjectValidator() {
 }
 public JexlObjectValidator(List<String> jexlExprRules) {
 super();
 this.jexlExprRules = jexlExprRules;
 }
 /**
 *
 * JEXL expression based general object validation.
 * See latest JEXL details at http://commons.apache.org/jexl/
 *
 * @param o
 * @return Return null if no error found.
 *         Otherwise first error of the JEXL expression validation rules.
 */
 public abstract ValidationError validate(Object o) ;
}

In this class the “List<String> jexlExprRules” attribute holds a list of String-based validation rules. The ValidationError validate(Object o) method validates
a given object based the specified jexlExprRules.
The ValidationError class simply holds the detailed message if the object is not valid. It may look like this:

public class ValidationError {
 private String errCode;
 private String errMsgs;

 public ValidationError(String errCode, String errMsgs) {
 super();
 this.errCode = errCode;
 this.errMsgs = errMsgs;
 }

 public String getErrCode() {
 return errCode;
 }
 public String getErrMsgs() {
 return errMsgs;
 }
}

Now we are ready to implement the validator for a person object.

import java.util.List;
import org.apache.commons.jexl2.Expression;
import org.apache.commons.jexl2.JexlContext;
import org.apache.commons.jexl2.JexlEngine;
import org.apache.commons.jexl2.MapContext;
public class PersonObjectValidator extends JexlObjectValidator {
 public PersonObjectValidator(List<String> jexlExprRules) {
 super(jexlExprRules);
 }

 public ValidationError validate(Object o) {
 Person person = (Person)o;

 JexlEngine jexl = new JexlEngine();
 JexlContext context = new MapContext();
 context.set("p", person);
 for (String expr : jexlExprRules ) {
 Expression e = jexl.createExpression( expr );
 Boolean isValid = (Boolean)e.evaluate(context);
 if (!isValid) {
 // Whoops, invalid object
 return new ValidationError("personErr", e.getExpression());
 }
 }        
 // OMG, an valid object!
 return null;    
}

}

In the validate(Object o) method, we first cast it to Person type and then initialize a JexlContext called “context”. Then we place the “person” object into the context with a name “p”.
We then iterate through rule list “jexlExprRules” to evaluate each expression rule against the given object “person” stored in the context. If any rule fails, we stop and return the ValidationError object with failure message(highlighted in above bold text). Otherwise return null for a valid object.

From the above sample code, you can see Jexl in the works  in these three simple validation steps:
(1) Use the JexlEngine object to create a Jexl Expression based a Jexl-syntax expression String
(2) Use the JexlContext object to hold an object to be validated(It holds more than one object in real-word applications)
(3) Use the jexl Expression created in step (1) to evaluate against the object stored in the context

You might wonder why the above expression’s evaluate(context) method call result can be cast to a Boolean object. The reason is the expression we defined earlier such as p.ssn != null always returns a boolean value. JEXL expression can return any object based on the expression defined by your application – it could be an Integer or a specific domain object etc.
Please refer to http://commons.apache.org/jexl/index.html for more details.

3. The Validation Process
The following unit test mocks the scenarios we defined in section 1(A Simple Validation Problem) – in which the same person object  is valid in Bonus-prize-draw application component while invalid in Guest-book sign-up component.

package sandbox.util;

import java.util.ArrayList;
import java.util.List;
import junit.framework.TestCase;
public class PersonObjectValidatorTest extends TestCase {
 private List<String> rule1;
 private List<String> rule2;
 private Person p;

 protected void setUp() {
 // Rule 1
 rule1 = new ArrayList<String>();
 rule1.add("p.ssn != null");
 rule1.add("p.birthYear < 1990");    
 // Rule 2
 rule2 = new ArrayList<String>();
 rule2.add("p.firstName != null");
 rule2.add("p.lastName != null");    
 // Person object to be validated
 p = new Person("123-45-1234","John", null, 1980);

 }
 // The person object is valid in one run-time application case
 public void testValidPersonObjectInBonusPrizeDrawComponent() {    
 PersonObjectValidator validator = new PersonObjectValidator(rule1);
 ValidationError err = validator.validate(p);
 assertNull(err);
 }
 // The same person object is invalid in another run-time application case
 public void testInValidPersonObjectInGuestbookSignupComponent() {
 PersonObjectValidator validator = new PersonObjectValidator(rule2);
 ValidationError err = validator.validate(p);
 assertNotNull(err); // The lastName is null to cause the person object being invalid
 }
}

Notice JEXL uses Java Reflection API extensively and above “p.firstName != null” rule is actually invoked as “p.getFirstName() != null”. In real-world application, your expression can contain any method call on the object.

4. Conclusion

The example here is extremely simple to just demo the dynamic object validation. In above unit test code, you can see the validation rule is specified as List<String> and you can load them from a configuration file/DB table in your application. This way you can re-configure rules dynamically per your application needs.

The above sample code uses apache-commons-jexl-2.0 and can be downloaded at http://commons.apache.org/jexl/index.html. Or if using Maven project build, the dependency entry is

<dependency>
 <groupId>org.apache.commons</groupId>
 <artifactId>commons-jexl</artifactId>
 <version>2.0</version>
</dependency>
Advertisement

Tags: , , , ,

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Connecting to %s


Follow

Get every new post delivered to your Inbox.