Red Hat Training

A Red Hat training course is available for JBoss Enterprise SOA Platform

Chapter 20. The Java Rule Engine Application Programming Interface

20.1. JSR94

JSR94 is an API used in JBoss Rules. It allows for support of multiple rule engines from a single API. JSR94 does not deal in any way with the rule language itself.
The JSR94 standard represents the "least common denominator" in features across rule engines. This means that there is less functionality in the JSR94 API than in the standard Knowledge API. By using JSR94, you cannot use all the capabilities of JBoss Rules. You should use globals and support for DRL, DSL and XML, via property maps in conjunction with JSR94. This introduces non-portable functionality.

Note

As JSR94 does not provide a rule language, you are only solving a small fraction of the complexity of switching rule engines with very little gain. Red Hat recommends you program against the Knowledge (JBoss Rules and jBPM) API.

20.2. Javax.rules Interfaces

  • Handle
    The Handle is used to retrieve an Object back from the WorkingMemory which was added in a StatefulRuleSession . With the Handle you can modify or remove an Object from the WorkingMemory. To modify an Object call updateObject() from the StatefulRuleSession. To remove it, call removeObject() with the Handle as the Parameter. Inside of the implementation of the Java Rule Engine API will be called the modifyObject() and retractObject() methods of the encapsulated Knowledge (Drools and jBPM) API.
  • ObjectFilter
    This interface is used to filter objects for RuleSession.
  • RuleExecutionSetMetadata
    The RuleExecutionSetMetadata is used to store name, description and URI for a RuleExecutionSet.
  • RuleRuntime
    The RuleRuntime is the key to a RuleSession. The RuleRuntime obtained from the RuleServiceProvider.
    If you retrieve a RuleRuntime call createRuleSession() to open a RuleSession.
    Through the RuleRuntime you can retrieve a list of URIs of all RuleExecutionSets, which were registered by a RuleAdministrator. You need the URI as a String to open a RuleSession to the rule engine. The rule engine will use the rules of the RuleExecutionSet inside of the RuleSession.
    The Map is used for Globals. Globals were formerly called ApplicationData (in Drools 2.x). The key needs to be the identifier of the Global and the Value the object you want to use as a Global.
  • RuleSession
    The RuleSession is the object you are working with if you want to contact the rule engine.
    If you are getting a RuleSession from the RuleRuntime, then it will be either a StatefulRuleSession or a StatelessRuleSession.
    Call the release()-method so that all resources will be freed.
  • StatefulRuleSession
    If you need to run the rule engine more than once, run a StatefulRuleSession. You can assert objects, execute rules and so on.
    You will get a Handle for every object which you are asserting to the Rule Session. Do not lose it, you will need it, to retract or modify objects in the Working Memory. You are having no direct contact to Drools´ Working Memory which is used inside the implementation, for this you got the RuleSession.
  • StatelessRuleSession
    A StatelessRuleSession means that you are having only one contact to the rule engine. You are giving a list of objects to the rule engine and the rule engine asserts them all and starts execution immediately. The result is a list of objects. The content of the result list depends on your rules. If your rules are not modifying or retracting any objects from the Working Memory, you should get all objects you re-added.
    You can use the ObjectFilter which will filter the resulting list of objects before you get it.

20.3. Javax.rules Classes

  • RuleServiceProvider
    The RuleServiceProvider gives you access to the RuleAdministrator or a RuleRuntime, which you need to open a new Rule Session. To get the RuleServiceProvider call RuleServiceProviderManager.getRuleServiceProvider().
    In a J2EE environment you can bind the RuleServiceProvider to the JNDI and create a lookup to place it in all your applications.
  • RuleServiceProviderManager
    The RuleServiceProvider is often compared with the DriverManager, which you use in JDBC. It works like setting up the Driver for a DataBase.

20.4. Javax.rules Exceptions

  • ConfigurationException
    This exception is thrown when a user configuration error has been made.
  • InvalidHandleException
    This exception is thrown when a client passes an invalid Handle to the rule engine.
  • InvalidRuleSessionException
    The InvalidRuleSessionException should be thrown when a method is invoked on a RuleSession and the internal state of the RuleSession is invalid. This may have occurred because a StatefulRuleSession has been serialized and external resources can no longer be accessed. This exception is also used to signal that a RuleSession is in an invalid state (such as an attempt to use it after the release method has been called) (Taken from JCP API Documentation).
  • RuleException
    Base class for all Exception classes in the javax.rules package.
  • RuleExecutionException
    This exception is not thrown in the Drools 3 JSR 94 implementation
  • RuleExecutionSetNotFoundException
    This exception is thrown if a client requests a RuleExecutionSet from the RuleRuntime and the URI or RuleExecutionSet cannot be found (Taken from JCP API Documentation).
  • RuleSessionCreateException
    This exception is thrown when a client requests a RuleSession from the RuleRuntime and an error occurs that prevents a RuleSession from being returned (Taken from JCP API Documentation).
  • RuleSessionTypeUnsupportedException
    This exception is thrown when a client requests a RuleSession and the vendor does not support the given type (defined in the RuleRuntime) or the RuleExecutionSet itself does not support the requested mode (Taken from JCP API Documentation).

20.5. Using a Rule Service Provider

Procedure 20.1. Task

  1. Use the following code to load the JBoss Rules rule service provider:
    Class ruleServiceProviderClass = Class.forName("org.drools.jsr94.rules.RuleServiceProviderImpl");
  2. Use the following code to register it:
    RuleServiceProviderManager.registerRuleServiceProvider( "http://jboss.com/products/rules", ruleServiceProviderClass);
  3. Call to the RuleServiceProvider using the following code:
    RuleServiceProviderManager.getRuleServiceProvider("http://jboss.com/products/rules");
  4. To stop the rule service, deregister it with this code:
    RuleServiceProviderManager.deregisterRuleServiceProvider( "http://jboss.com/products/rules");

20.6. Javax.rules.admin Interfaces

  • LocalRuleExecutionSetProvider
  • Rule
  • RuleAdministrator
  • RuleExecutionSet
  • RuleExecutionSetProvider

20.7. Javax.rules.admin Exceptions

  • RuleAdministrationException
    Base class for all administration RuleException classes in the javax.rules.admin package (Taken from JCP API Documentation).
  • RuleExecutionSetCreateException
    Occurs when there is an error in creating a rule execution set.
  • RuleExecutionSetDeregistrationException
    Occurs if there is an error upon attempting to unregister a rule execution set from a URI.
  • RuleExecutionSetRegisterException
    Occurs if there is an error upon attempting to register a rule execution set to a URI.

20.8. The RuleServiceProvider

The RuleServiceProvider provides access to the RuleRuntime and RuleAdministrator APIs. The RuleAdministrator provides an administration API for the management of RuleExecutionSet objects, making it possible to register a RuleExecutionSet that can then be retrieved via the RuleRuntime.

20.9. The RuleServiceProviderManager

The RuleServiceProviderManager manages the registration and retrieval of RuleServiceProviders. The JBossRules RuleServiceProvider implementation is automatically registered via a static block when the class is loaded using Class.forName, in much the same way as JDBC drivers.

20.10. Automatic RuleServiceProvider Registration Example

This is an example of registering the automatic RuleServiceProvider:
// RuleServiceProviderImpl is registered to "http://drools.org/"
// via a static initialization block
Class.forName("org.drools.jsr94.rules.RuleServiceProviderImpl");

// Get the rule service provider from the provider manager.
RuleServiceProvider ruleServiceProvider =
  RuleServiceProviderManager.getRuleServiceProvider("http://drools.org/");

20.11. Registering a LocalRuleExecutionSet with the RuleAdministrator API

Procedure 20.2. Task

  1. Create a RuleExecutionSet. You can do so by using the RuleAdministrator which provides factory methods to return an empty LocalRuleExecutionSetProvider or RuleExecutionSetProvider.
  2. Specify the name for the RuleExecutionSet.
  3. Register the RuleExecutionSet as shown below:
    // Register the RuleExecutionSet with the RuleAdministrator
    String uri = ruleExecutionSet.getName();
    ruleAdministrator.registerRuleExecutionSet(uri, ruleExecutionSet, null);
  4. Use the LocalRuleExecutionSetProvider to load a RuleExecutionSets from local sources that are not serializable, like Streams.
  5. Use the RuleExecutionSetProvider to load RuleExecutionSets from serializable sources, like DOM Elements or Packages. Both the "ruleAdministrator.getLocalRuleExecutionSetProvider( null );" and the "ruleAdministrator.getRuleExecutionSetProvider( null );" (use null as a parameter).
  6. The example below shoes you how to register the LocalRuleExecutionSet:
    // Get the RuleAdministration
    RuleAdministrator ruleAdministrator = ruleServiceProvider.getRuleAdministrator();
    LocalRuleExecutionSetProvider ruleExecutionSetProvider =
      ruleAdministrator.getLocalRuleExecutionSetProvider( null );
    
    // Create a Reader for the drl
    URL drlUrl = new URL("http://mydomain.org/sources/myrules.drl");
    Reader drlReader = new InputStreamReader(  drlUrl.openStream()  );
    
    // Create the RuleExecutionSet for the drl
    RuleExecutionSet ruleExecutionSet =
      ruleExecutionSetProvider.createRuleExecutionSet( drlReader, null );
    
  7. You can use the "ruleExecutionSetProvider.createRuleExecutionSet( reader, null )" property to provide configuration for the incoming source. When null is passed the default is used to load the input as a drl. Allowed keys for a map are "source" and "dsl". The key "source" takes "drl" or "xml" as its value.
  8. Set "source" to "drl" to load a DRL, or to "xml" to load an XML source. "xml" will ignore any "dsl" key/value settings. The "dsl" key can take a Reader or a String (the contents of the dsl) as a value. See the following dsl example:
    // Get the RuleAdministration
    RuleAdministration ruleAdministrator = ruleServiceProvider.getRuleAdministrator();
    LocalRuleExecutionSetProvider ruleExecutionSetProvider =
      ruleAdministrator.getLocalRuleExecutionSetProvider( null );
    
    // Create a Reader for the drl
    URL drlUrl = new URL("http://mydomain.org/sources/myrules.drl");
    Reader drlReader = new InputStreamReader(  drlUrl.openStream()  );
    
    // Create a Reader for the dsl and a put in the properties map
    URL dslUrl = new URL("http://mydomain.org/sources/myrules.dsl");
    Reader dslReader = new InputStreamReader( dslUrl.openStream()  );
    Map properties = new HashMap();
    properties.put( "source", "drl" );
    properties.put( "dsl", dslReader );
    
    // Create the RuleExecutionSet for the drl and dsl
    RuleExecutionSet ruleExecutionSet =
      ruleExecutionSetProvider.createRuleExecutionSet( reader, properties );
    

20.12. Using Stateful and Stateless RuleSessions

Procedure 20.3. Task

  1. Get the runtime by accessing the RuleServiceProvider as shown:
    RuleRuntime ruleRuntime = ruleServiceProvider.getRuleRuntime();
  2. To create a rule session, use one of the two RuleRuntime public constants. These are "RuleRuntime.STATEFUL_SESSION_TYPE" and "RuleRuntime.STATELESS_SESSION_TYPE", accompanying the URI to the RuleExecutionSet you wish to instantiate a RuleSession for.
  3. Optionally, access the properties to specify globals.
  4. The createRuleSession(...) method will return a RuleSession instance. You should cast it to StatefulRuleSession or StatelessRuleSession:
    (StatefulRuleSession) session =
      ruleRuntime.createRuleSession( uri,
                                     null,
                                     RuleRuntime.STATEFUL_SESSION_TYPE );
    session.addObject( new PurchaseOrder( "cheese" ) );
    session.executeRules();
  5. When using a StatelessRuleSession, you can only call executeRules(List list) passing a list of objects, and an optional filter, the resulting objects are then returned:
    (StatelessRuleSession) session =
      ruleRuntime.createRuleSession( uri,
                                     null,
                                     RuleRuntime.STATELESS_SESSION_TYPE );
    List list = new ArrayList();
    list.add( new PurchaseOrder( "even more cheese" ) );
    
    List results = new ArrayList();
    results = session.executeRules( list );

20.13. Using Globals with JSR94

JSR94 supports globals (in a manner that is not portable) by using the properties map passed to the RuleSession factory method. Globals must be defined in the DRL or XML file first, otherwise an exception will be thrown. The key represents the identifier declared in the DRL or XML, and the value is the instance you wish to be used in the execution.

20.14. Using Globals with JSR94 Example

Here is an example of implementing a global in JSR94:
java.util.List globalList = new java.util.ArrayList( );
java.util.Map map = new java.util.HashMap( );
map.put( "list", globalList ); 
//Open a stateless Session
StatelessRuleSession srs =
  (StatelessRuleSession) runtime.createRuleSession( "SistersRules",
                                                    map,
                                                    RuleRuntime.STATELESS_SESSION_TYPE );
...
// Persons added to List
// call executeRules( ) giving a List of Objects as parameter
// There are rules which will put Objects in the List
// fetch the list from the map
List list = (java.util.List) map.get("list");
Do not forget to declare the global "list" in your DRL:
package SistersRules; 
import org.drools.jsr94.rules.Person; 
global java.util.List list
rule FindSisters 
when 
    $person1 : Person ( $name1:name ) 
    $person2 : Person ( $name2:name ) 
    eval( $person1.hasSister($person2) ) 
then 
    list.add($person1.getName() + " and " + $person2.getName() +" are sisters"); 
    assert( $person1.getName() + " and " + $person2.getName() +" are sisters"); 
end

20.15. Further Reading About JSR94

If you need more information on JSR94, please refer to the following links:
  1. Official JCP Specification for Java Rule Engine API (JSR 94)
  2. The Java Rule Engine API documentation
  3. The Logic From The Bottom Line: An Introduction to The Drools Project. By N. Alex Rupp, published on TheServiceSide.com in 2004
  4. Getting Started With the Java Rule Engine API (JSR 94): Toward Rule-Based Applications. By Dr. Qusay H. Mahmoud, published on Sun Developer Network in 2005
  5. Jess and the javax.rules API. By Ernest Friedman-Hill, published on TheServerSide.com in 2003