Guideline: JSR94
JSR-94 is an industry standard that defines how Java programs deployed in JSE or JEE can acquire and interact with a rule engine. Being able to change engine implementation is a nice design approach, but as of today this specification is limited by the fact that there is still no standard to exchange rule definition between engines. So rule written for one engine can not be used by another one. W3C is working on the final specification of Rule Interchange Format.
Relationships
Main Description

The goal of this specification aims to make the client code for simple rule-based applications less dependent on rule engine vendor-specific classes. The basic interactions with a rule engine are typically parsing the rules in scope of a rule set, adding object references to one engine, firing the rules and getting results from the engine.
JSR94 defines a rule set as a rule execution set which can be loaded from external resources like URIs, Input streams, XML streams and readers. A rule execution set is a collection of rules. Another important JSR94 concept is the rule session which is a runtime connection between a client and a rule engine. A rule session is associated with a single rule execution set, consumes rule engine resources and must be explicitly released when the client no longer requires it. Session can be stateless or stateful. Stateless executes a rule execution set with a list of input objects in one call. Stateful is designed to maintain a long time conversation between the client and the engine and provides mechanism to assert / retract input object to the session.

The client code may run on server layer like a servlet controller, in a service tier part of an EJB or POJO, or in a standalone JSE JVM. The javax.rules API divides interaction with rule engines into administrative and runtime interactions. The basic operations supported by JSR-94 are:
• Acquiring a rule session for a registered rule execution set.
• Deploying and undeploying rulesets into a rule engine instance
• Querying simple metadata about a ruleset
• Executing a ruleset in either a stateful or stateless mode

The client code for run time execution


From the client point of view the interaction with a rule engine is done using the rule session. But the first part is to get an instance of the rule engine implementation. The service provider manager helps to get a rule service provider which in turn helps to get rule run time and rule administration implementations. Every specific implementation exposes an uniquely identifier for the service provider URL.


// Get the rule service provider from the provider manager
Class.forName("ilog.rules.bres.jsr94.IlrRuleServiceProvider");

RuleServiceProvider serviceProvider = RuleServiceProviderManager.getRuleServiceProvider(“ilog.rules.bres.jsr94”);

• For the reference implementation the URL is org.jsp.jsr94.ri.RuleServiceProvider
• For JRules the is ilog.rules.bres.jsr94.IlrRuleServiceProvider, and the service provider name is ilog.rules.bres.jsr94
• For JBoss-drools the is org.drools.jsr94.rules.RuleServiceProviderImpl and the service provider name is http://drools.org.
The code above is used for JSE deployment, as in JEE environment runtime clients should resolve the RuleRuntime and RuleAdministrator services directly using JNDI lookup.

Javax.naming.InitialContext initialContext = new InitialContext();
RuleRuntime ruleRuntime =
(RuleRuntime) PortableRemoveObject.narrow(
initialContext.lookup("org.jcp.jsr94.ri.RuleRuntime"),RuleRuntime.class );

In JSE we can use an inversion of control pattern and get those URL references from a properties file or a file descriptor.
The next step is to get a rule engine run time.


// Get a RuleRuntime and invoke the rule execution.
RuleRuntime ruleRuntime = serviceProvider.getRuleRuntime();


The RuleRuntime interface exposes the method to create a rule session given a previously registered RuleExecutionSet URI. It is possible to execute the rule engine in stateless or stateful mode using different type of rule session. We need to specify the rule execution set URI and the session type.


StatelessRuleSession statelessRuleSession = (StatelessRuleSession) ruleRuntime.createRuleSession(ruleExecutionSetURI, rulesessionProperties,RuleRuntime.STATELESS_SESSION_TYPE);

The second parameter is optional and is used to add some additional properties to the session. In JRules it is used to give the references to the rule set parameters and to specify if the RuleSession is a J2SE POJO rule session or a J2EE POJO rule session:


Map rulesessionProperties = new HashMap(); rulesessionProperties.put("loan", loan);
rulesessionProperties.put("borrower", borrower);

A stateless rules session exposes a stateless rule execution API to an underlying rules engine with two different methods to call the execution of the rule:


public java.util.List executeRules(java.util.List objects)
throws InvalidRuleSessionException,java.rmi.RemoteException


The list of objects set as parameters will be inserted in the engine's working memory. The list returned includes all the objects created by the executed rules. The only things we can retrieve with JSR94 from an execution are the objects in the working memory. The second API uses a filter of objects the client code can supply to select those objects that should be returned from the rule engine.

Filtering objects

To filter out objects from the list of returned objects from the rule execution call, the client code needs to provide an implementation of the ObjectFilter interface. The implementing class receives callback methods that allow filtering out objects as desired. Here is a simple filter class that removes any loan which does not have messages.


public class MyObjectFilter implements ObjectFilter {

@Override
public Object filter(Object obj) {
if (obj instanceof Loan) {
Loan loan = (Loan)obj;
if (loan.getMessages().size() != 0)
return obj;
}
return null;
}

Get rule execution set meta data


RuleRuntime can also being used to get the list of URIs that currently have rule execution set registered with them using the API List listURIs=ruleRuntime.getRegistrations();
The other object involved is the RuleExecutionSetMetadata interface which exposes metadata about a Rule Execution Set to runtime clients of a RuleSession like the name, URI and description of the rule execution set.


RuleExecutionSetMetadata metadata=statelessRuleSession
.getRuleExecutionSetMetadata();
metadata.getName();
metadata.getDescription();
metadata.getUri()

Stateful session


Client code can use stateful session to conduct long running conversation with the engine, and control the working memory with new facts. Input Objects can be progressively added to the StatefulRuleSession through the addObject method. Output Objects can be progressively retrieved though the getObject method.

StatefulRuleSession statefulRuleSession= (StatefulRuleSession) getRuleRuntime()
.createRuleSession(ruleExecutionSetURI, getProperties(loan, borrower),RuleRuntime.STATEFUL_SESSION_TYPE);
//first call the normal execution
statefulRuleSession.executeRules();
Loan l2 = new Loan(250000,240,7.25);
Handle hdl=statefulRuleSession.addObject(l2);
statefulRuleSession.executeRules();


Objects that have been added to the StatefulRuleSession must be removed and updated using the removeObject and updateObject methods. A client must test for the existence of an added Object using the containsObject method. The removeObject, updateObject, and containsObject methods must all use a javax.rules.Handle implementation instances to refer to and identify Object instances. Handle are used to ensure that Object instances can be unambiguously identified in the event of multiple class loaders being used or the StatefulRuleSession being serialized. The addObject methods returns a Handle instance for an Object added to a StatefulRuleSession, so that it can be used in the remove API for example.
In JRules Ruleset parameters and objects added to the RuleSession when it is created are uniquely identified by an instance of the IlrRuleSessionHandle class.


Administrate rule execution set


Administrative tasks supported by the API javax.rules.admin include instantiating the rule engine and loading rules. To get the rule administrator we use the service provider such as: RuleAdministrator ruleAdministrator = serviceProvider.getRuleAdministrator();

The RuleAdministrator allows RuleExecutionSet instances to be registered against a URI for use from the runtime API, as well as methods to retrieve a RuleExecutionSetProvider and a LocalRuleExecutionSetProvider implementation. The RuleExecutionSetProvider interface defines methods to create a RuleExecutionSet from a number of Serializable sources

LocalRuleExecutionSetProvider ruleExecutionSetProvider = ruleAdministrator.getLocalRuleExecutionSetProvider(null);
RuleExecutionSet ruleSet = ruleExecutionSetProvider.createRuleExecutionSet( inputStream, null );
ruleAdministrator.registerRuleExecutionSet( ruleSet.getName(),ruleSet,null );

The local rule execution set provider sends a local execution set to a remote engine using serialization and marshaling. The API get(Local)RuleExecutionSetProvider takes an argument of type Map, which is documented as "additional properties" and used for setting the JNDI properties. The source for the rule can come from non-Serializable resources, such as binary InputStreams or character-based Readers. Registering the execution set to a URI helps to create session to an execution set. The rules registered using the rules admin API are the only rules accessible to the runtime clients.
The following code gets the name and description of the execution set deployed:


ruleSet.getDescription();
ruleSet.getName();

getName() in the case of JRules Rule Execution server deployment returns the rule set path. From the rules set we can get all the rules in a list and then for each rule its name and description.
Rule.getName()returns in the case of JRules a string which specify the language name and the name space for this rule, like for example “IRL/validation/maximum_amount-brl.irl”. The rule.getDescription() returns the rule in the language of the rule engine vendor. For JRules it is the Ilog rule language such as:


package validation {
    rule maximum_amount {
       property status = "new";
       when {
             miniloan.Loan() from loan;
             evaluate (loan.amount > 1000000);
       } then {
             loan.addToMessages("The loan cannot exceed 1,000,000");
             loan.reject();
       }
    }
}