Introducing Jess


The Java Expert System Shell, or Jess, is a Java implementation of the popular CLIPS expert system shell. CLIPS, the C Language Integrated Production System, is a public domain software tool for developing and delivering rule-based systems.

NASA initially developed the CLIPS environment in the mid-1980s to provide NASA software engineers with a portable, high-performance implementation of an expert system shell. The CLIPS language was modeled on OPS5, one of the leading expert system languages. The OPS5 language was originally developed by Charles Forgy at Carnegie-Mellon University, and the language continues to be synonymous with expert system projects.

Jess is a Java implementation of CLIPS and is to CLIPS what Jython is to Python. Although CLIPS can integrate with Java through a well-defined API, Jess provides the same level of seamless integration with Java as was witnessed with Jython. Jess is compliant with CLIPS to the extent that software written for Jess will run largely unaltered under CLIPS.

Jython is covered in Chapter 9


Dr. Ernest J. Friedman-Hill at Sandia National Laboratory created Jess to support the development of expert systems in Java. Since its inception, many have realized the potential of Jess for adding the power of the rule engine to any Java application. This interest has gone well beyond the scope of expert system development, and Jess is being used effectively in enterprise systems.

In addition to being rule-based, Jess is a general-purpose programming language and provides the developer with a dynamic scripting language with which to access Java class libraries.

Installing Jess

Unlike its predecessor CLIPS, Jess is not public domain software. However, a trial version is available for evaluation purposes, and those in academia may apply for a free license.

Jess can be downloaded from http://herzberg.ca.sandia.gov/jess/index.shtml.

The documentation provided with the installation is comprehensive and includes a reference guide for the Jess language and the Java API along with example code.

Note

If you have obtained a trial version of Jess, it will expire after 30 days, so don't leave it too long before you start your research.


Jess Example

To help understand how Jess defines rules, we look at a simple example of a Jess program and then examine each section of the code in turn. Listing 10-1 implements one of the business rules listed earlier, which states that jet aircraft are not allowed to land on grass runways.

Listing 10-1. airplane.clp
 ; Fact templates ; (deftemplate airplane (slot name)) (deftemplate jet extends airplane) (deftemplate prop extends airplane) ; Rules ; (defrule can-use-grass-runway   "Planes that can use a grass runway"   (prop (name ?n))   =>   (printout t "Aircraft can use grass - " ?n crlf)) (defrule can-use-asphalt-runway   "Planes that can use asphalt"   (airplane (name ?n))   =>   (printout t "Aircraft can use asphalt - " ?n crlf)) ; Add some facts ; (deffacts aircraft-facts "Aircraft type facts"   (prop (name "Cessna 172"))   (prop (name "Tiger Moth"))   (jet (name "Boeing 737"))) ; Start the rule engine ; (reset) (run) 

The airplane.clp example is run directly from the command line:

 java cp jess.jar jess.Main airplane.clp 

Running the code for the example displays the types of runway surface an aircraft can use.

 Jess, the Java Expert System Shell Copyright (C) 2001 E.J. Friedman Hill and the Sandia Corporation Jess Version 6.1p6 11/21/2003 Aircraft can use asphalt - Boeing 737 Aircraft can use asphalt - Tiger Moth Aircraft can use grass - Tiger Moth Aircraft can use grass - Cessna 172 Aircraft can use asphalt - Cessna 172 

So, what does this example do? Well, the code states a few rules about the landing capabilities of jet aircraft as opposed to those powered by a propeller. Within the example, jet aircraft are only allowed to land on sealed runways, in this case, runways with an asphalt surface. Propeller-driven aircraft aren't quite so fussy and can land on all runway surfaces. Note this is a sweeping generalization about the capabilities of jet and prop-driven aircraft types.

The first part of the program defines a data structure for the different runway types using the deftemplate construct. These data structures hold the data for the facts declared later in the code.

 (deftemplate airplane (slot name)) (deftemplate jet extends airplane) (deftemplate prop extends airplane) 

This declares an airplane structure with a single slot, name. The data structures jet and prop inherit from airplane.

It won't be a rule-based program without any rules, so the next part of the code uses the defrule statement to declare a few rules about the different types of aircraft.

Jess rules are similar in form to the classic if ... then ... structure of business rules. In Jess, the if statement equates to the left-hand side (LHS) term of the rule. The LHS term details the pattern the rule engine matches against facts in the knowledge base. Where a match is found, the Jess rule engine fires the actions defined in the rule's right-hand side (RHS) term. Thus, rules are expressed as

 If LHS then RHS 

The Jess notation is terser, so we get

 LHS => RHS 

Here is the rule that states only propeller-powered aircraft can land on grass runways.

 (defrule can-use-grass-runway   "Planes that can use a grass runway"   (prop (name ?n))   =>   (printout t "Aircraft can use grass - " ?n crlf)) 

Adding, or asserting, facts of type prop causes the can-use-grass-runway rule to fire. The second rule in the example stipulates which aircraft can use a sealed runway. For the purposes of the example, all aircraft can use an asphalt runway, regardless of whether they are jet or propeller powered. This second rule is defined as

 (defrule can-use-asphalt-runway   "Planes that can use asphalt"   (airplane (name ?n))   =>   (printout t "Aircraft can use asphalt - " ?n crlf)) 

Rules are no good without facts upon which to act. The example uses the deffacts statement as shorthand to assert a number of named facts into the rule engine's knowledge base.

 (deffacts aircraft-facts "Aircraft type facts"   (prop (name "Cessna 172"))   (prop (name "Tiger Moth"))   (jet (name "Boeing 737"))) 

The deffacts construct sets up the program's data, adding each aircraft to the knowledge base according to its type, either a prop or a jet.

The deffacts statement is not the only way to add facts to our knowledge base. We could have used a rule to achieve the same result and instead written

 (defrule start-up   "Rule to be executed first"   =>   (assert (prop (name "Cessna 172")))   (assert (prop (name "Tiger Moth")))   (assert (jet (name "Boeing 737")))) 

Rules with no LHS terms are always executed first by the Jess rule engine. Thus, the start-up rule is fired when the program is reset and the facts asserted. This in turn fires the remaining rules, and the output of the program is generated. However, before this can happen, we need to reset the program and start the rule engine. The final two lines of code complete the example:

 (reset) (run) 

The rule engine runs until all rules eligible for execution have been fired, after which the program exits.

The Rete Algorithm

Like CLIPS, Jess uses an optimized algorithm for determining which rules are eligible for firing in the form of the Rete algorithm. The efficiency of this algorithm enables rule-based systems to offer superior performance to that of their procedural counterparts that must rely on a multitude of conditional statements for defining rules.

Forward and Backward Chaining

Rule engines like Jess that start by matching the LHS terms of a rule are called forward chaining systems. These systems are data-driven: they start from the facts. Once running, the execution cycle of one rule can update the facts in the knowledge base, causing the condition of another rule to be met, thus firing the rule.

Alternatively, rule engines that operate by commencing with a stated goal that is matched to the RHS terms are known as backward chaining systems. Jess uses predominantly forward chaining but has the flexibility to use the backward chaining approach. The logic programming language Prolog is a classic example of a backward chaining system.

Jess and Java

Running Jess programs through the Jess interpreter is one thing, but how can Jess be put to work in a Java or J2EE application? Thankfully, Jess provides a Java API for this very purpose.

Mixing Jess with Java combines the object-oriented and rule-based paradigms. This allows the definition of business rules using the Jess language, while Java is used to assert facts into the rule engine and retrieve the results.

The next example demonstrates the hybrid approach. For convenience, the initial rules and data structures from the first example have been retained. Listing 10-2 shows the rules the example Java program will be loading.

Listing 10-2. airplane_rules.clp
 ; Fact templates ; (deftemplate airplane (slot name)) (deftemplate jet extends airplane) (deftemplate prop extends airplane) ; Rules ; (defrule can-use-grass-runway   "Planes that can use a grass runway"   (prop (name ?n))   =>   (printout t "Aircraft can use grass - " ?n crlf)) (defrule can-use-asphalt-runway   "Planes that can use asphalt"   (airplane (name ?n))   =>   (printout t "Aircraft can use asphalt - " ?n crlf)) 

The rules and structures in airplane_rules.clp are loaded into working memory by the Airplane.java example, as shown in Listing 10-3.

Listing 10-3. Airplane.java
 import java.util.Iterator; import jess.Fact; import jess.RU; import jess.Rete; import jess.Value; public class Airplane {   public static void main(String[] args) {     try {       // Create an instance of the rule engine       //       Rete rete = new Rete();       // Load in the rules       //       rete.executeCommand("(batch src/airplane_rules.clp)");       rete.reset();       // Add 4 facts       // First 3 facts are added to rule engine using Java       //       Fact f1 = new Fact("jet", rete);       f1.setSlotValue("name",                       new Value("Boeing 737", RU.STRING));       rete.assertFact(f1);       Fact f2 = new Fact("prop", rete);       f2.setSlotValue("name",                       new Value("Cessna 172", RU.STRING));       rete.assertFact(f2);       Fact f3 = new Fact("prop", rete);       f3.setSlotValue("name",                       new Value("Tiger Moth", RU.STRING));       rete.assertFact(f3);       // Fourth fact is added by executing Jess statement       //       rete.executeCommand(         "(assert (jet (name \"Airbus 320\")))");       // Kick the rule engine into life       //       rete.run();       // Facts can be listed programmatically       //       System.out.println("\nAsserted facts");       Iterator iter = rete.listFacts();       while (iter.hasNext()) {         System.out.println("Fact: " + iter.next());       }       // Alternatively Jess can do the job for us       //       System.out.println("\nFacts as told by Jess");       rete.executeCommand("(facts)");    }    catch (Exception e) {      System.err.println(e);    }  } } 

Embedding Jess within a Java application first requires instantiating an instance of the Rete class. This is the Jess rule engine and provides an interface that allows the calling Java code to perform common Jess tasks such as asserting facts and defining rules. The Rete class also provides the method executeCommand() for parsing and running Jess statements.

The example uses the executeCommand() method to load the rules from airplane_rules.clp into the rule engine.

 rete.executeCommand("(batch src/airplane_rules.clp)"); 

The ability to execute a Jess command provides a convenient shortcut. Running the batch command to load the rules avoids the need for construction of both a FileReader and a Jesp class to parse the input file. Executing the batch command saves a few lines of code.

Facts are asserted into the rule engine by instantiating a new Fact instance and adding it to the Rete object. Here is the declaration of a fact for the jet data structure.

 Fact f1 = new Fact("jet", rete); f1.setSlotValue("name",                 new Value("Boeing 737", RU.STRING)); rete.assertFact(f1); 

Alternatively, facts can be asserted using a Jess command:

 rete.executeCommand("(assert (jet (name \"Airbus 320\")))"); 

With the rules loaded and all facts asserted, the rule engine is ready to run:

 rete.run(); 

Listing 10-4 shows the output from the complete program.

Listing 10-4. Output from Airplane.java
 Aircraft can use asphalt - Airbus 320 Aircraft can use asphalt - Tiger Moth Aircraft can use grass - Tiger Moth Aircraft can use asphalt - Cessna 172 Aircraft can use grass - Cessna 172 Aircraft can use asphalt - Boeing 737 Asserted facts Fact: (MAIN::initial-fact) Fact: (MAIN::jet (name "Boeing 737")) Fact: (MAIN::prop (name "Cessna 172")) Fact: (MAIN::prop (name "Tiger Moth")) Fact: (MAIN::jet (name "Airbus 320")) Facts as told by Jess f-0   (MAIN::initial-fact) f-1   (MAIN::jet (name "Boeing 737")) f-2   (MAIN::prop (name "Cessna 172")) f-3   (MAIN::prop (name "Tiger Moth")) f-4   (MAIN::jet (name "Airbus 320")) For a total of 5 facts. 

The Java API provided by Jess is proprietary to the Jess rule engine. Other rule-engine implementations, like JRules from ILOG, offer their own proprietary interfaces for accessing the services of the rule engine from a Java application.

To standardize on rule-engine access from the Java platform, a Java Specification Request (JSR) has been approved under the Java Community Process that defines a common API for rule engines.



    Rapid J2EE Development. An Adaptive Foundation for Enterprise Applications
    Rapid J2EEв„ў Development: An Adaptive Foundation for Enterprise Applications
    ISBN: 0131472208
    EAN: 2147483647
    Year: 2005
    Pages: 159
    Authors: Alan Monnox

    flylib.com © 2008-2017.
    If you may any questions please contact us: flylib@qtcs.net