Business process automation made easy with Java, Part 2

Design options for rule engine integration

The key benefits of abstracting a rule engine tier from the business object layer are: independent rules maintenance by business users, rules reuse across business objects, and reuse across multiple business contexts by multiple systems. This abstraction is a two-step process that involves:

  • Integrating the external engine with a business tier
  • Selecting the appropriate deployment method and communication protocols between them

In Part 1, we looked at the first step. In this article, we study the second step. We present different methods for rule engine deployment (e.g., stateful versus stateless) and determine the best options for specific situations. In Part 1, we introduced the Java Rule Engine API (Java Specification Request (JSR) 94) that provides a uniform mechanism to access rule engine services. Now we present emerging data interchange standards such as the Simple Rule Markup Language (SRML) that allow vendor independence in rules development.

Read the whole series, "Business Process Automation Made Easy with Java:"

Rule engine integration design options

In this section, we discuss two main options to integrate a rule engine product with a Model-View-Controller (MVC)-based J2EE (Java 2 Platform, Enterprise Edition) Web application. The integration options are based on the engine's invocation mechanism—synchronous or asynchronous. The view's main requirement is synchronization with the model so that data displayed to the user is up to date. Based on the Observer pattern, the model acts as the subject, and the view acts as the observer. Synchronization can be achieved by using a push mechanism, where the view listens for change notification in the model, or a pull mechanism, where the view calls the model to get updated data whenever required, as in a Struts framework.

Figure 1 shows the proposed design for the two integration options. The integration tier is abstracted in this figure to the rule engine controller, and the adapter as explained in Part 1. The pattern's main advantage is loose coupling between the application modules and the rule engine technology used in the back end.

Figure 1. Rule engine integration architecture. Click on thumbnail to view full-size image.

Synchronous invocations

As explained earlier, using the suggested MVC design implies synchronous event execution from the Web tier. As shown in Figure 1, the EJBAction component implements the business logic that may or may not require rules execution. Therefore, in case the need for rules execution arises, EJBAction invokes the RulesController for that purpose. As a result of invoking the RulesController, a rule session object is created and initialized with the required objects (facts) from the model that are needed by the rules set to execute. The initialization objects are obtained by calling the corresponding EJBs (business components) and usually represent a snapshot of the data each EJB represents at the time of invocation. The session object is sent to the rules adapter that abstracts the proprietary Java Rule Engine API from the controller implementation. The adapter extracts the facts from the session, initializes the rule engine with them, then loads the requested rules set from a cache (compiled rules set), and fires the rules.

As a result of firing rules, more facts may be created, and some facts may be altered. The adapter extracts the resulting facts from the rule engine's working memory and puts them back into the session object. The session object returns to the RulesController. The RulesController extracts the facts from the session as a result of rules execution, and then attempts to either update the model components (EJBs) with the altered facts or sends the results back to the Web tier so they can display on the screen as a side effect of rules execution. The decision is based solely on the nature of rules and the application's business logic. For example, server-side validation rules cause the controller to return the results of rules execution as a validation message to the Web tier; meanwhile, action-based rules may cause the controller to update the data in one or more EJBs.

Asynchronous invocations

Some applications require rule engine invocation in asynchronous mode. As shown in the previous section, given the events cycle explained in MVC, asynchronous behavior is not achieved with that design. Messaging middleware, such as Java Message Service (JMS), is recommended for asynchronous rule engine invocation. As shown in Figure 1, the gray components allow the application to communicate asynchronously with the rule engine.

Two JMS queues (or one queue and one topic) can be used as rule engine input queue and rule engine output queue, respectively. The EJBAction detects the need to invoke the rule engine service in an asynchronous fashion and consequently invokes the RequestSender component to accomplish that task. The RequestSender maps the data sent by the EJBAction (mostly optional fact objects and a hint about the rule set to be executed) into an XML message that represents the request to the rule engine. The RequestSender can use a protocol repository to create the XML message; it then puts the message on the rule engine input queue.

A message bean (RulesMBean) listens to the request on the input queue and gets triggered when a request arrives. The RulesMBean extracts the optional facts and the mandatory rules set (or business rule name), and, based on this information, decides which method to call in the rule engine controller. The controller invokes the adapter as mentioned previously and updates the model as a result of firing rules. The controller might decide to send back a response to the message originator; in that case, it would create a response XML message using the ResponseSender component. The ResponseSender puts a message on the rule engine output queue or topic. The ApplicationMBean that listens on the output queue can pick up the response message. The following code snippets illustrate this idea:

public class RulesEngineRequest
      private Document request;
      private String rulesetName;
      private HashMap properties;
      private String id;
      private String replyTo;
      public RulesEngineRequest(String xmlString) throws RulesEngineException
            } catch (Exception ex)
            throw new RulesEngineException("Could not parse the request due to the following exception:\n"+ex);
public class RulesEngineResponse
      private Document response;
      public RulesEngineResponse(IDocument doc) throws RulesEngineException
                  response.addElement("/rules-response", "result-descriptor");
                  response.addElement("/rules-response/result-descriptor", "outcomes");
            } catch(Exception ex)
              throw new RulesEngineException("Could not create RulesEngineResponse due to the following exception:\n"+ex);

... public class RulesMBean implements MessageDrivenBean, MessageListener { ... public void onMessage(Message msg) { TextMessage tm = (TextMessage) msg; try { String text = tm.getText(); RulesEngineRequest request=new RulesEngineRequest(text); String rulesetName=request.getRulesetName(); Map properties=request.getProperties(); ... RulesEngineControllerLocal rulesEngineController=CoreEJBUtil.getRulesEngineControllerLocalHome().create(new HashMap()); rulesEngineController.addFacts(factsList); rulesEngineController.fireRules(rulesetName); factsList=rulesEngineController.getFacts(); ...

Note: Download the complete source code that accompanies this article.

Applying the options

Clearly, business requirements mostly drive invocation method selection; but, from a performance perspective, asynchronous invocation can be more desirable because backend resources can serve it better, and the client can do other tasks during processing. In a credit card authorization scenario, for example, asynchronous invocation might not be an option, but it might be an option for risk underwriting situations. However, the RulesController has no knowledge about the invocation method, and therefore, the same rule set may be fired in two different ways depending on the scenario.

Design considerations

In this section, we discuss some design considerations regarding the suggested integration design.

Data synchronization

One major performance issue with the suggested integration design is the need to synchronize data back to the model components after a rules engine invocation. In a J2EE-distributed environment, the model components can deploy in more than one application server and usually on different JVMs. There is no guarantee that the update process for model components (explained earlier) is done locally, but it usually happens over the network using RMI-IIOP (Remote Method Invocation-Internet Inter-ORB Protocol). Two main approaches overcome this problem:

The first option is to collocate the business components most affected by the rule engine rules-firing on the same JVM as the rule engine. The use of EJB 2.0 local interfaces is most efficient for that purpose. The other approach is to optimize the update cycle by designing the fact objects to implement a Dirty Marker pattern that allows the controller to figure out which object has been altered by the rule engine and which has not. The controller inspects the dirty indicator in all the objects and sends the dirty ones to the corresponding EJB to be stored. It is also worth noting that some rule engine products can deal with EJBs directly as facts in the working memory and might consequently help reduce synchronization overhead. In that case, network overhead of setting and getting granular data members rather than the use of bulk updates as suggested by the proposed design has to be considered.

Workflow engine integration

In certain instances, rule engines are used to make complex decisions about business process routing through business rules execution. In situations like that, we recommend abstracting the rules-firing results in decision objects that can be mapped to XML for easy integration with workflow engines. As an example, let's assume that during a credit card application process the application may route to an underwriter for manual decision-making based on certain data in the applicant profile. In this case, a rule engine would invoke based on data input by the applicant. The rules-firing results would be abstracted into decision objects such as Decline, Approve, or Transfer. Within each decision object, some attributes can be stored, attributes that describe the context of the decision, such as the application identifier or the applicant identifier. The decision objects can be prioritized based on a precedence rule that would, for example, merge all decisions into just one file decision (an Approve with Decline is obviously a Decline). Then the decision can be mapped to XML and parsed by the workflow engine to take appropriate workflow action based on it (e.g., route a task to someone's work queue).

Scalability and quality-of-service (QoS) considerations

Scalability is a concern for most J2EE rule engine implementations. One of the main decisions involves whether to deploy the engine on the same JVM as the rest of the application modules or to deploy it as a standalone server. We recommend embedding the engine in stateless session beans to minimize state overhead, particularly during rules execution. The rules context should never transfer to the caller over RMI to avoid serialization overhead.

Other aspects of scalability such as memory utilization per engine instance and CPU utilization, which are vendor/technology dependent, are outside the scope of this article. Always execute an engine deployment-mode performance test in a controlled test environment to avoid surprises.

Given the nature of embedded components, the rule engine can take advantage of QoS options offered by the application server platform that houses it. Most enterprise-grade application servers such as IBM WebSphere or BEA WebLogic offer optimum deployment and configuration options for EJBs in meeting QoS objectives of performance, reliability, availability, and failover. For example, very high scalability can be achieved by deploying a rule engine as a stateless session bean, as stateless beans have minimal creation overhead, and an enterprise-grade server can spawn them quickly. Other QoS options offered by some vendors include session failover whereby a critical decision component can be automatically failed-over to an active replica during server failures. In order to leverage failover, however, the session bean must deploy as stateful. The embedded approach also enables independent scaling of the rule engine platform for optimum performance.

1 2 Page 1
Page 1 of 2