Rules Guide

CHAPTER 3

Developing Custom Conditions and Actions

This chapter describes how to write your own classes for special-purpose conditions and actions. It includes the following topics:

This chapter assumes you are familiar with rules-based applications and using the Rule Editor in exteNd Director. For background information, see How You Use Rules.

 
Top of page

About custom conditions and actions

Conditions and actions are Java classes used to build rules fired by portlets and other application components. The Rule Editor provides a selection of prebuilt conditions and actions. You can also write custom conditions and actions to meet your application's specific requirements.

Conditions and actions are typically implemented as JavaBeans, with properties you can set in the Rule Editor.

 
Top of page

Designing a condition or action

Designing a condition or action involves several decision points.

What logic should be implemented?    When designing rule-based logic, consider how conditions and actions will interact with each other and with the application source code. Here are key considerations:

Will any of the installed conditions or installed actions meet my requirements?   Before you begin your development, you should become familiar with the installed conditions and actions. There may be one that will meet your requirements, or you may want to modify one of the installed versions. For more information, see Installed Conditions and Installed Actions.

What kind of user interface should be rendered by the Rule Editor?   By default, the Rule Editor generates a generic property panel as the interface for interacting with conditions and actions you use to build rules. If a condition or action has properties, the property panel provides controls—such as check boxes and text boxes—for choosing or entering required values. You can also specify a custom user interface in your condition and action class, which the Rule Editor then uses to render the property panel. For more information, see Defining properties.

What supporting classes will be needed?   Conditions and actions often rely on supporting classes such as:

Type of class

Used for

Custom classes

Implementing logic not provided in the exteNd Director API

BeanInfo classes

Providing additional information for the condition or action JavaBean

For more information    For more information, see Writing a BeanInfo class

Resource bundles

Localizing static strings that appear in the property panel for your conditions and actions

For more information    For more information, see Using resource bundles

Does the rule need to access runtime values?   You can define properties to access session whiteboard values that can be set dynamically at runtime. For more information, see Defining runtime properties.

 
Top of page

Defining logic

Conditions and actions each have three methods that define their implementations:

Method

Used in

Description

doCondition()

Conditions

Fulfills the requirements of a condition

In this method, you write code that makes comparisons or evaluates property values and returns a boolean value

doAction()

Actions

Fulfills the requirements of an action

In this method, you write code that performs actions based on whether the associated condition evaluates to true or false

This method typically returns the result of a business rule

toString()

Both conditions and actions

Returns a string that describes the condition and its current settings

This string appears as a description of a condition or action in the Rule Editor

These methods are included in the templates generated by exteNd Director's Condition Wizard and Action Wizard.

For more information    For an overview of the templates and core methods, see About the template methods.

 
Top of section

Defining logic for a condition

Typically a condition compares property values entered in the Rule Editor and returns a boolean. This logic is coded within the doCondition() method.

The doCondition() method includes an EbiContext object, allowing you to access runtime values.

Example of doCondition()

This example shows code from the installed condition CheckTime, which checks whether the current time of day is within the time range specified for the condition in the Rule Editor:

  // ...
  // JavaBean get/set accessor methods
  	 public int getFrom() {
  	 	 return from;
  	 }
  
  	 public void setFrom( int from ) {
  	 	 this.from = from;
  	 }
  
  	 public int getTo() {
  	 	 return to;
  	 
  	 public void setTo( int to ) {
      	 this.to = to;
  	 }
  // Check to see if the current time of day is within the specified
  // range.
  public boolean doCondition( com.sssw.re.api.EbiContext context ) throws com.sssw.re.exception.EboConditionException {
  	 	 int hour = new GregorianCalendar().get( Calendar.HOUR_OF_DAY );
  
      	 if ( from < to ) {
  	 	 	 // Daytime check
  	 	 	 if ( hour >= from ) {
  	 	 	 	 if ( hour <= to ) {
  	 	 	 	 	 return true;
  	 	 	 	 }
  	 	 	 }
  	 	 	 return false;
  	 	 }
  	 	 else {
  	 	 	 // Crossover check
  	 	 	 if ( hour >= to ) {
  	 	 	 	 if ( hour <= from ) {
  	 	 	 	 	 return false;
  	 	 	 	 }
  	 	 	 }
  	 	 	 return true;
  	 	 }
  
  	 }
  // ...

Notes about the condition example

get/set methods   These are JavaBean accessor methods that define two properties for this condition: from a specified time and to a specified time (inclusive). These values are accessed from the property panel in the Rule Editor.

For more information    For information, see Defining JavaBeans.

doCondition()    This method is the workhorse of condition classes. In this example the property values are compared to the current time, which is obtained from a Java GregorianCalendar object. The code does two checks—for within current day and for crossover times—in each case returning a boolean.

 
Top of section

Defining logic for an action

An action returns the result of a rule, based on the return value of one or more conditions. The logic for an action is coded in the doAction() method.

Actions can perform virtually any activity, including accessing a database via SQL, manipulating whiteboard values, returning HTTP and HTML values, performing operations, and controlling user access to objects. Like conditions, actions typically have properties settable in the Rule Editor.

Returning values to the caller

Because actions return the result of a rule, you need to understand how the result is handled by the caller, typically a portlet or JSP page. Here is a summary of the common HTTP response methods you might use to return a value from an action to the portlet that fired the rule:

Return value method

Description

Usage

EbiContext.setResponseStatus()

Sets an HTTP status code that indicates whether the action succeeded or failed

Called from doAction() method

EbiContext.setResponsePhrase()

Sets a response phrase used to pass data from the action back to the firing portlet

Called from doAction() method

EbiContext.setResponseType()

Sets the type of the response phrase to indicate how the firing portlet should process the data

Should be called from the doAction() method whenever the method EbiContext.setResponsePhrase() is called

Example of doAction()

The following shows code from the installed action ReturnAsHTMLBold. It returns the text entered by the user in bold format:

  public void doAction( com.sssw.re.api.EbiContext context ) throws
     com.sssw.re.exception.EboActionException {
  context.setResponsePhrase( "<b>" + getValue().getValue( context ) + 	 	 "</b>" );

 
Top of section

Defining a condition or action rule descriptor

The toString() method provided by the template returns a description of the condition or action that appears in the Rule Editor when a condition or action is selected. Typically, toString() calls the properties' get methods to include property values.

Example of toString()

Here is how toString() is implemented in the installed condition CheckTime:

  // Use resource bundle, "caResource" instantiated with Class
  // definition, for string processing.
  public String toString() {
  	 return caResource.getString("the hour is between <b>") +
  	 	 getHours().elementAt( from ) + caResource.getString("</b>
  	 	 and <b>") + getHours().elementAt( to );
  	 }
  // Implement a JPanel using getParameterPanel() using getHours() 
  // to get selected property values.
  
  private Vector getHours() {
  	 	 if ( hours == null ) {
  	 	 	 hours = new Vector();
  	 	 	 hours.addElement( caResource.getString("12 am"));
  	 	 	 hours.addElement( caResource.getString("1 am"));
  	 	 	 hours.addElement( caResource.getString("2 am"));
          // ...

Notes about toString() example

A local method, getHours(), uses a Vector to store property values. The toString() method calls getHours() to display the hours selected in the Rule Editor.

NOTE:   In this example, toString() directs processing to a resource bundle that is instantiated with the condition class. This provides support for localization. For more information, see Using resource bundles.

Here is what the result looks like in the Rule Editor:

CAtoStringexample

 
Top of page

Defining properties

There are several ways to define properties and property panels for conditions and actions. The Rule subsystem supports Java constructs like JavaBeans and JPanels, and also provides default controls for certain data types and objects and support for using runtime values in properties. You can also follow the implementation of resource bundles and BeanInfo classes used in the installed conditions and actions.

This section provides descriptions and examples of each approach.

 
Top of section

Defining JavaBeans

Like other JavaBeans, conditions and actions can have properties. By defining properties, you allow conditions and actions to be customized in the Rule Editor for use in a variety of rules.

When you add properties in a custom condition or action, you must follow JavaBeans standards. For each property, define:

The Rule Editor can use these constructs to automatically generate a property panel for the condition or action, based on the data type of the property. For a model implementation, see Example of doCondition().

You can also use a JDK BeanInfo class to enhance the display properties. See Writing a BeanInfo class.

 
Top of section

Defining runtime properties

The Rule subsystem provides two string template data types that allow properties in conditions and actions to be set dynamically from runtime values:

When you define properties using these data types, the Rule Editor lets you specify values for these properties by referencing whiteboard keys. When the rule is fired, the property will be set to the real-time value associated with the whiteboard key.

For more information    For information about using the whiteboard, see How You Use Rules.

Working with the !valueOf construct

For each property you want to set dynamically, you need to define a member variable of type EboStringTemplateSingle or EboStringTemplateMulti. The Rule Editor will generate the associated TextField or TextArea, labeled with a string that ends with the caret (^) symbol. This symbol indicates that the property has been defined as a string template that can be set either statically by entering a literal string or dynamically by using the construct !valueOf. With this construct, you specify the value associated with a whiteboard key. Here is the syntax:

  !valueOf.whiteboard key

Certain session values, such as userID, are automatically provided. For example: to set the property to the value associated with the whiteboard key UserID, enter !valueOf.UserID.

Resolving the valueOf! expression   When you write a condition or action that needs to evaluate a runtime value, use one of these methods to get the value:

Method

In class

Usage

merge()

EbiContext

Gets a string of one or more whiteboard values by providing the whiteboard key names

getValue()

EboStringTemplateSingle EboStringTemplateMulti

Gets a string value for a whiteboard key associated with a specified template

Example of using string template values

This example shows code from the installed action SaveToWhiteBoard. The action saves data to the whiteboard in the specified whiteboard key:

  public boolean resolve;
  	 public com.sssw.re.core.EboStringTemplateSingle data = new
    com.sssw.re.core.EboStringTemplateSingle();
  	 public com.sssw.re.core.EboStringTemplateSingle template = new
    com.sssw.re.core.EboStringTemplateSingle();
  
  // Set the template value for the whiteboard key that will be used 
  // to store the data value.
   	 public void setKeyTemplate( EboStringTemplateSingle detailKey ) {
  	 	 this.template = detailKey;
  	 }
  // Return the template for the whiteboard key.
   	 public EboStringTemplateSingle getKeyTemplate() {
  	 	 return template;
  	 }
  
  // Return the template for the data.
   	 public EboStringTemplateSingle getData() {
  	 	 return data;
  	 }
  
  // Set the template value for the data to be saved to the 
  // whiteboard.
   	 public void setData( EboStringTemplateSingle data ) {
  	 	 this.data = data;
  	 }
  
  // Return boolean true if the data value is a templatized string 
  // that needs to be resolved, and false if the data value is the 
  // value to be saved.
  	 public boolean getResolve() {
  	 	 return resolve;
  	 }
  
  // Set the value that determines if the data value needs to be 
  // resolved.
   	 public void setResolve( boolean resolve ) {
  	 	 this.resolve = resolve;
  	 }
  
  // Save the data value, or resolved data value if resolved is
  // true, to the whiteboard in the specified whiteboard key.
  public void doAction( com.sssw.re.api.EbiContext context ) throws com.sssw.re.exception.EboActionException {
  	 	 String merged = getKeyTemplate().getValue( context );
  	 	 context.setValue( merged, ( getResolve() ? getData().getValue( context
       ) : getData().getTemplate() ) );
      }

Notes on string template example

This action defines three properties that appear in the Rule Editor:

CAStringTemplate

EboStringTemplateSingle   The code instantiates two template objects for each template property. The first property (Key-template) will be resolved to get its data representation. The second property (Data) is resolved only if the Resolve control is selected.

doAction()    Gets the properties and implements these methods:

Method

Description

getValue()

The template method that resolves the key value for Key_template

setValue()

The context method that sets the Key-template value on the whiteboard—and:

  • If Resolve is false, sets the Data value string on the whiteboard

  • If Resolve is true, uses template.getValue() to resolve the key value

 
Top of section

Using generic property panels

The Rule subsystem supports a set of data types for which it can automatically generate generic property panels. For each supported data type, the Rule Editor renders a preselected GUI control in the property panel. All you need to do is implement JavaBean accessor methods for each property.

Here is a list of the supported data types and the associated controls:

Supported data type

GUI control

java.lang.String

char

int

long

double

float

TextField

boolean

Checkbox

com.sssw.re.core.EboStringTemplateSingle

TextField

com.sssw.re.core.EboStringTemplateMulti

TextArea

com.sssw.re.condtion.Compare

com.sssw.re.core.EboRule

DropDownListBox

 
Top of section

Creating a custom property panel

If you prefer to create your own property panel rather than use the generic controls, you can implement the getParameterPanel() method to specify a custom user interface. The Rule Editor requires the custom interface to be defined as a JPanel; it will not create a custom property panel based on any other type of portlet returned by getParameterPanel().

When including the getParameterPanel() method in a condition or action, you must implement a listener interface to allow the controls in the JPanel to respond to user actions.

Example of using a custom JPanel

This is the code that defines the property panel for the installed condition CheckTime:

  // ...
  // Return a custom ui component to be used for editing this 
  // condition.
   	 public java.awt.Component getParameterPanel() {
  	 	 JPanel p = new JPanel();
  	 	 p.setLayout( new BoxLayout( p, BoxLayout.X_AXIS ) );
  	 	 p.add( new JLabel( caResource.getString("between the hours
  	 	 	 of") ) );
  	 	 p.add( FROM = new JComboBox( getHours() ) );
  	 	 p.add( new JLabel( caResource.getString("and") ) );
  	 	 p.add( TO = new JComboBox( getHours() ) );
  	 	 FROM.setAlignmentY( JComponent.CENTER_ALIGNMENT );
  	 	 TO.setAlignmentY( JComponent.CENTER_ALIGNMENT );
  	 	 FROM.setSelectedIndex( from );
  	 	 TO.setSelectedIndex( to );
  	 	 FROM.addItemListener( this );
  	 	 TO.addItemListener( this );
  	 	 return p;
  	 }
  
  // Return a vector of hours for selection in the ui.
   	 private Vector getHours() {
  	 	 if ( hours == null ) {
  	 	 	 hours = new Vector();
  	 	 	 hours.addElement( caResource.getString("12 am"));
  	 	 	 hours.addElement( caResource.getString("1 am"));
  	 	 	 hours.addElement( caResource.getString("2 am"));
          // ...

Notes on custom JPanel example

Here is the custom property panel generated by the code:

CAJPanelExample

JComboBox implementation    The variables TO and FROM are defined as ints. Using a generic panel would result in text fields for each property. Instead, getParameterPanel() instantiates a JPanel and implements a pair of JComboBox controls, providing a dropdown list of time selections in the Rule Editor. The code defines a local method, getHours(), that uses a Vector to fill in the values of the combo boxes.

Resource bundle    The code uses a resource bundle caResource to localize the labels for the ComboBox. For information, see Using resource bundles.

 
Top of section

Writing a BeanInfo class

You can write a BeanInfo class for any condition and action to specify the appearance of attributes in the property panel. For example, you can use a BeanInfo class for:

NOTE:   Using a BeanInfo class is the recommended way to implement resource bundles for localization. For information about resource bundles, see Using resource bundles.

Like JavaBeans, BeanInfo classes require their associated condition or action classes to specify properties as member variables with associated get and set methods.

Procedure To create a BeanInfo class:

  1. Create a Java class in your preferred Java editor. The class must extend EboBeanInfo and implement these code elements:

    Code element

    Description

    imports

    For conditions:

    package com.sssw.re.condition.*

    For actions:

    package com.sssw.re.action.*

    For conditions and actions:

    com.sssw.re.core.*

    java.beans.*

    getClazz() method

    Returns the condition or action class associated with the BeanInfo class

    PropertyDescriptor[ ] pds

    Provides information to the Rule Editor for generating the property panel for the condition or action

    The array should be the same size as the number of properties you want to appear in the property panel

    PropertyDescriptor pd

    Defines a descriptor for (each) property

    Each individual property descriptor is added to the property descriptor array

  2. Name the BeanInfo class by appending BeanInfo to the name of the associated condition or action class.

    For example: if your class name is CheckCondition, the BeanInfo class must be named CheckConditionBeanInfo.

For more information    For an example of a BeanInfo class, see Example of a BeanInfo class.

 
Top of section

Using resource bundles

Resource bundles are a standard feature of the Java JDK used for localization. You can store any object, string, number, or other data in a resource bundle. To create a resource bundle, you need to create either a Java class that extends ListResourceBundle or a properties file that contains static strings. For each locale you want to support, you provide a separate version of the resource bundle.

You can use resource bundles to implement localization in conditions, actions, and associated BeanInfo classes. Here is a recommended approach:

  1. Create resource bundles for all locales supported by your application.

  2. In your condition, action, or BeanInfo class, declare ResourceBundle objects that reference your resource bundles by calling the EboResourceBundle.getBundle() method. This simplest version of this method takes one argument, the fully qualified name of your resource bundle class.

    For example: if your resource bundle is MyBundle.class located in package locales, your declaration should look like this:

      static com.sssw.re.util.EboResourceBundle eoCABundle = com.sssw.re.util.EboResourceBundle.getBundle("locales.MyBundle")
    
  3. Access the data in the resource bundle by calling get methods on the ResourceBundle object.

    For example: if your resource bundle contains localized strings, call the getString() method on the ResourceBundle object, passing the key associated with the desired string value.

NOTE:   For complete information on creating resource bundles, see the reference documentation for the ResourceBundle, ListResourceBundle, and PropertyResourceBundle classes in the Java standard API documentation. All these classes are in the java.util package.

Example of a BeanInfo class

This is the code that defines the BeanInfo class for the installed condition CheckTime:

  package com.sssw.re.condition;
  import com.sssw.re.core.*;
  import java.beans.*;
  
  public class CheckTimeBeanInfo extends EboBeanInfo {
  
  	 // Resource Bundle definition
      static com.sssw.re.util.EboResourceBundle eoCABundle =
        com.sssw.re.util.EboResourceBundle.getBundle( EboConstant.CA_BUNDLE,
          getClass().getClassLoader() );
          
  	 public Class getClazz() {
  	 	 return com.sssw.re.condition.CheckTime.class;
  	 }
  
  	 public PropertyDescriptor[] getPropertyDescriptors() {
  	 	 PropertyDescriptor[] pds = new PropertyDescriptor[ 2 ];
  	 	 PropertyDescriptor pd;
  	 	 pd = super.getPropertyDescriptor( "From" );
  	 	 pd.setDisplayName( eoCABundle.getString( "From" ) );
  	 	 pds[ 0 ] = pd;
  	 	 pd = super.getPropertyDescriptor( "To" );
  	 	 pd.setDisplayName( eoCABundle.getString( "To" ) );
  	 	 pds[ 1 ] = pd;
  	 	 return pds;
  	 }
  
  }

Notes on the BeanInfoClass example

Getting the resource bundle   Uses EboResourceBundle.getBundle() to get the name of the resource bundle for the installed conditions and actions stored in CA_BUNDLE and get the class loader.

Defining property descriptors   Since the CheckTime condition defines two properties, it requires two property descriptors—as indicated in the array declaration.

Specifying a display name for a property   You can use the setDisplayName() method to specify a property name. When the Rule Editor generates the property panel, it uses this display name as the label for the UI control associated with the property. The setDisplayName() method is called on the property descriptor and takes a string argument. This example shows the recommended approach for implementing resource bundles. It uses key values To and From—passing them to the resource bundle so that the Rule Editor displays a localized string.



Copyright © 2004 Novell, Inc. All rights reserved. Copyright © 1997, 1998, 1999, 2000, 2001, 2002, 2003 SilverStream Software, LLC. All rights reserved.  more ...