Add a simple rule engine to your Spring-based applications

Use Spring built-in facilities to capture business logic

Any nontrivial software project contains a nontrivial amount of so-called business logic. What exactly constitutes business logic is debatable. In the mountains of code produced for a typical software application, bits and pieces here and there actually do the job the software was called for—process orders, control weapons systems, draw pictures, etc. Those bits contrast sharply with others that deal with persistence, logging, transactions, language oddities, framework quirks, and other tidbits of a modern enterprise application.

More often than not, the business logic is deeply intermixed with all those other pieces. When heavy, intrusive frameworks (such as Enterprise JavaBeans) are used, discerning where the business logic ends and framework-inspired code begins becomes especially difficult.

There is one software requirement rarely spelled out in the requirement definition documents yet has the power to make or break any software project: adaptability, the measure of how easy it is to change the software in response to business environment changes.

Modern companies are forced to be quick and flexible, and they want the same from their enterprise software. Business rules that were so painstakingly implemented in your classes' business logic today will become obsolete tomorrow and will need to be changed quickly and accurately. When your code has business logic buried deep inside tons and tons of those other bits, modification will quickly become slow, painful, and error-prone.

No wonder some of the trendiest fields in enterprise software today are rule engines and various business-process-management (BPM) systems. Once you look through the marketing-speak, those tools promise essentially the same thing: the Holy Grail of Business Logic captured in a repository, cleanly separated and existing by itself, ready to be called from any application you may have in your software house.

Though commercial rule engines and BPM systems have many advantages, they also include many shortcomings. The easy one to pick on is the price, which can sometimes easily reach into the seven digits. Another is the lack of practical standardization that continues today in spite of major industry efforts and multiple on-paper standards available. And, as more and more software shops adapt agile, lean, and quick development methodologies, those heavyweight tools find it difficult to fit in.

In this article, we build a simple rule engine that, on one hand, leverages the clear separation of business logic typical for such systems and, on the other hand—because it's piggy-backed on the popular and powerful J2EE framework—doesn't suffer from the complexity and "uncoolness" of the commercial offerings.

Spring time in the J2EE universe

After the complexity of enterprise software became unbearable and the business-logic problem entered the spotlight, the Spring Framework and others like it were born. Arguably, Spring is the best thing that happened to enterprise Java in a long time. Spring provides the long list of tools and small code conveniences that make J2EE programming more object-oriented, much easier, and, well, more fun.

In the heart of Spring lies the principle of Inversion of Control. This is a fancy and overloaded name, but it comes down to these simple ideas:

  • Your code's functionality is broken into small manageable pieces
  • Those pieces are represented by simple, standard Java beans (simple Java classes that exhibit some, but not all, of the JavaBeans specification)
  • You do not get involved with managing those beans (creating, destroying, setting dependencies)
  • Instead, the Spring container does it for you based on some context definition usually provided in the form of an XML file

Spring also provides many other features, such as a complete and powerful Model-View-Controller framework for Web applications, convenience wrappers for Java Database Connectivity programming, and a dozen other frameworks. But those subjects reach well outside this article's scope.

Before I describe what it takes to create a simple rule engine for Spring-based applications, let's consider why this approach is a good idea.

Rule-engine designs have two interesting properties that make them worthwhile:

  • Firstly, they separate the business logic code from other areas of the application
  • Secondly, they are externally configurable, meaning that the definitions of the business rules and how and in which order they fire are stored externally to the application and manipulated by the rule creator, not the application user or even a programmer

Spring provides a good fit for a rule engine. The highly componentized design of a properly-coded Spring application promotes the placing of your code into small, manageable, separate pieces (beans), which are externally configurable via the Spring context definitions.

Read on to explore this good match between what a rule-engine design needs and what the Spring design already provides.

The design of a Spring-based rule engine

We base our design on the interaction of Spring-controlled Java beans, which we call rule engine components. Let's define the two types of components we might need:

  • An action is a component that actually does something useful in our application logic
  • A rule is a component that makes a decision in a logical flow of actions

As we are big fans of good object-oriented design, the following base class captures the base functionality of all of our components to come, namely, the ability to be called by other components with some argument:

 public abstract class AbstractComponent {
   public abstract void execute(Object arg) throws Exception; 

Naturally the base class is abstract because we will never need one by itself.

And now, code for an AbstractAction, to be extended by other future concrete actions:


public abstract class AbstractAction extends AbstractComponent {

private AbstractComponent nextStep; public void execute(Object arg) throws Exception { this.doExecute(arg); if(nextStep != null) nextStep.execute(arg); } protected abstract void doExecute(Object arg) throws Exception;

public void setNextStep(AbstractComponent nextStep) { this.nextStep = nextStep; }

public AbstractComponent getNextStep() { return nextStep; }


As you can see, AbstractAction does two things: It stores the definition of the next component to be invoked by our rule engine. And, in its execute() method, it calls a doExecute() method to be defined by a concrete subclass. After doExecute() returns, the next component is invoked if there is one.

Our AbstractRule is similarly simple:


public abstract class AbstractRule extends AbstractComponent {

private AbstractComponent positiveOutcomeStep; private AbstractComponent negativeOutcomeStep; public void execute(Object arg) throws Exception { boolean outcome = makeDecision(arg); if(outcome) positiveOutcomeStep.execute(arg); else negativeOutcomeStep.execute(arg);


protected abstract boolean makeDecision(Object arg) throws Exception;

// Getters and setters for positiveOutcomeStep and negativeOutcomeStep are omitted for brevity

In its execute() method, the AbstractAction calls the makeDecision() method, which a subclass implements, and then, depending on that method's result, calls one of the components defined as either a positive or negative outcome.

Our design is complete when we introduce this SpringRuleEngine class:

 public class SpringRuleEngine {
   private AbstractComponent firstStep;
   public void setFirstStep(AbstractComponent firstStep) {
      this.firstStep = firstStep;
   public void processRequest(Object arg) throws Exception {

That's all there is to our rule engine's main class: the definition of a first component in our business logic and the method to start processing.

But wait, where is the plumbing that wires all our classes together so they can work? You will next see how the magic of Spring helps us with that task.

Spring-based rule engine in action

Let's look at a concrete example of how this framework might work. Consider this use case: we must develop an application responsible for processing loan applications. We need to satisfy the following requirements:

  • We check the application for completeness and reject it otherwise
  • We check if the application came from an applicant living in a state where we are authorized to do business
  • We check if applicant's monthly income and his/her monthly expenses fit into a ratio we feel comfortable with
  • Incoming applications are stored in a database via a persistence service that we know nothing about, except for its interface (perhaps its development was outsourced to India)
  • Business rules are subject to change, which is why a rule-engine design is required

First, let's design a class representing our loan application:


public class LoanApplication { public static final String INVALID_STATE = "Sorry we are not doing business in your state"; public static final String INVALID_INCOME_EXPENSE_RATIO = "Sorry we cannot provide the loan given this expense/income ratio"; public static final String APPROVED = "Your application has been approved"; public static final String INSUFFICIENT_DATA = "You did not provide enough information on your application"; public static final String INPROGRESS = "in progress"; public static final String[] STATUSES = new String[] { INSUFFICIENT_DATA, INVALID_INCOME_EXPENSE_RATIO, INVALID_STATE, APPROVED, INPROGRESS };

private String firstName; private String lastName; private double income; private double expences; private String stateCode; private String status; public void setStatus(String status) { if(!Arrays.asList(STATUSES).contains(status)) throw new IllegalArgumentException("invalid status:" + status); this.status = status; }

// Bunch of other getters and setters are omitted


Our given persistence service is described by the following interface:

 public interface LoanApplicationPersistenceInterface {
   public void recordApproval(LoanApplication application) throws Exception;
   public void recordRejection(LoanApplication application) throws Exception;
   public void recordIncomplete(LoanApplication application) throws Exception;

We quickly mock this interface by developing a MockLoanApplicationPersistence class that does nothing but satisfy the contract defined by the interface.

We use the following subclass of the SpringRuleEngine class to load the Spring context from an XML file and actually begin the processing:

 public class LoanProcessRuleEngine extends SpringRuleEngine {
   public static final SpringRuleEngine getEngine(String name) {
      ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("SpringRuleEngineContext.xml");
      return (SpringRuleEngine) context.getBean(name);

At this moment, we have the skeleton in place, so it is the perfect time to write a JUnit test, which appears below. A few assumptions are made: We expect our company to operate in only two states, Texas and Michigan. And we only accept loans with an expense/income ratio of 70 percent or better.


public class SpringRuleEngineTest extends TestCase {

public void testSuccessfulFlow() throws Exception { SpringRuleEngine engine = LoanProcessRuleEngine.getEngine("SharkysExpressLoansApplicationProcessor"); LoanApplication application = new LoanApplication(); application.setFirstName("John"); application.setLastName("Doe"); application.setStateCode("TX"); application.setExpences(4500); application.setIncome(7000); engine.processRequest(application); assertEquals(LoanApplication.APPROVED, application.getStatus()); } public void testInvalidState() throws Exception { SpringRuleEngine engine = LoanProcessRuleEngine.getEngine("SharkysExpressLoansApplicationProcessor"); LoanApplication application = new LoanApplication(); application.setFirstName("John"); application.setLastName("Doe"); application.setStateCode("OK"); application.setExpences(4500); application.setIncome(7000); engine.processRequest(application); assertEquals(LoanApplication.INVALID_STATE, application.getStatus()); } public void testInvalidRatio() throws Exception { SpringRuleEngine engine = LoanProcessRuleEngine.getEngine("SharkysExpressLoansApplicationProcessor"); LoanApplication application = new LoanApplication(); application.setFirstName("John"); application.setLastName("Doe"); application.setStateCode("MI"); application.setIncome(7000); application.setExpences(0.80 * 7000); //too high engine.processRequest(application); assertEquals(LoanApplication.INVALID_INCOME_EXPENSE_RATIO, application.getStatus()); } public void testIncompleteApplication() throws Exception { SpringRuleEngine engine = LoanProcessRuleEngine.getEngine("SharkysExpressLoansApplicationProcessor"); LoanApplication application = new LoanApplication(); engine.processRequest(application); assertEquals(LoanApplication.INSUFFICIENT_DATA, application.getStatus()); }

1 2 Page 1
Page 1 of 2