bea.com | products | dev2dev | support | askBEA

Download Docs | Site Map | Glossary
Search

Development Guide

Using the Expression Package

This topic illustrates how to use the services of the Expression package. The Expression package is part of the Personalization and Interaction Management features in WebLogic Portal. The Expression package allows you to externalize calculations, business policies, decision trees, and other operations from your Java code.

This section includes information on the following subjects:

- What Is the Expression Package?

- Assembling and Managing Expressions

- Working with Expressions

- Configuring the Expression Package

What Is the Expression Package?

As previously mentioned, the Expression package allows you to externalize business logic or formulas from your Java code. Using the Expression package, any arithmetic, boolean, relational or conditional statement can be represented. You can use the Expression package to dynamically assemble and evaluate your own business logic.

An example of Expression package use is a rental car agency using it for calculating rental costs, which may change frequently. Rather than expressing the calculation using Java statements, the calculation can be externalized from the Java code into an XML document and interpreted at run time.

WebLogic Portal provides an Expression example. To see this example, take the following steps:

1. Start the Personalization server, as follows:

   Start —> BEA WebLogic Platform 7.0 —> WebLogic Portal 7.0 —> Portal Examples —> Personalization Examples —> Launch Personalization Server

2. After the Personalization server is running, start the Personalization Examples, as follows:

   Start —> BEA WebLogic Platform 7.0 —> WebLogic Portal 7.0 —> Portal Examples —> Personalization Examples —> Start Personalization Examples

   A browser window opens showing the Personalization Examples index, as shown in Figure 16-1.

   Figure 16-1 Personalization Examples Index

3. If you are not logged in or have not created a user, click Please visit the User Login example first. A page opens where you can either log in or go to another page to create a user.

4. After you have logged in or created a user, in the left column of the page, select Expressions —> Execute Expression. The Execute Expression Example page opens, as shown in Figure 16-8.

   Figure 16-2 Execute Expression Example

   This window shows a simple application of the Expression package, where the parameters are set using the drop-down lists.

5. Click View Source in the left column. A new page opens showing the JSP source, as shown in Figure 16-3.

   Figure 16-3 JSP Source

   This page shows the JSP source (exec_expression.jsp) for the Expression Example. After the expression is executed (and a parameter is changed), the results are contained in the exec_expression_results.jsp. Both JSP files are located in the <BEA_HOME>\weblogic700\samples\portal\p13nDomain\beaApps\p13nApp\p13n directory.

   The next step provides information about how the expression works.

6. On the Execute Expression Example page, click the How does it work? link. A a new browser window opens that describes the example, as shown in Figure 16-4.

   Figure 16-4 How Does It Work? Execute Expression

   This page describes how the Expression Example works. It also suggests an exercise to further your understanding of expressions.

7. On the Execute Expression Example page, click the Preview Expression XML button. A page showing the XML appears, as shown in Figure 16-5.

   Figure 16-5 Preview Expression as XML

   This page shows the Expression Example XML before it is executed.

The next section discusses the differences between the Expressions package and the Rules Framework.

Using Rules or Expressions

One of the applications of the Rules Manager is to use business rules to match users and groups with appropriate content. The Rules Manager, like the Expressions package, is part of WebLogic Portal Personalization and Interaction Management.

The most important difference between the Expression package and the Rules Engine is that the Expression package uses named variables, while the Rules Engine does not. Additionally, the Rules Manager uses rule sets, where one rule can trigger another, that is, the rules can cascade.

In general, you use expressions when you want to bind variables to values (usually only one) and rules where you need pattern detection and want to evaluate all possible bindings to variables.

The Rule Engine has extremely powerful pattern matching and inferencing capabilities. However, these capabilities may come with a performance penalty. If you find yourself repeatedly executing a named rule, consider converting the rule to an expression. If you do not leverage the inferencing capabilities of the Rule Engine or rarely have more than one potential variable-value binding, then use expressions.

You should carefully evaluate the performance differences between using expressions, supplying an explicit binding between variables and values through a UnificationList or custom Unifier, and using the Rule Engine to explore all potential bindings.

Table 16-1 shows some examples of when to use rules and expressions.

Table 16-1 Expressions vs. Rules

Feature | Expressions | Rules
--- | --- | ---
Externalize business logic from Java code | Yes | Yes
Rapid deployment of business logic independent of application code | Yes | Yes
Non programmers can assemble business logic using JSP or Swing GUI | Yes | Yes
Inferencing capability | No | Yes One rule firing can cause another rule to be fired.
Explicit binding of value to variables | Yes | No Values are bound to variables using class type. All possible bindings are automatically tested.
Long-lived persistence of business logic | Yes | Yes Business logic is persisted as XML documents. The XML Schema can provide independence from the Java code.
Business logic can be passed between processes | Yes | Yes XML documents defining business logic can be serialized or passed between Web Services.
XML Parsing cache | No | Yes RulesManager implements a TTL cache for ruleset documents.
Expressions cache and optimization | Yes | Yes The Rule Engine uses the Expression package internally, and hence leverages many of its underlying optimizations.

To see an example of rules, take the following steps:

1. In the Personalization Examples window, select Rule —> Rules Manager. The Rules Manager Example appears, as shown in Figure 16-6.

   Figure 16-6 Rules Manager Example

   This page shows an example of rules. This example demonstrates that the action of one rule will cause the condition of another rule to become satisfied. This ability is not present in the Expression package.

2. For more explanation, click the How does it work? link. The explanation appears, as shown in Figure 16-7.

   Figure 16-7 How Does It Work? Rules Manager

Expression Package Classes

The Expression package allows users to dynamically assemble and execute XML-based expressions. The package defines a set of Java classes that represent various types of expression operators, and contains services for evaluating expressions consisting of instances of these operators.

The Expression package includes a base Expression class, a Variable class, and the following operator classes for operating on Expressions and Variables:

- Basic language operators (object creation, method call, and so on.)

- Logical operators

- Comparative operators

- Collection operators

- Mathematical operators

- String operators

The Expression package also includes the following services for operating on expressions:

- Unifier—prepares an expression for evaluation.

- Validator—validates that an expression is well-formed before evaluation.

- Optimizer—optimizes the structure of an expression before evaluation.

- Evaluator—evaluates an expression and returns the result of evaluation.

- Executor—an aggregate service that combines the unification, validation, and evaluation processes.

Unlike an expression written directly in Java and executed from within a Java program, the Expression package allows you to dynamically assemble and modify expressions from within your Java programs. An expression may be modified any number of times both before and after evaluation. When you assemble expressions using the Expression package you can also take advantage the advanced features of the Expression package, such as expression caching, validation, and optimization.

The Expression package serves as the foundation of the BEA Rules Engine. The Rules Engine leverages the package in order to represent and evaluate rule condition and action expressions. Likewise, you can use the Expression package to dynamically assemble and evaluate your own business logic.

The Package Structure for the Expression Package

The Expression package interfaces and abstract classes can be found in the following package: com.bea.p13n.expression

The Expression package operators are organized in the following packages:

Basic language operators—com.bea.p13n.expression.operators

Logical operators—com.bea.p13n.expression.operators.logical

String operators—com.bea.p13n.expression.operators.string

Mathematical operators—com.bea.p13n.expression.operators.math

Comparative operators—com.bea.p13n.expression.operators.comparative

Collection operators—com.bea.p13n.expression.operators.collection

The Expression package related classes are packaged in the p13n_util.jar archive.

Assembling and Managing Expressions

Before you can begin using expressions, you must first learn how to programmatically assemble them using the various operator classes provided in the Expression package.

An expression is represented as a tree, where each node is another expression itself or a plain Java object. Expression trees are assembled in a bottom-up manner; a child expression or Java object is first created, and then added to a parent expression.

Figure 16-8 illustrates the steps required to build an expression tree.

- The first step in the expression assembly process is to create one or more child operators or Java objects.

- Next, a parent operator is created by supplying the child operators or Java objects to the parent operator's constructor.

- This process of creating subexpressions continues until the entire expression is assembled.

Figure 16-8 Building an Expression Tree

Maintaining Parent-child Relationships

Each of the operator classes defined in the Expression package extends a common base class that contains the necessary logic for maintaining parent-child relationships; therefore, you do not have to worry about maintaining these relationships while assembling expressions. However, it is possible to modify the structure of an expression after it has been created.

Table 16-2 shows the operators provided in the Expression interface for adding, modifying, or removing subexpressions in an expression.

Table 16-2 Methods for Building an Expression Tree

Java Method | Description
--- | ---
>     addSubExpression
| Adds a child (can be a subexpression) to an expression object.
>     removeSubExpression
| Removes an object (can be a subexpression) of the expression object.
>     setSubExpression
| Replaces existing object (of an expression) by the given object (can be a subexpression).
>     getSubExpression
| Can be used to access the children of an expression object.
>     getParent
| Can be used to access the parent expression of an expression object.

For more information about the Expression interface, see the Javadoc.

Managing the Expression Cache

The expression interface also includes methods to manage the caching of results. The result of evaluating an expression may be cached in each expression object. When the cache is enabled for an expression, trying to evaluate the same expression a second time will return the cached value.

Note: By default, caching is turned off. You may want to keep the cache turned off for some operators, such as MethodCall.

Table 16-3 shows the methods provided in the Expression interface to manage the caching of results.

Table 16-3 Methods to Manage Caching of Results

Java Method | Description
--- | ---
>     setCacheEnabled
| Can be used to enable or disable the cache for an expression.
>     isCacheEnabled
| Can be used to check if the cache is enabled for an expression.
>     isCached
| Can be used to check if a result is currently cached for an expression.
>     getCachedValue
| Can be used to get the current cached result of evaluating the expression.

For more information about the Expression interface, see the Javadoc.

Working with Expressions

After you have assembled an expression, you are ready to work with it using the various Expression package services. These services allow you to prepare an assembled expression for evaluation, validate that the expression is well-formed, optimize its structure, and finally, evaluate the expression.

The following information is presented in this section:

- The Expression Factory

- Expression Package Services

- Unification Service

- Optimization Service

- Validation Service

- Evaluation Service

- Execution Service

The Expression Factory

The ExpressionFactory provides methods to create the various Expression package services and data structures used by these services.

For example, the following method will create an instance of the Validator service:

>     ExpressionFactory.createValidator(null);

For more detail on how to construct the various Expression package services, see the Javadoc.

Expression Package Services

The Expression package offers services which can be used on any expression that is built using the operators in the Expression package.

Unification Service

The Unifier is used to unify variables (assign values to variables) present in an expression. The Unifier uses a data structure known as a UnificationList that stores the variable name and the corresponding value of the variable. Like the Unifier, the UnificationList instances are created via the ExpressionFactory. The Unifier gets the value from the list for a particular variable using the variable name as a key to search the UnificationList, and binds the retrieved value to the variable.

For more information about the Unifier interface and the ExpressionFactory class, see the Javadoc.

Optimization Service

The Optimizer is used to optimize an expression. The default optimization algorithm used by the Optimizer is shown below.

- Traverse an expression tree and add each unique subexpression to a list.

- If a subexpression is equal to an expression present in the list, then replace it with a proxy expression. The proxy expression delegates to the original expression.

For more information about the Optimizer interface and the ExpressionFactory class, see the Javadoc.

Validation Service

The Validator is used to validate an expression. The default validation algorithm used by the Validator is as follows:

For each operand of an operator:

- Get the required type of the operand.

- If the operand is an expression, evaluate the expression and compare the type of the result with the required type; otherwise, assert that the operand is of the required type.

- If the type does not match or an error occurs during the evaluation of an operand expression, the Validator throws an InvalidExpressionException . An UnboundVariableException is thrown if any variables in an expression are not bound to a value.

The Validator can be used in a stateless or stateful mode. In stateless mode, any expression evaluations necessary to perform validation will be executed in stateless mode.

For more information about stateless and stateful evaluation modes, see the Evaluation Service section below.

For more information about the Validator interface and the ExpressionFactory class, see the Javadoc.

Evaluation Service

The Evaluator is used to evaluate an expression. An expression can be evaluated in stateful or stateless mode:

Stateful mode

In this mode, the value of each variable that appears in the expression is determined by retrieving the value set within the variable.

In other words, stateful mode relies upon the expression having been previously unified by a Unifier.

When an expression is evaluated in stateful mode and results caching is turned on, the results of evaluation will be cached within the expression.

Stateless mode

In this mode, the value of each variable that appears in the expression is determined by looking up a value that is bound to the name of the variable in an external data structure.

In other words, the evaluation process does not rely upon state associated with the expression, and as such, does not require the expression to be unified before evaluation.

The data structure that contains the name-value mappings for variables is known as a UnificationList and is associated with the Evaluator. Like the Evaluator, the UnificationList instances can be created using the ExpressionFactory.

A side effect of stateless mode is that expression evaluation cannot take advantage of results caching.

You can use of stateful mode in a situation where an expression need only be evaluated within a single thread of execution. In the case of multithreaded evaluation of a single expression, you must use stateless mode.

Note: If an expression does not contain variables, then there is no difference between the two evaluation modes.

For more information about the Evaluator interface and the ExpressionFactory class, see the Javadoc.

Execution Service

The Executor aggregates the Unification Service, Validation Service and Evaluation Service. The execute method on an Executor takes a Unifier, a Validator and an Evaluator to execute a cycle of unification-validation-evaluation operations.

The algorithm used by the Executor is shown below:

Unification

- If the Unifier is not null, unify the expression.

- If the Unifier is null, do not unify the expression.

Note: The Unifier should be null in the case where the expression passed to the Executor is already unified, or the expression is to be evaluated in stateless mode.

Validation

- If the Validator is not null, validate the given expression.

- If the Validator is null, ignore validation.

Evaluation

- If the Evaluator is not null, evaluate the expression in stateful or stateless mode. (depending on the type of evaluator passed.)

- If the Evaluator is null, the Executor throws an IllegalArgumentException.

- Return the result.

Note: If the Evaluator passed is stateless, then the Unifier should be null.

For more information about the Executor interface and the ExpressionFactory class, see the Javadoc.

Code Examples

This section contains examples that illustrate how to construct expressions programmatically and use the Expression package services.

This section contains the following four code examples:

- Stateful Evaluation of a Simple Expression

- Stateful Evaluation of an Expression Containing Variables

- Stateless Validation and Evaluation of an Expression Containing Variables

- Stateful Validation and Evaluation of an Expression Containing Variables

Stateful Evaluation of a Simple Expression

A logical expression is constructed and executed in stateful mode. The expression does not contain any variables.

Listing 16-1 Example

The source code for creating and executing the expression is shown below:

>     Expression expression = new LogicalAnd(Boolean.TRUE, Boolean.FALSE);

>     // Prepare for creating an executor by creating a stateful
>     // evaluator. Since the expression does not contain variables,
>     // we are not using a validator or a unifier in this example,
>     // so we will not create them.

>     // null is passed for the environment Map.

>     Evaluator evaluator = ExpressionFactory.createEvaluator(null);

>     // null is passed for the environment Map.

>     Executor executor = ExpressionFactory.createExecutor(null);

>     // Execute the above expression by passing null for both the unifier
>     // and validator parameters.

>     Object result = executor.execute(expression, null, null, evaluator);

>     // The result should be Boolean.FALSE.

Stateful Evaluation of an Expression Containing Variables

An expression containing variables is constructed and evaluated in stateful mode.

Listing 16-2 Example

The source code for creating and executing the expression in stateful mode is shown below.

>     // Create a variable that can store an object of type Boolean
>     // and whose name is "?booleanVariable".

>     Variable booleanVariable = new Variable("?booleanVariable", Boolean.class);

>     // Now, we will use the variable that we created in the above step.

>     Expression expression = new LogicalAnd(Boolean.TRUE, booleanVariable);

>     // Next, we'll unify the expression by binding any variables
>     // present in the expression. In the above case, there is one
>     // variable in the expression so the variable needs to be assigned a
>     // value. This is shown below.

>     // Create a UnificationList to store the variable name and value as
>     // key-value pairs.

>     UnificationList unificationList = ExpressionFactory.createUnificationList(null);

>     UnificationList.addObject("?booleanVariable", Boolean.FALSE);

>     // Create a unifier.

>     Unifier unifier = ExpressionFactory.createUnifier(null, unificationList);

>     // Prepare for creating an executor by creating a stateful
>     // evaluator. We are not using a validator in this example,
>     // so we will not create one.

>     // null is passed for the environment Map.

>     Evaluator evaluator = ExpressionFactory.createEvaluator(null);

>     // null is passed for environment Map.

>     Executor executor = ExpressionFactory.createExecutor(null);

>     // Execute the above expression by passing a unifier and a null
>     // validator.

>     Object result = executor.execute(expression, unifier, null, evaluator);

>     // The result should be Boolean.FALSE.

Note: The expression can be unified before calling the execute method by calling the unify method on the Unifier. Once the expression is unified there is no need to pass a unifier to the execute method of the executor.

Stateless Validation and Evaluation of an Expression
Containing Variables

An expression containing variables is constructed and evaluated in stateless mode. The Validator service is also used to validate the expression.

Listing 16-3 Example

The source code for creating and executing the expression in stateless mode is shown below.

>     // Create a variable that can store an object of type Boolean
>     // and whose name is "?booleanVariable".

>     Variable booleanVariable = new Variable("?booleanVariable", Boolean.class);

>     // Now we will use the variable that we created in the above step.

>     Expression expression = new LogicalAnd(Boolean.TRUE, booleanVariable);

>     // Next, we'll unify the expression by binding any variables
>     // present in the expression. In the above case there is one
>     // variable in the expression, so the variable needs to be assigned
>     // a value. This is shown below.

>     // Create a UnificationList to store the variable name and value as
>     // key-value pairs.

>     UnificationList unificationList = ExpressionFactory.createUnificationList(null);

>     UnificationList.addObject("?booleanVariable", Boolean.FALSE);

>     // Prepare for creating an executor by creating a stateless
>     // evaluator. We are not using a unifier in this example,
>     // so we will not create one.

>     // Creating a stateless evaluator by passing null for the
>     // environment Map and the UnificationList.

>     Evaluator evaluator = ExpressionFactory.createEvaluator(null, unificationList);

>     // Creating a stateless validator.

>     Validator validator = ExpressionFactory.createValidator(null, evaluator);

>     // Creating an executor.

>     Executor executor = ExpressionFactory.createExecutor(null);

>     // Execute the above expression by passing null for the unifier and
>     // a non-null validator.

>     Object result = executor.execute(expression, null, validator, evaluator)

>     // The result should be Boolean.FALSE.

>     // After calling execute method, the given expression will not be
>     // modified by any services that were used above.

>     // The stateless execution mode is useful if an expression is shared
>     // between multiple threads.

Stateful Validation and Evaluation of an Expression
Containing Variables

An expression containing variables is constructed and evaluated in stateful mode. The Validator service is also used to validate the expression.

Listing 16-4 Example

The source code for creating and executing the expression in a stateful mode is shown below.

>     // Create a variable that can store an object of type Boolean
>     // and whose name is "?booleanVariable".

>     Variable booleanVariable = new Variable("?booleanVariable", Boolean.class);

>     // Now we will use the variable that we created in the above step.

>     Expression expression = new LogicalAnd(Boolean.TRUE, booleanVariable);

>     // Next, we'll unify the expression by binding any variables
>     // present in the expression. In the above case, there is one
>     // variable in the expression, so the variable needs to be assigned
>     // a value. This is shown below.

>     // Create a UnificationList to store the variable name and value
>     // as key-value pairs.

>     UnificationList unificationList = ExpressionFactory.createUnificationList(null);

>     UnificationList.addObject("?booleanVariable", Boolean.FALSE);

>     // Create a unifier.

>     Unifier unifier = ExpressionFactory.createUnifier(null, unificationList);

>     // Prepare for creating an executor by creating a stateful
>     // evaluator and validator.

>     // null is passed for the environment Map.

>     Evaluator evaluator = ExpressionFactory.createEvaluator(null);

>     // null is passed for the environment Map.

>     // Creating a validator.

>     Validator validator = ExpressionFactory.createValidator(null);

>     // Creating an executor.

>     Executor executor = ExpressionFactory.createExecutor(null);

>     // Execute the above expression by passing a unifier and a non-null
>     // validator.

>     Object result = executor.execute(expression, unifier, validator, evaluator);

>     // The result should be Boolean.FALSE.

Note: The expression can be unified before calling the execute method by calling the unify method on the Unifier. Once the expression is unified there is no need to pass a unifier to the execute method of the Executor. The validation service can be used directly by calling the validate method. The validate method throws an InvalidExpressionException if the given expression is invalid.

Configuring the Expression Package

The expression.properties file contains configuration settings for the Expression package and should be modified with care.

This file is archived in p13n_util.jar under the package com.bea.p13n.expression.

>     ##
>     # Expression Comparator null handling
>     #
>     # If the following property is set to true the Expression
>     # Comparator will return false as the result of comparing
>     # any non-null value to a null, regardless of the
>     # comparison being performed.
>     #
>     # Defaults to true.
>     ##

>     expression.comparator.nullcheck=true

>     ##

>     # Expression Comparator equality epsilon.
>     #
>     # The following property determines the epsilon value for
>     # numeric equality comparisons.
>     #

>     # Defaults to 0.
>     ##

>     expression.comparator.epsilon=0.00001

>     ##
>     # Expression Introspector Method Array Caching
>     #
>     # If the following property is set to true the Expression
>     # Introspector will cache the array of Methods implemented by a
>     # Java Class.
>     #

>     # Defaults to true.
>     ##

>     expression.introspector.method.array.cache=true

>     ##
>     # Expression Introspector Method Caching
>     #
>     # If the following property is set to true the Expression
>     # Introspector will cache Methods by signature.
>     #
>     # Defaults to true.
>     ##

>     expression.introspector.method.cache=true

>     ##
>     # Expression Parser Node Support Classes
>     #
>     # This property supports a comma-delimited list of classes
>     # extending the base AST NodeSupport class. Such classes
>     # provide node creation support for expression-schema namespaces
>     # required for constructing the intermediate AST representing a
>     # given Expression instance.
>     #
>     # All NodeSupport subclasses must co-exist peacefully with the
>     # required CoreNodeSupport instance.
>     ##

>     parser.node.support.list=\

>     com.bea.p13n.expression.internal.parser.expression.ExpressionNodeSupport

>     ##
>     # Expression Parser Transform Visitor Class
>     #
>     # This property specifies the ExpressionTranformVisitor or
>     # subclass to be used for intermediate AST-to-Expression
>     # transformations.
>     #
>     ##

>     parser.transform=\

>     com.bea.p13n.expression.internal.parser.expression.ExpressionTransformVisitor