4. Introduction to Programming with Jess in Java

There are two main ways in which Java code can be used with Jess: Java can be used to extend Jess, and the Jess library can be used from Java. The material in this section is relevant to both of these endeavors. Refer to the API documentation for the complete story on these classes.
Note: the code samples herein are necessarily not complete Java programs. In general, all excerpted code would need to appear inside a try block, inside a Java method, inside a Java class, to compile; and all Java source files are expected to include the "import jess.*;" declaration. Sometimes examples build on previous ones; this is usually clear from context. Such compound examples will need to be assembled into one method before compiling.

4.1. The jess.JessException class

The jess.JessException exception type is the only kind of exception thrown by any functions in the Jess library. jess.JessException is rather complex, as exception classes go. An instance of this class can contain a wealth of information about an error that occurred in Jess. Besides the typical error message, a jess.JessException may be able to tell you the name of the routine in which the error occurred, the name of the Jess constructs that were on the exceution stack, the relevant text and line number of the executing Jess language program, and the Java exception that triggered the error (if any.) See the the API documentation for details.

One of the most important pieces of advice for working with the Jess library is that in your catch clauses for JessException, display the exception object. Print it to System.out, or convert to a String and display it in a dialog box. The exceptions are there to help you by telling when something goes wrong; don't ignore them.

Another important tip: the JessException class has a method getNextException which returns non-null when a particular JessException is a wrapper for another kind of exception. For example, if you use the Jess function call to call a function that throws an exception, then call will throw a JessException, and calling JessException.getNextException() will return the real exception that was thrown. Your JessException handlers should always check getNextException(); if your handler simply displays a thrown exception, then it should display the return value of getNextException(), too.

4.2. The jess.Value class

The class jess.Value is probably the one you'll use the most in working with Jess. A Value is a self-describing data object. Every datum in Jess is contained in one. Once it is constructed, a Value's type and contents cannot be changed; it is immutable. Value supports a type() function, which returns one of these type constants (defined in the class jess.RU (RU = "Rete Utilities")):
 
  final public static int NONE             =     0; ; an empty value (not NIL)
  final public static int ATOM             =     1; ; a symbol
  final public static int STRING           =     2; ; a string
  final public static int INTEGER          =     4; ; an integer
  final public static int VARIABLE         =     8; ; a variable
  final public static int FACT             =    16; ; a fact index
  final public static int FLOAT            =    32; ; a double float
  final public static int FUNCALL          =    64; ; a function call
  final public static int LIST             =   512; ; a multifield
  final public static int DESCRIPTOR       =  1024; ; (internal use)
  final public static int EXTERNAL_ADDRESS =  2048; ; a Java object
  final public static int INTARRAY         =  4096; ; (internal use)
  final public static int MULTIVARIABLE    =  8192; ; a multivariable
  final public static int SLOT             = 16384; ; (internal use)
  final public static int MULTISLOT        = 32768; ; (internal use)
  final public static int LONG             = 65536; ; a Java long
Please always use the names, not the literal values, and the latter are subject to change without notice.

Value objects are constructed by specifying the data and (usually) the type. Each overloaded constructor assures that the given data and the given type are compatible. Note that for each constructor, more than one value of the type parameter may be acceptable. The available constructors are:
  public Value(Object o) throws JessException 
  public Value(String s, int type) throws JessException 
  public Value(Value v) 
  public Value(ValueVector f, int type) throws JessException 
  public Value(double d, int type) throws JessException 
  public Value(int value, int type) throws JessException 
Value supports a number of functions to get the actual data out of a Valueobject. These are
  public Object externalAddressValue(Context c) throws JessException
  public String stringValue(Context c) throws JessException 
  public ValueVector factValue(Context c) throws JessException 
  public ValueVector funcallValue(Context c) throws JessException 
  public ValueVector listValue(Context c) throws JessException 
  public double floatValue(Context c) throws JessException 
  public double numericValue(Context c) throws JessException 
  public int descriptorValue(Context c) throws JessException 
  public int intValue(Context c) throws JessException 
The class jess.Context is described in the next section. If you try to convert random values by creating a Value and retrieving it as some other type, you'll generally get a JessException. However, some types can be freely interconverted: for example, integers and floats.

4.2.1. The subclasses of jess.Value

jess.Value has a number of subclasses: jess.Variable, jess.FuncallValue, jess.FactIDValue, and jess.LongValue are the four of most interest to the reader. When you wish to create a value to represent a variable, a function call, a fact, or a Java long, you must use the appropriate subclass.
Note to the design-minded: I could have use a Factory pattern here and hidden the subclasses from the programmer. Because of the many different Value constructors, and for performance reasons, I decided this wouldn't be worth the overhead.
4.2.1.1. The class jess.Variable
Use this subclass of Value when you want to create a Value that represents a Variable. The one constructor looks like this:
  public Variable(String s, int type) throws JessException 
The type must be RU.VARIABLE or RU.MULTIVARIABLE or an exception will be thrown. The String argument is the name of the variable, without any leading '?' or '$' characters.
4.2.1.2. The class jess.FuncallValue
Use this subclass of Value when you want to create a Value that represents a function call (for example, when you are creating a jess.Funcall containing nested function calls.) The one constructor looks like this:
  public FuncallValue(Funcall f) throws JessException 
4.2.1.3. The class jess.LongValue
Use this subclass of Value when you want to create a Value that represents a Java long. These are mostly used to pass to Java functions called via reflection. The one constructor looks like
  public LongValue(long l) throws JessException 
4.2.1.4. The class jess.FactIDValue
Use this subclass of Value when you want to create a Value that represents a fact-id. The one constructor looks like this:
  public FactIDValue(Fact f) throws JessException 
In previous versions of Jess, fact-id's were more like integers; now they are really references to facts. As such, a fact-id must represent a valid
jess.Fact object. Call externalAddressValue(Context) to get the jess.Fact object, and call Fact.getFactId() to get the fact-id as an integer. This latter manipulation will now rarely, if ever, be necessary.

4.2.2. Value resolution

Some jess.Value objects may need to be resolved before use. To resolve a jess.Value means to interpret it in a particular context. jess.Value objects can represent both static values (atoms, numbers, strings) and dynamic ones (variables, function calls). It is the dynamic ones that obviously have to be interpreted in context.

All the jess.Value member functions, like intValue(), that accept a jess.Context as an argument are self-resolving; that is, if a jess.Value object represents a function call, the call will be executed in the given jess.Context, and the intValue() method will be called on the result. Therefore, you often don't need to worry about resolution as it is done automatically. There are several cases where you will, however.

4.3. The jess.Context class

jess.Context represents an execution context for the evaluation of function calls and the resolution of variables. There are very few public member functions in this class, and only a few of general importance.

You can use getVariable() and setvariableto get and change the value of a variable from Java code, respectively.

The function getEngine() gives any Userfunction access to the Rete object in which it is executing.

When a Userfunction is called, a jess.Context argument is passed in as the final argument. You should pass this jess.Context to any jess.Value.(x)Value() calls that you make.

4.4. The jess.Rete class

The jess.Rete class is the rule engine itself. Each jess.Rete object has its own knowledge base, agenda, rules, etc. To embed Jess in a Java application, you'll simply need to create one or more jess.Rete objects and manipulate them appropriately. We'll cover this in more detail in the section on embedding Jess in Java applications. Here I will cover some general features of the jess.Rete class.

4.4.1. Equivalents for common Jess functions

Several of the most commonly used Jess functions are wrappers for methods in the jess.Rete class. Examples are run(), run(int), reset(), clear(), assertFact(Fact), retract(Fact), retract(int), and halt(). You can call these from Java just as you would from Jess.

4.4.2. Executing other Jess commands

You can use the Rete class's executeCommand(String cmd) method to easily execute, from Java, any Jess function call or construct definition that can be represented as a parseable String. For example,
 
  
  import jess.*;
  public class ExSquare
  {
    public static void main(String[] unused)
    { 
      try
        {
          Rete r = new Rete();
          r.executeCommand("(deffunction square (?n) (return (* ?n ?n)))");
          Value v = r.executeCommand("(square 3)");
    
          // Prints '9'
          System.out.println(v.intValue(r.getGlobalContext()));
        }
      catch (JessException ex)
        {
          System.err.println(ex);
        }
    }
  }
  C:\> java ExSquare
  
    9
  
executeCommand() returns the jess.Value object returned by the command. Commands executed via executeCommand() may refer to Jess variables; they will be interpreted in the global context. In general, only defglobals can be used in this way.

Note that you may only pass one function call or construct at a time to executeCommand().
4.4.2.1. Optional commands
Note that when you create a Rete object from Java, it will already contain definitions for all of the functions that come with Jess. There are no longer any "optional" commands.

4.4.3. The script library

Some of Jess's commands are defined in Jess language code, in the file jess/scriptlib.clp. Each Rete object will load this script library when it is created and again if (clear) is called. In previous versions of Jess you had to do this yourself; this is no longer necessary.

4.4.4. Transferring values between Jess and Java code

This section describes a very easy-to-use mechanism for communicating inputs and results between Jess and Java code.

These methods are available in the class jess.Rete:
    public Value store(String name, Value val);
    public Value store(String name, Object val);
    public Value fetch(String name);
    public void clearStorage();
while these functions are available in Jess:
    (store <name> <value>)
    (fetch <name>)
    (clear-storage)
Both store methods accept a "name" and a value (in Java, either in the form of a jess.Value object or an ordinary Java object; in Jess, any value), returning any old value with that name, or null (or nil in Jess) if there is none. Both fetch methods accept a name, and return any value stored under that name, or null/nil if there is no such object. These functions therefore let you transfer data between Jess and Java that cannot be represented textually (of course they work for Strings, too.) In this example we create an object in Java, then pass it to Jess to be used as an argument to the definstance command.
 
  
  import jess.*;
  public class ExFetch
  {
    public static void main(String[] unused) throws JessException
    { 
      Rete r = new Rete();
      r.store("DIMENSION", new java.awt.Dimension(10, 10));
      r.executeCommand("(defclass dimension java.awt.Dimension)");
      r.executeCommand("(definstance dimension (fetch DIMENSION) static)");

      r.executeCommand("(facts)");
    }
  }
  C:\> java ExFetch
  
      f-0   (MAIN::dimension (class <External-Address:java.lang.Class>) (height 10.0) (size <External-Address:java.awt.Dimension>) (width 10.0) (OBJECT <External-Address:java.awt.Dimension>))
      For a total of 1 facts.
  

Note that storing a null (or nil) value will result in the given name being removed from the hashtable altogether. clearStorage() and clear-storage each remove all data from the hashtable.

Note that the Jess clear and Java clear() functions will call clearStorage(), but reset and reset() will not. Stored data is thus available across calls to reset().

Another example, with a main program in Java and a set of rules that return a result using store is in the directory Jess60/jess/examples/xfer/ .

4.4.5. Methods for adding, finding and listing constructs

The easiest (and still encouraged) way to define templates, defglobals, and other constructs is to use Jess language code and let Jess parse the textual definition. However, many of these constructs are represented by public classes in the Jess library, and if you wish, you can construct your own instances of these in Java code and add them to an engine explicitly. This is currently possible for most, but not all, Jess constructs. Right now the jess.Defrule class does not expose enough public methods to properly create one outside of the jess package. This is deliberate, as this API is likely to change again in the near future. For information about the classes mentioned here (jess.Deftemplate, jess.Defglobal, etc) see the API documentation.

These jess.Rete methods let you add constructs to the engine: These methods return individual constructs from within the engine, generally by name: These methods return java.util.Iterators of various data structures in the engine:

4.4.6. I/O Routers

The functions printout and format take an I/O router name as an argument. The default argument t is Jess' standard input and output. Jess also has a special WSTDOUT router for printing user messages internally - for example, the Jess> prompt, the messages you get when you issue a watch command, and the output of commands like facts and ppdefrule. The read command and readline take input from the t router by default.

By default, Jess's standard routers are connected to Java's standard streams, so that output goes to the command-line window. This is perfect for command-line programs, but of course not acceptable for GUI-based applications. To remedy this, Jess lets you connect the t router to any Java java.io.Reader and java.io.Writer objects you choose. In fact, you can not only redirect the t router, but you can add routers of your own, in much the same way that the open command creates a new router that reads from a file.

These functions in the Rete class let you manipulate the router list: The words "input" and "output" are from the perspective of the Jess library itself; i.e., Jess reads from input routers and writes to output routers.

Note that you can use the same name for an input router and an output router (the t router is like that.) Note also that although these functions accept and return generic Reader and Writer objects, Jess internally uses java.io.PrintWriter and java.io.BufferedReader. If you pass in other types, Jess will construct one of these preferred classes to "wrap" the object you pass in.

When Jess starts up, there are three output routers and one input router defined: the t router, which reads and writes from the standard input and output; the WSTDOUT router, which Jess uses for all prompts, diagnostic outputs, and other displays; and the WSTDERR router, which Jess uses to print stack traces and error messages. By default, t is connected to System.in and System.out, and both WSTDOUT and WSTDERR are connected to System.out (neither is connected to System.err.) You can reroute these inputs and outputs simply by changing the Readers and Writers they are attached to using the above functions. You can use any kind of streams you can dream up: network streams, file streams, etc.

The boolean argument consoleLike to the addInputRouter method specifies whether the stream should be treated like the standard input or like a file. The difference is that on console-like streams, a read call consumes an entire line of input, but only the first token is returned; while on file-like streams, only the characters that make up each token are consumed on any one call. That means, for instance, that a read followed by a readline will consume two lines of text from a console-like stream, but only one from a file-like stream, given that the first line is of non-zero length. This odd behaviour is actually just following the behaviour of CLIPS.

The jess.Rete class has two more handy router-related methods: getOutStream() and getErrStream(), both of which return a java.io.PrintWriter object. getOutStream() returns a stream that goes to the same place as the current setting of WSTDOUT; errStream() does the same for WSTDERR.

4.4.7. jess.awt.TextAreaWriter and jess.awt.TextReader

Jess ships with two utility classes that can be very useful when building GUIs for Jess: the jess.awt.TextAreaWriter and jess.awt.TextReader classes. Both can serve as adapters between Jess and graphical input/output widgets. The TextAreaWriter class is, as the name implies, a Java Writer that sends any data written to it to a java.awt.TextArea. This lets you place Jess's output in a scrolling window on your GUI. The jess.Console and jess.ConsoleApplet jess GUIs use these classes. To use TextAreaWriter, simply call addOutputRouter(), passing in an instance of this class:
 

  import java.awt.TextArea;
  import jess.awt.*;
  import jess.*;
  public class ExTAW
  {
    public static void main(String[] unused) throws JessException
    { 
       TextArea ta = new TextArea(20, 80);
       TextAreaWriter taw = new TextAreaWriter(ta);
           
       Rete r = new Rete();
       r.addOutputRouter("t", taw);
       r.addOutputRouter("WSTDOUT", taw);
       r.addOutputRouter("WSTDERR", taw);
       // Do something interesting, then...
       System.exit(0);
    }
  }
C:\> java ExTAW
Now the output of the printout command, for example, will go into a scrolling window (of course, you need to display the TextArea on the screen somehow!) Study jess/ConsolePanel.java and jess/Console.java to see a complete example of this.

jess.awt.TextReader is similar, but it is a Reader instead. It is actually quite similar to java.io.StringReader, except that you can continually add new text to the end of the stream (using the appendText() method). It is intended that you create a jess.awt.TextReader, install it as an input router, and then (in an AWT event handler, somewhere) append new input to the stream whenever it becomes available. See the same jess/Console* files for a complete usage example for this class as well.

4.5. The jess.ValueVector class

The jess.ValueVector class is Jess's internal representation of a list, and therefore has a central role in programming with Jess in Java. The jess.ValueVector class itself is used to represent generic lists (multifields), while specialized subclasses are used as function calls (jess.Funcall), facts (jess.Fact), and templates (Deftemplate).

Working with ValueVector itself is simple. Its API is reminiscent of java.util.Vector. Like that class, it is a self-extending array: when new elements are added the ValueVector grows in size to accomodate them. Here is a bit of example Java code in which we create the Jess list (a b c).
 
  
  import jess.*;
  public class ExABC
  {
    public static void main(String[] unused) throws JessException
    { 
      ValueVector vv = new ValueVector();
      vv.add(new Value("a", RU.ATOM));
      vv.add(new Value("b", RU.ATOM));
      vv.add(new Value("c", RU.ATOM));
  
      // Prints "(a b c)"
      System.out.println(vv.toStringWithParens());
    }
  }
  C:\> java ExABC
  
    (a b c)
  
The add() function returns the ValueVector object itself, so that add() calls can be chained together for convenience:
 
  
  import jess.*;
  public class ExChain
  {
    public static void main(String[] unused) throws JessException
    { 
      ValueVector vv = new ValueVector();
      vv.add(new Value("a", RU.ATOM)).add(new Value("b", RU.ATOM)).add(new Value("c", RU.ATOM));
  
      // Prints "(a b c)"
      System.out.println(vv.toStringWithParens());
    }
  }
  C:\> java ExChain
  
    (a b c)
  
To pass a list from Java to Jess, you should enclose it in a jess.Value object of type RU.LIST.

4.6. The jess.Funcall class

jess.Funcall is a specialized subclass of ValueVector that represents a Jess function call. It contains the name of the function, an internal pointer to the actual jess.Userfunction object containing the function code, and the arguments to pass to the function.

You can call Jess functions using jess.Funcall if you prefer, rather than using jess.Rete.executeFunction(). This method has less overhead since there is no parsing to be done. This example is an alternate version of the "defclass Dimension" example above.
 
  
  import java.awt.Dimension;
  import jess.*;
  public class ExADimension
  {
    public static void main(String[] unused) throws JessException
    { 
      Rete r = new Rete();
      Context c = r.getGlobalContext();
      Value dimension = new Value("dimension", RU.ATOM);

      Funcall f = new Funcall("defclass", r);
      f.arg(dimension).arg(new Value("java.awt.Dimension", RU.ATOM));
      f.execute(c);
    
      new Funcall("definstance", r).arg(dimension).
          arg(new Value(new Dimension(10, 10))).
          arg(new Value("static", RU.ATOM)).execute(c);
        
      new Funcall("facts", r).execute(c);
    }
  }
  C:\> java ExADimension
  
      f-0   (MAIN::dimension (class <External-Address:java.lang.Class>) (height 10.0) (size <External-Address:java.awt.Dimension>) (width 10.0) (OBJECT <External-Address:java.awt.Dimension>))
      For a total of 1 facts.
  
The example shows several styles of using jess.Funcall. You can chain add() calls, but remember that add() returns ValueVector, so you can't call execute() on the return value of Funcall.add() A special method arg() is provided for this purpose; it does the same thing as add() but returns the Funcall as a Funcall.

The first entry in a Funcall's ValueVector is the name of the function, even though you don't explicitly set it. Changing the first entry will not automatically change the function the Funcall will call!

The Funcall class also contains some public static constant Value member objects that represent the special atoms nil, TRUE, FALSE, EOF, etc. You are encouraged to use these.

4.7. The jess.Fact class

Another interesting subclass of ValueVector is jess.Fact, which, predictably, is how Jess represents facts. A Fact is stored as a list in which all the entries correspond to slots. The head or name of the fact is stored in a separate variable (available via the getName() method.)

Once you assert a jess.Fact object, you no longer "own" it - it becomes part of the Rete object's internal data structures. As such, you must not change the values of any of the Fact's slots. If you retract the fact, the Fact object is released and you are free to alter it as you wish.

4.7.1. Constructing an Unordered Fact from Java

In the following example, we create a template and assert an unordered fact that uses it.
 
  
  import jess.*;
  public class ExPoint
  {
    public static void main(String[] unused) throws JessException
    { 
      Rete r = new Rete();
      r.executeCommand("(deftemplate point \"A 2D point\" (slot x) (slot y))");
  
      Fact f = new Fact("point", r);
      f.setSlotValue("x", new Value(37, RU.INTEGER));
      f.setSlotValue("y", new Value(49, RU.INTEGER));
      r.assertFact(f);

      r.executeCommand("(facts)");
    }
  }
  C:\> java ExPoint
  
    f-0   (MAIN::point (x 37) (y 49))
    For a total of 1 facts.
  

4.7.2. Constructing a Multislot from Java

In this example, the template has a multislot. In Java, a multislot is represented by a Value of type RU.LIST; the Value object contains a ValueVector containing the fields of the multislot.
 
  
  import jess.*;
  public class ExMulti
  {
    public static void main(String[] unused) throws JessException
    { 
      Rete r = new Rete();
      r.executeCommand("(deftemplate vector \"A named vector\"" +
                       " (slot name) (multislot list))");
  
      Fact f = new Fact("vector", r);
      f.setSlotValue("name", new Value("Groceries", RU.ATOM));
      ValueVector vv = new ValueVector();
      vv.add(new Value("String Beans", RU.STRING));
      vv.add(new Value("Milk", RU.STRING));
      vv.add(new Value("Bread", RU.STRING));
      f.setSlotValue("list", new Value(vv, RU.LIST));
      r.assertFact(f);

      r.executeCommand("(facts)");
    }
  }
  C:\> java ExMulti
  
    f-0   (MAIN::vector (name Groceries) (list "String Beans" "Milk" "Bread"))
    For a total of 1 facts.
  

4.7.3. Constructing an Ordered Fact from Java

An ordered fact is actually represented as an unordered fact with a single slot: a multislot named __data. You don't need to create a template for an ordered fact: one will be created automatically if it doesn't already exist.
 
  
  import jess.*;
  public class ExOrdered
  {
    public static void main(String[] unused) throws JessException
    { 
      Rete r = new Rete();
  
      Fact f = new Fact("letters", r);
      ValueVector vv = new ValueVector();
      vv.add(new Value("a", RU.ATOM));
      vv.add(new Value("b", RU.ATOM));
      vv.add(new Value("c", RU.ATOM));
      f.setSlotValue("__data", new Value(vv, RU.LIST));
      r.assertFact(f);

      r.executeCommand("(facts)");
    }
  }
  C:\> java ExOrdered
  
    f-0   (MAIN::letters a b c)
    For a total of 1 facts.
  

4.8. The jess.Deftemplate class

Yet another interesting subclass of ValueVector is jess.Deftemplate, the purpose of which should be obvious. Deftemplate has a fairly large interface which allows you to set and query the properties of a template's slots.

This example is an alternative to the deftemplate command in the previous example.
 

  import jess.*;
  public class ExBuildDeftemplate
  {
    public static void main(String[] unused) throws JessException
    { 
      Rete r = new Rete();
      Deftemplate dt = new Deftemplate("point", "A 2D point", r);
      Value zero = new Value(0, RU.INTEGER);
      dt.addSlot("x", zero, "NUMBER");
      dt.addSlot("y", zero, "NUMBER");
      r.addDeftemplate(dt);
    
      // Now create and assert Fact
    }
  }
C:\> java ExBuildDeftemplate

4.9. The jess.Token class

The jess.Token class is used to represent partial matches in the Rete network. You'll use it if you're writing an Accelerator (not documented here) or if you're working with queries.

Only a few methods of jess.Token are public, and fewer are of use to the programmer. int size() tells you how many jess.Facts are in a given jess.Token. The most important method is Fact fact(int), which returns the jess.Fact objects that make up the partial match. Its argument is the zero-based index of the jess.Fact to retrieve, and must be between 0 and the return value of size(). Each Fact will correspond to one pattern on a rule or query LHS; dummy facts are inserted for not and test CEs.

4.10. The jess.JessEvent and jess.JessListener classes

jess.JessEvent and jess.JessListener make up Jess's rendition of the standard Java event pattern. By implementing the JessListener interface, a class can register itself with a source of JessEvents, like the jess.Rete class. jess.Rete (potentially) fires events at all critical junctures during its execution: when rules fire, when a reset() or clear() call is made, when a fact is asserted or retracted, etc. JessEvent has a getType() method to tell you what sort of event you have been notified of; the type will be one of the constants in the JessEvent class.

You can control which events a jess.Rete object will fire using the setEventMask() method. The argument is the result of logical-OR-ing together some of the constants in the jess.JessEvent class. By default, the event mask is 0 and no events are sent.

As an example, let's suppose you'd like your program's graphical interface to display a running count of the number of facts on the fact-list, and the name of the last executed rule. You've provided a static method, MyGUI.displayCurrentRule(String ruleName), which you would like to have called when a rule fires. You've got a pair of methods MyGUI.incrementFactCount() and MyGUI.decrementFactCount() to keep track of facts. And you've got one more static method, MyGUI.clearDisplay(), to call when Jess is cleared or reset. To accomplish this, you simply need to write an event handler, install it, and set the event mask properly. Your event handler class might look like this.
 

  import jess.*;
  
  public class ExMyEventHandler implements JessListener
  {
    public void eventHappened(JessEvent je)
    {
      int defaultMask = JessEvent.DEFRULE_FIRED | JessEvent.FACT |
                        JessEvent.RESET | JessEvent.CLEAR;
      int type = je.getType();
      switch (type)
      {
        case JessEvent.CLEAR:               
          Rete engine = (Rete) je.getSource();
          int mask = engine.getEventMask();
          mask |= defaultMask;
          engine.setEventMask(mask);

        // FALL THROUGH
        case JessEvent.RESET:
          // MyGUI.clearDisplay();
          break;
          
        case JessEvent.DEFRULE_FIRED:
          // MyGUI.displayCurrentRule( ((Activation) je.getObject()).getRule().getName());
          break;
          
        case JessEvent.FACT | JessEvent.REMOVED:
          // MyGUI.decrementFactCount();
          break;

        case JessEvent.FACT:
          // MyGUI.incrementFactCount();
          break;
          
        default:
          // ignore
      }
    }   
  }
C:\> java ExMyEventHandler
Note how the event type constant for fact retracting is composed from FACT | REMOVED. In general, constants like DEFRULE, DEFTEMPLATE, etc, refer to the addition of a new construct, while composing these with REMOVE signifies the removal of the same construct.

The getObject() method returns ancillary data about the event. In general, it is an instance of the type of object the event refers to; for DEFRULE_FIRED it is a jess.Defrule.

To install this listener, you would simply create an instance and call jess.Rete.addEventListener(), then set the event mask:
 

  import jess.*;
  public class ExMask
  {
    public static void main(String[] unused) throws JessException
    { 
      Rete engine = new Rete();
      engine.addJessListener(new ExMyEventHandler());
      engine.setEventMask(engine.getEventMask() |
                          JessEvent.DEFRULE_FIRED | JessEvent.CLEAR |
                          JessEvent.FACT | JessEvent.RESET );
    }
  }
C:\> java ExMask
One added wrinkle: note how the handler for JessEvent.CLEAR sets the event mask. When (clear) is called, the event mask is typically reset to the default. When event handlers are called, they have the opportunity to alter the mask to re-install themselves. Alternatively, they can call removeJessListener() to unregister themselves.

Note that each event handler added will have a negative impact on Jess performance so their use should be limited.

There is no way to receive only one of an event / (event | REMOVE) pair.

4.10.1. Working with events from the Jess language

It's possible to work with the event classes from Jess language code as well. To write an event listener, you can use the jess.JessEventAdapter class. This class works rather like the jess.awt adapter classes do. Usage is best illustrated with an example. Let's say you want to print a message each time a new template is defined, and you want to do it from Jess code. Here it is:
  Jess> ;; make code briefer
  (import jess.*)
  TRUE

  Jess> ;; Here is the event-handling deffunction
  ;; It accepts one argument, a JessEvent
  (deffunction display-deftemplate-from-event (?evt)
    (if (eq (get-member JessEvent DEFTEMPLATE) (get ?evt type)) then
      (printout t "New deftemplate: " (call (call ?evt getObject) getName) crlf)))
  TRUE

  Jess> ;; Here we install the above function using a JessEventAdapter
  (call (engine) addJessListener
  (new JessEventAdapter display-deftemplate-from-event (engine)))
  
  Jess> ;; Now we add DEFTEMPLATE to the event mask
  (set (engine) eventMask
  (bit-or (get (engine) eventMask) (get-member JessEvent DEFTEMPLATE)))
Now whenever a new template is defined, a message will be displayed.

4.11. Setting and Reading Java Bean Properties

As mentioned previously, Java objects can be explicitly pattern-matched on the LHS of rules, but only to the extent that they are Java Beans. A Java Bean is really just a Java object that has a number of methods that obey a simple naming convention for Java Bean properties. A class has a Bean property if, for some string X and type T it has either or both of: Note that the capitalization is also important: for example, for a method named isVisible, the property's name is visible, with a lower-case V. Only the capitalization of the first letter of the name is important. You can conveniently set and get these properties using the Jess set and get methods. Note that many of the trivial changes in the Java 1.1 were directed towards making most visible properties of objects into Bean properties.

An example: AWT components have many Bean properties. One is visible, the property of being visible on the screen. We can read this property in two ways: either by explicitly calling the isVisible() method, or by querying the Bean property using get.
  Jess> (defglobal ?*frame* = (new java.awt.Frame "Frame Demo"))
  TRUE

  Jess> ;; Directly call 'isVisible', or...
  (printout t (call ?*frame* isVisible) crlf)
  FALSE

  Jess> ;; ... equivalently, query the Bean property
  (printout t (get ?*frame* visible) crlf)
  FALSE

4.12. Formatting Jess Constructs

The class jess.PrettyPrinter can produce a formatted rendering of many Jess objects, including jess.Defrules, Deffunctions jess.Defquerys, etc -- anything that implements the jess.Visitable interface. jess.PrettyPrinter is very simple to use: you just create an instance, passing the object to be rendered as a constructor argument, and then call toString to get the formatted result.
 
  
  import jess.*;
  public class ExPretty
  {
    public static void main(String[] unused) throws JessException
    { 
      Rete r = new Rete();
      r.executeCommand("(defrule myrule (A) => (printout t \"A\" crlf))");
      Defrule dr = (Defrule) r.findDefrule("myrule");
      System.out.println(new PrettyPrinter(dr));
    }
  }
  C:\> java ExPretty
  
    (defrule MAIN::myrule
      (MAIN::A)
      =>
      (printout t "A" crlf))
  

Back to index