Common Java Cookbook

Edition: 0.19

Download PDF or Read on Scribd

Download Examples (ZIP)

9.4. Externalizing Logic with an Expression Language

9.4.1. Problem

You need to capture application logic in an external file.

9.4.2. Solution

Use an external properties file to store expressions used in an application. For this recipe, imagine yourself creating a system to sort Ball objects based on a set of arbitrary criteria. Instead of hard-coding criteria in a series of Java if-else clauses, create a framework with loads sorting criteria from a properties file containing boolean JEXL expressions. For instance, the first line in this properties file would be:

Hvy-Green-Basket = ball.color == 'Green' && (ball.weight > 1000)

This translates to "If the ball's color is Green and the weight is over 1000, put this ball into the Heavy Green basket." The name of each property is the name of the basket into which a Ball matching the criteria is placed. The contents of the criteria file are:

Hvy-Green-Basket = ball.color == 'Green' && (ball.weight > 1000)
Sm-Yellow-bin = ball.color == 'Yellow' && (ball.weight < 100)
Transparent-Bin = ball.isTransparent( )
Lrg-Yellow-Basket = ball.color == 'Yellow' &&(ball.weight >= 100)
Misc-Bin = true

Each criterion is applied to each Ball object in the order it appears in the criteria file. The heavy green sorting criteria is applied first, and each criterion is evaluated until the last criterion is reached. The last criteria always evaluates to true—similar to a switch-case control statement, the "Misc-bin" is the default. The following code reads this criteria file and evaluates each JEXL expression in order to sort a collection of Ball objects:

import org.apache.commons.jexl.Expression;
import org.apache.commons.jexl.ExpressionFactory;
import org.apache.commons.jexl.JexlContext;
import org.apache.commons.jexl.JexlHelper;
// Load in our criteria properties
Properties criteria = new Properties( );
criteria.load( getClass( ).getResourceAsStream("criteria.txt") );
Set binNames = criteria.getKeys( );
// Load our ball objects into a List
List balls = getBalls( );
Iterator ballsIter = balls.iterator( );
while( ballsIter.hasNext( ) ) {
    Ball ball = (Ball) ballsIter.next( );
    // Iterate through every rule, until you find a match...
    Iterator binIter = binName.iterator( );
    while( ruleIter.hasNext( ) ) {
        
        // Get the name of the basket
        String basket = (String) binIter.next( );
        // Get the expression corresponding to this bin.
        String expr = conditions.get( bin );
        Expression e = ExpressionFactory.createExpression( expr );          
        // Populate the context with the current Ball object
        JexlContext jc = JexlHelper.createContext( );
        jc.getVars( ).put("ball", ball);
        
        // Evaluate the Expression.
        Boolean result = (Boolean) e.evaluate(jc);
        
        // If the expression evaluated to true, add this Ball to the bin.
        if( result.booleanValue( ) == true ) {
            sendBall( ball, basket );
        }
    }
}

The result of the Expression evaluation is a Boolean value, and, if the Boolean result is true, the matching Ball is sent to the specified basket.

9.4.3. Discussion

Using this technique, as the number of criteria increases, the code to implement this sorting algorithm remains unchanged. The behavior of the system can be altered by changing the criteria file; compiled code is left untouched. The code in this Solution section is longer than a series of if-else clauses to implement these criteria in code, but, as the number of sorting criteria increases, you will be glad you took the extra time to create a general solution without hard coding system behavior in Java code.

This was the first example that involved something more than printing out a pretty message for human consumption. JEXL has been used to create a "language" for sorting criteria; if a client wants to change the rules, you can now train someone familiar with simple logic statements to change a system to meet changing requirements.

9.4.4. See Also

This recipe demonstrated a system that uses a simple set of rules to categorize balls. For more information about a serious open source Rule Engine for Java named JESS, take a look at http://herzberg.ca.sandia.gov/jess/index.shtml. If you are interested in Rule Engines, take a look at JSR 94: Java Rule Engine API (http://jcp.org/en/jsr/detail?id=94) or Ernest Friedman-Hill's "Jess in Action" (Manning).


Creative Commons License
Common Java Cookbook by Tim O'Brien is licensed under a Creative Commons Attribution-Noncommercial-No Derivative Works 3.0 United States License.
Permissions beyond the scope of this license may be available at http://www.discursive.com/books/cjcook/reference/jakartackbk-PREFACE-1.html. Copyright 2009. Common Java Cookbook Chunked HTML Output. Some Rights Reserved.