Java Development News:

Jess and the javax.rules API

By Ernest Friedman-Hill

01 Oct 2003 | TheServerSide.com

Introduction

Ernest Friedman-Hill is also the author of Jess In Action (by Manning Publicaions). Jess in Action introduces rule programming concepts and teaches you the Jess language. It progresses through a series of fully-developed applications chosen to expose you to practical rule-based development and shows you how you can add power and intelligence to your Java software. You can obtain a copy of the book from Amazon.com or the Manning site.

Jess is a rule engine and scripting environment for the Java platform. JSR 94 is a developing Java Community Process standard for interfacing rule engines to Java software. The reference implementation of JSR 94 is a driver for Jess; with it, you can connect Jess to Java software using the vendor-independent JSR 94 API. In this article, based on an excerpt from the new book "Jess in Action" from Manning Publications (http://www.manning.com/friedman-hill,) you'll see how to apply this new API to access Jess from your J2EE or J2SE applications.


JSR 94: the javax.rules API

Rule-based programming is a declarative programming technique in which you write heuristic rules rather than procedural instructions. A software module called a rule engine decides how to apply the rules to your data. Rule-based programming is widely used in the insurance and financial services industries, among others, and is especially useful whenever complex criteria must be applied to large amounts of information.

Rule engines can be used together with various commercial off-the-shelf products, including application servers. Historically, there has been a certain amount of vendor lock-in in choosing a rule engine, because each rule engine has its own programmer's interface.

The Java Rule Engine API (http://www.jcp.org/jsr/detail/94.jsp), defined by the javax.rules package, is a standard enterprise API for accessing rule engines, currently being developed by a consortium of rule engine vendors under the Java Community Process. The javax.rules package will allow the host program to interact generically with multiple rule engines, the same way the Java Database Connectivity (JDBC) API makes it possible to write vendor-neutral database programs. The API will include mechanisms for creating and managing sets of rules; for adding, removing, and modifying objects in working memory; and for initializing, resetting, and running the engine.

The javax.rules package can be used in both J2SE and J2EE applications. It includes mechanisms for storing and managing rule bases as well as classes that represent individual sessions with a rule engine. Using the javax.rules package, you can write Java code to work with rule engines in a generic manner, so that changing from one rule engine to another won't necessitate changes to your Java code. The name JSR 94 refers to the standards committee that developed this new API.

Implementations of JSR 94 intended for use with J2EE containers deal with the programming restrictions imposed by that environment. For example, some JSR 94 implementations will be implemented on top of an RMI-based rule server like the one developed in Chapter 21 of "Jess in Action." Other JSR 94 implementations will be targeted toward high performance in J2SE applications, and some will switch between the two.

At the time of this writing, the JSR 94 specification is being readied for public release, and one of the implementations, which is the reference implementation, is a driver for Jess. It is possible that some of what I'll present here will change, but most of this material will still be valid when the javax.rules API becomes official.


Working with javax.rules

The javax.rules API divides interaction with rule engines into administrative and runtime interactions. Administrative tasks include instantiating the rule engine and loading rules, and runtime tasks include manipulating working memory and executing rules. If you use javax.rules from a J2SE program, you'll probably need to perform all of these tasks from your code. On the other hand, in the J2EE environment, the administrative tasks are part of application deployment. Eventually, javax.rules-compliant application servers should offer dedicated mechanisms - GUIs - for installing a rule engine and preparing rule sessions, just as they currently include tools for plugging in databases and other resources. The JSR 94 reference implementation includes a JCA connector that makes a RuleServiceProvider accessible through JNDI.


Setting things up

The administrative phase begins with finding an appropriate javax.rules.RuleServiceProvider object, which gives the programmer access to the rest of the javax.rules implementation. In a J2EE environment, you may be able to retrieve the RuleServiceProvider using JNDI. Otherwise, you can get one from the javax.rules.RuleServiceProviderManager class:

 String implName = "org.jcp.jsr94.ri.RuleServiceProvider"; Class.forName(implName); RuleServiceProvider serviceProvider = RuleServiceProviderManager.getRuleServiceProvider(implName);


This is a little like loading a JDBC driver - it's a similar concept, in that both provide access to an external resource, and you need to do some general initialization before you instantiate any of the classes.

Once you have the RuleServiceProvider, you can retrieve a javax.rules.admin.RuleAdministrator. From the RuleAdministrator, you can get a RuleExecutionSetProvider, which, as the name suggests, creates javax.rules.RuleExecutionSets. A RuleExecutionSet is basically a loaded set of rules, ready to be executed.

The javax.rules.admin package includes two different RuleExecutionSetProvider classes. RuleExecutionSetProvider itself includes methods for creating RuleExecutionSets from serializable objects, and therefore can be used when the rule engine is located in a remote server; the constructor arguments can be sent via RMI. Another class called LocalRuleExecutionSetProvider includes additional methods to create RuleExecutionSets from nonserializable resources like java.io.Reader objects (local files). Given a RuleServiceProvider, you can create a RuleExecutionSet from a local file rules.xml like this:

 RuleAdministrator admin = serviceProvider.getRuleAdministrator(); HashMap properties = new HashMap(); properties.put("name", "My Rules"); properties.put("description", "A trivial rulebase"); FileReader reader = new FileReader("rules.xml"); RuleExecutionSet ruleSet = null; try { LocalRuleExecutionSetProvider lresp = admin.getLocalRuleExecutionSetProvider(properties); ruleSet = lresp.createRuleExecutionSet(reader, properties); } finally { reader.close(); }


You then register the RuleExecutionSet with the RuleAdministrator under a well-known name. At runtime, you use the same name to create a RuleSession; the RuleSession uses the named RuleExecutionSet. This example uses the well-known name rules:

 admin.registerRuleExecutionSet("rules", ruleSet, properties);


Executing the rule engine

In the runtime phase, you create a RuleSession. A RuleSession is basically an instance of a rule engine with a specific set of rules loaded into it. You get RuleSessions from the javax.rules.RuleRuntime object, and you get the RuleRuntime, in turn, from the RuleServiceProvider.

There are two kinds of RuleSessions: stateful and stateless. They differ in the capabilities they offer. The working memory of a StatefulRuleSession persists between method calls. You can add objects to working memory in a series of invocations, then run the engine, and then add more objects and run again. In contrast, a StatelessRuleSession is a one-shot affair: To call its executeRules methods, you must supply the entire initial contents of working memory, and you will receive a list of the final contents as a return value.

Here you create a StatefulRuleSession, add two objects (an Integer and a String) to working memory, execute the rules, and then retrieve the entire contents of working memory as a java.util.List. Finally, you dispose of the RuleSession by calling release:

 RuleRuntime runtime = rsp.getRuleRuntime(); StatefulRuleSession session = (StatefulRuleSession) runtime.createRuleSession("rules", properties, RuleRuntime.STATEFUL_SESSION_TYPE); session.addObject(new Integer(1)); session.addObject("A string"); session.executeRules(); List results = session.getObjects(); session.release();


The javax.rules API is clearly designed to be used in a managed environment like the J2EE. For a simple application like the one we're imagining here, it could be overkill. Consider that by using Jess's native API, all the code in this section and the preceding one can be expressed as follows (assuming the appropriate defclasses are defined in rules.clp):

 Rete session = new Rete(); session.executeCommand("(batch rules.clp)"); session.definstance("java.lang.Integer", new Integer(1), false); session.definstance("java.lang.String", "A string", false); session.run(); Iterator results = engine.listDefinstances();


On the other hand, both in the J2EE environment and in standalone applications, javax.rules gives you some measure of vendor independence.


The Reference Implementation

The first implementation of javax.rules is the reference implementation (RI) included as part of the specification. This RI is, as I mentioned earlier, a wrapper for Jess. Jess is a rule engine and scripting environment written in Java. You can learn more about Jess (and download a trial version) from http://herzberg.ca.sandia.gov/jess.

The RI is an interesting case study, because the mapping of the javax.rules API forces a specific interpretation onto Jess's more general facilities. Here I'm not concerned with describing how it is implemented, but simply with filling in the missing information that allows you to use it.

javax.rules is not a standard rule language; it says nothing about rule languages. Other groups are working on developing standardized rule languages, although less consensus exists in this area. For the same reason there is no one standard general programming language, it is likely that vendor-specific rule languages will be with us for a long time. Each rule language has its own strengths and weaknesses, and the expressiveness, elegance, and power of a rule language can be a major factor in choosing a rule engine. Therefore, if you change rule engine vendors, you will probably have to rewrite your rules, even if you don't need to change your Java code. In this section, you'll see how to write Jess code that fits into the javax.rules framework.

Generally, the rules in a rule engine can operate on Java objects. Those Java objects that have been made available to the rules are known as working memory elements. Working memory elements in a RuleSession are arbitrary Java objects. Because Jess doesn't let you add arbitrary objects to working memory without providing extra information in the form of a definstance call, the RI has to make some assumptions and provide the extra information. Every time an object is added to working memory, Jess automatically defines a defclass for it; the defclass name is the full class name of the object: com.acme.Employee, for example. Therefore, a Jess program meant for use within the JSR 94 RI (here, a trivial rule to give an employee a promotion after three years) might look like this:

 (defclass com.acme.Employee com.acme.Employee nil) (defrule promote-programmer ?e <- (com.acme.Employee (rank "Programmer I") (service ?s&:(> ?s 3))) => (modify ?e (rank "Programmer II")))


Working memory elements that are not JavaBeans call for a different programming style, compared to normal Jess. You can use individual patterns to bind objects, as is the case here, but most tests will be fairly verbose and so should be expressed as test conditional elements, as in the following example:

 (defrule find-number-3 (java.lang.Integer (OBJECT ?i1)) (test (eq 3 (?i1 intValue))) => ;; Found 3, do something...


One other thing about the RI is special: The input files must be XML, rather than normal Jess input files. The format is exceedingly simple. Each XML file must contain a rule-execution-set element, which contains three children: a name (the name of the set of rules); a description (a brief summary of the rule's purpose); and a code element, which contains Jess code. Note that the Jess code has to use normal XML encoding, so that special characters like & and < must be represented using their escape characters. Here's a rule base containing the single add-two-integers rule:


Finally, note that the RI is by definition a strictly conforming implementation of the javax.rules API. As such, there are no hooks to give you access to queries, store/fetch, or other Jess features that go beyond the lowest common denominator of what a rule engine offers. The javax.rules API doesn't provide a specific escape mechanism by which vendors can expose nonstandard interfaces, although many of its methods accept property lists as a customization mechanism. Jess is more than just a rule engine, so the javax.rules interface limits what you can do with Jess. When the javax.rules API is finalized, Jess will offer native support for it, including extensions to allow access to these features and more.


Summary

The javax.rules API, which as of this writing is still being developed, promises to provide portable, generic access to rule engines from multiple vendors. The API includes sophisticated features for managing rule bases in the J2EE environment, but is still fairly simple to use in runtime components. The javax.rules API provides one way to integrate the Jess rule engine into enterprise applications. Although the javax.rules API is not without its flaws, it represents a powerful capability and will be a welcome addition to the Java toolbox.


About the Author

Dr. Friedman-Hill is a Principal Member of the Technical Staff at Sandia National Laboratories in Livermore, California. He is the developer of Jess, the Java rule engine. He has taught Java programming to over 3,000 students for the University of California Extensions in Berkeley, in San Diego, and online. Dr. Friedman-Hill's current work at Sandia includes the development of software for mechanical design and analysis. He lives in Gaithersburg, MD.

Ernest Friedman-Hill is also the author of Jess In Action (by Manning Publications). Jess in Action introduces rule programming concepts and teaches you the Jess language. It progresses through a series of fully-developed applications chosen to expose you to practical rule-based development and shows you how you can add power and intelligence to your Java software. You can obtain a copy of the book from Amazon.com or the Manning site.