15. The javax.rules API

15.1. Introduction

javax.rules, also known as JSR-94, is a Java runtime api for rule engines. It provides a very simple model of interaction with the rule engine itself, and a somewhat more involved mechanism for administering sets of rules.

The javax.rules API does not define a rule language of its own, but rather just an API for working with vendor-specific rule languages. In theory, you can write a program to the javax.rules API, and use it with different rule engines without modifying or recompiling the Java code. This would be accomplished by changing parameters in a configuration file, and swapping out one vendor-specific rule definition file for another. In practice, the javax.rules API fails to expose many of the interesting features of Jess directly, so this implementation provides a "hook" that will allow you to add Jess-specific code to take advantage of these features.

Finally, note that the reference implementation of javax.rules took the form of a driver for Jess. This driver was defined in the package org.jcp.jsr94.jess and used its own XML "wrapper" format for Jess language code. Jess 7 now includes its own javax.rules driver, defined in the package jess.jsr94. This new driver supports code written in the Jess language itself as well as JessML; the reference implementation's "wrapper" format is not supported.

15.2. Using javax.rules

Using javax.rules to work with Jess is a two-stage process. In the first stage, you create and register a rule execution set, which is basically an instance of jess.Rete with templates, rules, and other necessary code already loaded. In the second stage, you retrieve the rule execution set from the registrar, load Java objects into it, and execute the rules.

15.2.1. Registering a RuleExecutionSet

Our first example shows how to create a rule execution set from a file of Jess code and how to register it with javax.rules. You could use an XML file instead of a Jess file; the code would not change. Exception handling has been left out for brevity. Most of the methods in the javax.rules API throw both a subclass of javax.rules.RuleException and java.rmi.RemoteException.

Typically, a RuleExecutionSet is created once and executed multiple times. The intention behind the javax.rules design is that RuleExecutionSets would be created using an app server's configuration tools, and then used by application code.


import javax.rules.*;
import javax.rules.admin.*;
import java.util.HashMap;
import java.io.FileReader;

public class ExJSR94Register {
  public static final String URI = "rules://test-rules";
  public static final String RULE_SERVICE_PROVIDER = "jess.jsr94";

  public static void main(String[] argv) throws Exception {
    // Load the rule service provider.
    // Loading this class will automatically register this
    // provider with the provider manager.
    Class.forName(RULE_SERVICE_PROVIDER + ".RuleServiceProviderImpl");

    // Get the rule service provider from the provider manager.
    RuleServiceProvider serviceProvider =
        RuleServiceProviderManager.getRuleServiceProvider(RULE_SERVICE_PROVIDER);

    // get the RuleAdministrator
    RuleAdministrator ruleAdministrator =
        serviceProvider.getRuleAdministrator();

    // get an input stream to a ruleset
    FileReader reader = new FileReader("rules.clp");

    // parse the ruleset
    try {
        // "properties" holds vendor-specific information
        // Jess doesn't use this parameter for these methods
        HashMap properties = new HashMap();

        // Create the RuleExecutionSet
        RuleExecutionSet executionSet =
            ruleAdministrator.getLocalRuleExecutionSetProvider(properties).
            createRuleExecutionSet(reader, properties);

        // register the RuleExecutionSet
        ruleAdministrator.registerRuleExecutionSet(URI,
                                                   executionSet, properties);

    } finally {
        reader.close();
    }
  }
}

15.2.2. Using a RuleExecutionSet

Now that we have registered a RuleExecutionSet, we can use it to run the rules. For this example, let's use the following trivial rule file rules.clp:


Jess> (defclass String java.lang.String)

Jess> (defrule foo
=>
(definstance String (new String "Hello, World!") static))

When executed, this rule will fire and add a single String to working memory. Here is the Java code to retrieve and execute the RuleExecutionSet:


import javax.rules.*;
import javax.rules.admin.*;
import java.util.*;

public class ExExecuteJSR94 {
  public static final String URI = "rules://test-rules";
  public static final String RULE_SERVICE_PROVIDER = "jess.jsr94";

  public static void main(String[] argv) throws Exception {
    // Get the rule service provider from the provider manager.
    RuleServiceProvider serviceProvider =
        RuleServiceProviderManager.getRuleServiceProvider(RULE_SERVICE_PROVIDER);

    // Get a RuleRuntime
    RuleRuntime runtime = serviceProvider.getRuleRuntime();

    // Create a StatelessRuleSession
    StatelessRuleSession session = (StatelessRuleSession)
        runtime.createRuleSession(URI, new HashMap(),
                                  RuleRuntime.STATELESS_SESSION_TYPE);

    // Create a input list.
    List input = new ArrayList();

    // Execute the rules
    List results = session.executeRules(input);

    // Release the session.
    session.release();

    // Report the results
    System.out.println(results);
  }
}
C:\> java ExExecuteJSR94

When you run this program, if all goes well it will print "[Hello, World!]," displaying the single object in working memory.

15.2.3. Working with Java Objects

When we called executeRules() in the example above, we passed an empty List as an argument. You can instead pass in a List populated with Java objects. Jess will, for each object:

  1. Determine the name of the class, without the package name.
  2. Ensure that a defclass has been created under that name, or create a new one.
  3. Add that object as a definstance under that class name.

Jess will then execute the rules. You therefore should write your rules assuming that the objects will be filed in working memory under their class name with the package prefix removed. Note that your rule file should contain any defclass statements for objects actually matched by your rules, or the rules won't parse.

In this release, there's no way to control how these defclasses will be placed into modules, and there's no way to express Jess's concept of inheritance. These features will be added in a later release.

15.2.4. Going Further

There's a lot more to the javax.rules API than we've covered here. In particular, besides stateless rule sessions, there are stateful rule sessions that let you add and remove objects over time. Furthermore, there are many different ways to create a RuleExecutionSet. Besides creating one from a local file, they can be created from XML DOM trees and directly from existing jess.Rete objects. Interested readers are directed to the specification itself for more information until more information is added to this manual.