Jaffa Design Document
Bean Rules Engine
Based on Jaffa v2.0
Last Revised On 25-Aug-04
Jaffa Design Document
Bean Rules Engine
Revision 8
1 Contents
1
2
Contents .................................................................................................................. 1
Requirements .......................................................................................................... 2
2.1
2.2
2.3
2.4
2.5
2.6
2.7
2.8
2.9
3
Design ..................................................................................................................... 5
3.1
3.2
3.3
3.4
3.5
3.6
3.7
3.8
3.9
3.10
3.11
3.12
3.13
3.14
3.15
3.16
3.17
3.18
3.19
4
Variables ........................................................................................................................ 2
New Validations.............................................................................................................. 2
Reflect Rules in UI / Widgets ......................................................................................... 2
Performing Validations ................................................................................................... 3
Bean Life Cycle .............................................................................................................. 3
Organizing Rules and Bindings ...................................................................................... 3
Conditional Rules ........................................................................................................... 4
Unit Testing .................................................................................................................... 4
Re-use ............................................................................................................................ 4
Variables ........................................................................................................................ 5
Scripting ......................................................................................................................... 6
Default Values ................................................................................................................ 9
Defining Rules .............................................................................................................. 10
Defining Rule Sets........................................................................................................ 12
Binding Rules to Beans ................................................................................................ 13
Rule Inheritance and Variations ................................................................................... 13
Rule Variations ............................................................................................................. 14
Rules File Grammar / Syntax ...................................................................................... 17
Reflect Rules in UI / Widgets ....................................................................................... 18
Bean Life Cycle ............................................................................................................ 21
Injecting the functionality to the bean ........................................................................... 22
Loading and caching the bean rules ............................................................................ 24
Organization of Rule Definitions ................................................................................... 25
Specify rules via Annotations ....................................................................................... 27
Parsing and Validating Rules Files .............................................................................. 28
Conditional Rules ......................................................................................................... 29
Custom Scripting Triggers ............................................................................................ 29
Unit Testing .................................................................................................................. 30
Extension Points .................................................................................................... 31
Document Revision 5
Page 1 of 31
Jaffa Design Document
Bean Rules Engine
Based on Jaffa v2.0
Last Revised On 25-Aug-04
2 Requirements
2.1 Variables
2.1.1 Within both default values and validations we want to be able to globally
define and re-use values. We also need to derive values from various
external sources and use them in default values and rules
2.1.2 Give direct access (via an API) to the property values such that they can
be also used directly in the code.
2.1.3 Default Values
2.1.4 Any bean when constructed should allow any field to be initialized with a
default state. It should be able to use a variable value, a hard-coded value
or a combination
2.2 New Validations
2.2.1 Be able to specify a property is Read-Only
2.2.2 Be able to specify a property is Hidden (Unavailable)
2.2.3 Be able to specify a property a Candidate Key Validation
2.2.4 Be able to specify bean level validations, including the fields that validation
is relative to
2.2.5 Be able to specify a new vaildator definition without needing to refactor the
DTD
2.2.6
Be able to specify a field or bean rule based on an expression (using a
JSP 2.0 style expression language) Example ${field1} lt ${field2} &&
${field3} == true
2.2.7 Provide either a Java class as a rule implementation, or provide the rule
via a scripting language
2.2.8 Allow a rule to be designed that provides client-side web page validation
using javascript
2.3 Reflect Rules in UI / Widgets
2.3.1 For Strings and Numbers limit character input, and adjust the visual size of
the box
2.3.2 For strings apply the case conversion property
Document Revision 5
Page 2 of 31
Jaffa Design Document
Bean Rules Engine
Based on Jaffa v2.0
Last Revised On 25-Aug-04
2.3.3 Indicate Mandatory Fields
2.3.4 Hide fields that are hidden / unavailable
2.3.5 Make read only fields non-editable
2.3.6 Allow lookups to be associated to fields that have foreign object
validations
2.3.7 Allow rules to provide additional Browser based javascript validations. This
should be fired onBlur and onSubmit
2.4 Performing Validations
2.4.1 Be able to specify client-tier (JavaScript), web-tier (no DB access) or
business-tier
2.4.2 Provide optimal performance for executing core rules, comparable to ‘hard
coding’ the rules. Custom rules, and ones based on ‘Scripting’ should be
optimized for execution where applicable
2.4.3 Avoid re-validations (especially DB level) of rules without constraining how
and when the rule validations are invoked.
2.5 Bean Life Cycle
2.5.1 Need to determine the exact life cycle of when various things happen to
beans that are being managed by the rules engine.
2.5.2 Optionally provide control points at lifecycle events. Obviously with the use
of Aspects this is already achievable, but not that simple to do dynamically
to a deployed system.
2.5.3 Define when rules can be change, and how complex the initial build
process is, and the follow-on process of changing rules in a deployed
runtime environment
2.6 Organizing Rules and Bindings
2.6.1 Support Rule Inheritance and overloading at the bean and property level
2.6.2 Support variant rules with context based Inheritance and overloading
2.6.3 Allow for a rules file per bean, and keep it packaged with the bean it
applies to
2.6.4 Allow for variant rules files, or variant rules in files
Document Revision 5
Page 3 of 31
Jaffa Design Document
Bean Rules Engine
Based on Jaffa v2.0
Last Revised On 25-Aug-04
2.6.5 Allow for definition of global properties, these may be referenced from any
other rule
2.6.6 Allow for new rules definitions to be stored along side bean rules
2.6.7 Address the use of Markers, Annotations (JDK1.5) , Javadoc for defining
rules at the source code level
2.7 Conditional Rules
2.7.1 Apply/Ignore a rule or group of rules based on a specific Variation
2.7.2 Apply/Ignore a rule or group of rules based on a specific Property Value
2.7.3 Apply/Ignore a rule or group of rules based on a generic Expression
(Using the expression language)
2.7.4 Apply/Ignore a rule or group of rules based on access to a specific Role
2.7.5 Apply/Ignore a rule or group of rules based on access to a specific
Business Function
2.8 Unit Testing
2.8.1 Make sure it is simple to run multiple unit tests with different rule sets, so
functionality can be tested based on different settings
2.9 Re-use
2.9.1 Make this module of Jaffa re-usable outsize of the Jaffa framework. The
core of the rules engine should not depend on the persistence layer,
presentation layer or middleware. However this it is expected that any
validators that will be provided that need to access persistent objects, will
use the Jaffa persistence tier. This is a one way constraint as it is
expected that the Jaffa presentation tier will be modified to use the rules
engine, and therefore have a dependency on it.
2.9.2 All 3rd party products / projects used must have a LGPL or similar ‘free’
commercial usage license to be viable for use in the rules engine.
Document Revision 5
Page 4 of 31
Jaffa Design Document
Bean Rules Engine
Based on Jaffa v2.0
Last Revised On 25-Aug-04
3 Design
3.1 Variables
Requirements: 2.1.1
A variable can be defined in the rules engine, and then used in any subsequent
expression.
<variable [cached=’application | session’] name='literal'>expression</variable>
or
<variable [cached = ’application | session’]
( name='literal'
( environment = ‘literal’ |
jndiSource = ‘literal’ [field = ‘literal’] |
dataSource = ‘literal’ table = ‘literal’ field = ‘literal’
[where = ‘expression’] |
sessionAttribute = ‘literal’ [field = ‘literal’]
)
) |
propertyFile = ‘url’
/>
Where does the variable’s value come from?
Hard coded values (including expressions)
A JNDI object, or a field within that object
The database (by specifying a data source, a table and field to get the
value from, and a query on a the table should return one row)
An environment variable from the local OS
An Object in the Http Session, or a field within that object
A property file, where all entries in the property file are loaded as variables
The ‘cached’ attribute defines where the variable is cached, which effects when the
variable is initialized. Whether variables are pre-loaded, or loaded on demand,
this is an implementation detail, but in the design will specify how they are
cached. If a variable is cached at the application level, it is never re-evaluated,
and its value becomes a ‘global’ constant through out the application. Application
variables are shared by all user sessions.
A variable that is cached at the session level remains the same throughout a
user’s session and won’t change or be re-evaluated until the user starts a new
session. The variables stored in the session are not shared between users.
Variable declarations where no ‘cached‘ attribute is specified are not cached at all
and are evaluated each time they are referenced.
Document Revision 5
Page 5 of 31
Jaffa Design Document
Bean Rules Engine
Based on Jaffa v2.0
Last Revised On 25-Aug-04
Note: sessionAttribute variables cannot be defined using ‘application’ caching. Also
the HttpSession is not always available when session variables are referenced, so
all sessionAttribute variables will be initialized at the start of a request, unless they
are defined to be session scope, and have already been cached.
There will be a programmatic API that will flush all cached variables; this will be
used downstream if a ‘Rules Editor’ is developed, and also if any changes are
made to the property that identified where rules files are read from (See 2.6
Organizing Rules and Bindings)
Variants
A variable may be defined in either a ‘core’ rules file, or be defined as part of a
group of ‘variant’ rules file. All variant rules will override core rules values if the
variable name is the same.
Requirements: 2.1.2
public Object RulesManager.getVariable(String name);
public void RulesManager.flush(); // Clean all cached data
public void RulesManager.flushRules();// Clear all cached rules and expressions
public void RulesManager.flushProperties(); // Clear all cached property values
The name of the property in the getProperty should not include the “variable.”
Prefix used to reference properties in the scripting engine. The scripting engine
just does this to prevent naming conflicts between property values and other
objects exposed to the scripting engine.
3.2 Scripting
General
We plan to use the Jakarta Bean Scripting Framework
(http://jakarta.apache.org/bsf/) for evaluating any expressions. The exact syntax
of the expression language will be based on what specific scripting language is
used.
Within BSF there are scripting language options like
BeanShell (http://www.beanshell.org/)
Jython (http://www.jython.org/)
Groovy (http://groovy.codehaus.org/)
Examples of expressions with scripting languages
Expr : root.namedBean
Java : root.getNamedBean()
// root is a javabean (BeanC)
Document Revision 5
Page 6 of 31
Jaffa Design Document
Bean Rules Engine
Based on Jaffa v2.0
Last Revised On 25-Aug-04
Expr : root.namedBean.get("BF2")
Java : root.getNamedBean().get("BF2")
// namedBean is property on BeanC of type java.util.Map.
// and this gets the item names BF2 from the map
Expr : root.namedBean.get("BF2").beanA[5]
Java : ( (BeanA[]) root.getNamedBean().get("BF2") ).getBeanA()[5]
// the BF2 object is an array of beanA classes, and this gets the 6th one
Expr : root.namedBean.get("BF2").beanA[5].field1
Java : ( (BeanA[]) root.getNamedBean().get("BF2") ).getBeanA()[5].getField1()
// field1 is a property on beanA
Expr : root.get(1).namedBean.get(\"BF2\").beanA[5].field1
Java : ( (BeanC) root.get(1)).getNamedBean().get("BF2") ).getBeanA()[5].getField1()
// root is a java.util.List containing BeanC Objects
There will be a default scripting language for the Rule Engine. In cases where it
is not possible to specify what scripting language to be used, the default
language will be assumed.
Currently expressions for variables, default values and conditional rules will use
the default expression language.
This will be defined in framework.properties
framework.rules.defaultLanguage=beanshell
@todo - Does this add to many dependencies? How can you have a central
config file and still have low dependencies?
Variables
Variables will be made available within any scripting or expression evaluation
statements via a reference to ‘variable’. In the example we define a variable as
then use it in a business rule
<variable cached=”application” name=”passwordlength”>4</property>
<property-validator>
<name>passwordLength</name>
<error>[error.password.length]</error>
<server script="beanshell">
field != null && field.length >= variable.passwordlength
</server>
</property-validator>
Some scripting languages allow for values to be assigned to beans, this can be
dangerous for properties that are shared not only between requests but also
users. For this reason we must prevent any modification to any of the property
values when used in a bean scripting framework. For example, if we provide
these values to the scripting engine we must make sure all values are
Example of what will not be allowed
<variable cached=”application” name=”passwordlength”>4</variable>
Document Revision 5
Page 7 of 31
Jaffa Design Document
Bean Rules Engine
Based on Jaffa v2.0
Last Revised On 25-Aug-04
<bean class=”user”>
<script event=”onCreation”>
if(variable.defaultPassword==null)
variable.defaultPassword=”dummy”;
bean.password = variable.defaultPassword;
</script>
</bean>
Example of what is allowed and should be used with caution
<variable cache="session"
name="userProfile"
sessionAttribute="org.jaffa.presentation.UserSession"
field="userData.costCenter"
/>
NOTE: As we allow a variable to be defined based on a variable, we have the
potential for a recursive definition. This will be prevented as we will parse and
evaluate variables in a defined processing order, and therefore only previously
processed variables will be available. Hence the following should be possible
<variable name=”passwordMin”>4</variable>
<variable name=”passwordMax”>variable.passwordMin + 9</variable>
These should be defined in the same rules file, as there is no guaranteed load
order for rules files.
Accessing Beans
As scripts are used throughout the rules engine in various contexts, there are
some cases where there are more scriptable objects that just the variables.
Typically if a script is being run with respect to a given bean (typically in the case
of a <bean-validator>) then the bean is available to the scripting language as an
object of name ‘bean’.
For example
@todo
If a script is being run with respect to a given property within a bean (typically in
the case of a <property-validator>) then the property is available to the scripting
language as an object of name ‘property’.
For example
@todo
External Scripts
Examples of external scripts for different languages …
test.bsh
boolean testFunction() {
System.out.println("The bean available to the script: " + root);
System.out.println("Original value of Fieldd2 of the bean: " + root.field2);
root.setField2(99);
Document Revision 5
Page 8 of 31
Jaffa Design Document
Bean Rules Engine
Based on Jaffa v2.0
Last Revised On 25-Aug-04
System.out.println("Modified value of Fieldd2 of the bean: " + root.field2);
return true;
}
test.groovy
testFunction = {
System.out.println("The bean available to the script: " + root);
System.out.println("Original value of Fieldd2 of the bean: " + root.field2);
root.setField2(99);
System.out.println("Modified value of Fieldd2 of the bean: " + root.field2);
return true;
}
test.py
def testFunction():
print("The bean available to the script: " + root.toString())
print("Original value of Fieldd2 of the bean: " + root.field2.toString())
root.field2 = 99
print("Modified value of Fieldd2 of the bean: " + root.field2.toString())
return "true"
3.3 Default Values
Requirements: 2.1.4
Default values are values injected into a given object when that object is
constructed. Normally objects are instantiated via a constructor (Object o =
Class(…)), not via a factory (Object o = Factory.newObject(Class))
new
When defining a field in the rules engine we should support a construct along the
lines of
<bean class=”package.class”>
<property name=”field1”>
<default>expression</default>
…
</property>
</bean>
When a bean property is initialized, there should be two mechanisms to do this.
Firstly it should be possible to initialize the property by directly setting the value
on the internal member variable relating to this property. Alternatively the
property should be initialized via its related setXxx() method. Initialization via the
setXxx() method implies that it must pass the rules associated to the setter.
Initializing property via its member variable will be essential in scenarios where a
property has been defined as read-only or hidden in the rules engine, as using
the ‘set’ method in this case would cause an error.
The following will be the basic way to set a default value (via its setter)
<default>XXX</default>
Document Revision 5
Page 9 of 31
Jaffa Design Document
Bean Rules Engine
Based on Jaffa v2.0
Last Revised On 25-Aug-04
To set a default value via it member variable we need to specify it as an attribute.
This will be done as follows
<default member=’m_field1’>XXX</default>.
Initialization using Variables
If we are using a variable to set this property we can use the ‘variable’ attribute to
name the variable. This same effect could be achieved by using the variable in
an expression, but this approach allows us to bypass the use of the expression
language.
NOTE: Bypassing the expression language is for improved performance, if it can
be proven that there is no real performance gain taking this approach, this
attribute will be removed!
Via the setter
<default variable=”email.address”/>
or
<default script=”beanshell”>variable.email.address</default>
Via the member variable
<default member=”_email” variable=”email.address”/>
or
<default member=”_email” script=”beanshell”>variable.email.address</default>
Initialization using Scripting
As shown above in the example, if we need a more complex approach to
initialization we can use a scripting language. Here the above example has been
modified to do some string concatenation
<default script=”beanshell”>’me@’+variable.email.address</default>
or
<default member=”_email” script=”beanshell”>’me@’+variable.email.address</default>
In the above case the script attribute tells us to treat the value as a script, and
then also indicate to the scripting engine what language is being used.
3.4 Defining Rules
Bean Validator, based on a Java class with static and dynamic parameters
<bean-validator>
<name>my-other-rule</name>
<description>Compare Dates</description>
<script message=”End date must be after start date”>
${bean.endDate} empty || ${bean.endDate} >= ${bean.startDate}
</script>
</bean-validator>
<bean class="com.myapp.jobs.WorkOrderForm">
<my-other-rule/>
</bean>
Document Revision 5
Page 10 of 31
Jaffa Design Document
Bean Rules Engine
Based on Jaffa v2.0
Last Revised On 25-Aug-04
Bean Validator, based on Server scripting (beanshell)
<bean-validator>
<name>my-other-rule2</name>
<description>Compare Dates</description>
<input-param name=”startField”/>
<input-param name=”endField”/>
<script message=”${endField} must be after ${startField}”>
${bean.${endField}} empty || ${bean.${endField}} >= ${bean.${startField}}
</script>
</bean-validator>
<bean class="com.myapp.jobs.WorkOrderForm">
<my-other-rule2 startField=”jobStartDateTime” endField=”jobCompletedDateTime”/>
</bean>
Property Validator, based on a Java class with static and dynamic
parameters
<property-validator>
<name>abc-foreign-key</name>
<description>Generic Foreign Key validator</description>
<bean-level-rule>false</ bean-level-rule >
<static-param name='domainClassName'>com.myapp.ValidFieldValues</static-param>
<static-param name='fieldNameForTable'>ABC</static-param>
<static-param name='fieldNameForValue'>LegalValue</static-param>
<input-param name='fieldNameForField'/>
<class>org.jaffa.rules.GenericForeignKeyFieldValidator</class>
</property-validator>
Property Validator, based on a Browser validation and a Java Class
<property-validator>
<name>mandatory</name>
<description>Mandatory validator</description>
<bean-level-rule>true</bean-level-rule>
<execution-realm/>
<error>[error.required]</error>
<client-script>field.value != null && field.value != ''</client-script>
<class>org.jaffa.rules.fieldvalidators.MandatoryFieldValidator</class>
<method>validate</method>
</property-validator>
Property Validator, based on a Browser validation as some Server scripting
(beanshell)
<property-validator>
<name>minLength</name>
<bean-level-rule>false</bean-level-rule>
<execution-realm/>
<input-param name="value" errorArg="1"/>
<error>[error.minValue]</error>
<client-script>
field.value != null && field.value.length >= value
</client-script>
<server-script language="beanshell">
Document Revision 5
Page 11 of 31
Jaffa Design Document
Bean Rules Engine
Based on Jaffa v2.0
Last Revised On 25-Aug-04
field != null && field.length >= value
</server-script>
</property-validator>
@todo direct inline scripting with out defining it as a bean or property rule???
Should we?
<bean class = "org.jaffa.user.ui.UserForm">
<property name="employmentDate">
<my-custom-rule/>
</property >
or
<property name="employmentDate">
<script message=”Field ${field} can’t use futuristic date ${value}”>
${value} <= ${DateOnly.date}
</script>
</property >
Core Bean Validators
<candidate-key
ck-fields="emailAddress"
pk-fields="userId"
/>
Core Property Validators
<foreign-key
domain="org.jaffa.user.domain.CostCenter"
field="centerCode"
lookupComponent="Jaffa.User.CostCenterLookup"
lookupField="centerCode"
lookupRequestParams="centerCodeDd=BeginsWith&displayResultsScreen=true"
/>
<hidden/>
<readOnly/>
3.5 Defining Rule Sets
<rule-set name=”part-rules”>
<mandatory/>
<string length=20/>
<foreign-key domain=”Catalog” field=”part”/>
</rule-set>
<bean class=”item”>
<property “part”>
<apply-rules name=”part-rules”/>
<other rule/>
</property>
</bean>
Document Revision 5
Page 12 of 31
Jaffa Design Document
Bean Rules Engine
Based on Jaffa v2.0
Last Revised On 25-Aug-04
3.6 Binding Rules to Beans
Inheritence
- No bean rule inheritance
- Special property level rule <super/> if the property is defined
<bean class=”user.userForm”
extends-bean=”user.domain.User”
execution-realm=”presentation”>
// This overides the label and doesn’t inherit any rules
<property name=”userId” label=”User Acount no”/>
// This overides the label and inherits all rules
<property name=”userId” label=”User Acount no”>
<super/>
</property>
// This inherits all rules from a different field on the base bean
<property name=”name” extends-property=”userName”>
<super/>
</property>
// This prevents any inheritance of this field
<property name=”location”/>
// This inherits all rules from a different field on a different base bean
<property name=”name”
extends-bean=”user.domain.AccountInfo”
extends-property=”userName”>
<super/>
</property>
// This inherits all rules from a different field on the base bean
// and speified there execution order relative to the custom ones
<property name=”name” extends-property=”userName”>
<pre-check-rule/>
<super/>
<min-length length=4/>
</property>
3.7 Rule Inheritance and Variations
A bean can extend another bean so all its properties inherit from the other bean
properties. The main use for this is so the a presentation bean (aka a FormBean)
that use the same property names as persistent bean (aka a Domain Object) can
inherit those features and hence any widget based on the form bean can also
reflect those features to the end user.
Document Revision 5
Page 13 of 31
Jaffa Design Document
Bean Rules Engine
Based on Jaffa v2.0
Last Revised On 25-Aug-04
As the structure of a form bean may be completely different to that of the domain
object it is extending, the extends clause does not mean that the bean level rules
come across for this object
<bean class=”package1.class1” extendsClass=”package2.class2”>
</bean>
If a bean is extending another bean where there are differences in property
names between the beans, then a property can explicitly specify the name of the
property it wants to extend
<bean class=”package1.class1” extendsClass=”package2.class2”>
<property name=”myField” extendsProperty=”baseField”/>
</bean>
It is also possible without extending the whole bean, for a property to specifically
extent another property on another being by specify both the other bean and
property name.
<bean class=”package1.class1” extendsClass=”package2.class2”>
<property name=”myField”
extendsProperty=”baseField”/>
<property name=”myOtherField”
extendsClass=”package3.class3”
extendsProperty=”anotherField”/>
</bean>
3.8 Rule Variations
The rules engine will have the capability to deal with variant rule sets. It is up to
the implementation of Jaffa via the PortletFilter to determine whether a given
thread of execution uses a specific rules variation. This concept is to allow the
system to run based on ‘core’ rules, and in certain cases have variations
dynamically applied to them.
For example you have an application that different customer log into, based on
which customer logs in, they have different rules. In this case that may be
different credit limit, restricted access to some of the bean values, different labels
etc. You could choose to make the customer code the variation, and then set up
variation rules for each customer code.1
Variation rules are specified with an additional attribute in the root tag of the XML
rule file
<rules variation=”name”>
</rules>
Document Revision 5
Page 14 of 31
Jaffa Design Document
Bean Rules Engine
Based on Jaffa v2.0
Last Revised On 25-Aug-04
Each of the bean and property definitions in a variant rules file can choose as to
whether they override or extend the core rules already defined.
If they override, only the variant rules are executed, if they extend first the core
rules (if there are any) are executed, and then the variant ones are executed too.
Below shows various examples of this …
<rules variation=”CustomerA”>
<bean class=”package1.class1”>
<property name=”field1”>
<my-new-rule1/>
</property>
<my-new-rule2/>
</bean>
<bean class=”package2.class2” override=”true”>
<property name=”field1” override=”true”>
<my-new-rule1/>
</property>
<property name=”field2”>
<my-new-rule2/>
</property>
</bean>
</rules>
Pseudo Code for Execution of Rules for a property
validateProperty(String beanName, String propertyName)
{
String variation = ThreadLocal.getVariation();
BeanDescriptor coreBeanDescriptor = RulesEngine.getBeanInfo(beanName)
BeanDescriptor variantBeanDescriptor =
RulesEngine.getBeanInfo(beanName, variation)
PropertyDescriptor corePropertyDescriptor =
coreBeanDescriptor.getPropertyDescriptor(propertyName)
PropertyDescriptor variantPropertyDescriptor =
variantBeanDescriptor.getPropertyDescriptor(propertyName)
if (variantPropertyDescriptor exists &&
variantPropertyDescriptor overrides the corePropertyDescriptor)
{
executeRules(variantPropertyDescriptor)
} else if (variantPropertyDescriptor exists &&
variantPropertyDescriptor does not override the
corePropertyDescriptor)
{
executeRules(corePropertyDescriptor)
executeRules(variantPropertyDescriptor)
} else {
executeRules(corePropertyDescriptor)
}
}
executeRules(PropertyDescriptor propertyDescriptor)
{
foreach rule in propertyDescriptor.getRules() {
if (rule.getName() == "super") {
String extendsBean = propertyDescriptor.getExtendsBean()
if (extendsBean == null)
extendsBean = propertyDescriptor. getBeanDescriptor().
Document Revision 5
Page 15 of 31
Jaffa Design Document
Bean Rules Engine
Based on Jaffa v2.0
Last Revised On 25-Aug-04
getExtendsBean()
String extendsProperty = propertyDescriptor.getExtendsProperty()
if (extendsProperty == null)
extendsProperty = propertyDescriptor.getName()
if (extendsBean != null && extendsProperty != null)
validateProperty(extendsBean, extendsProperty)
} else {
execute rule on property value
}
}
}
Pseudo Code for Execution of Rules for a bean
validateBean(String beanName)
{
String variation = ThreadLocal.getVariation();
BeanDescriptor coreBeanDescriptor = RulesEngine.getBeanInfo(beanName)
BeanDescriptor variantBeanDescriptor =
RulesEngine.getBeanInfo(beanName, variation)
// execute rules for all the properties as defined in the variant file
foreach variantPropertyDescriptor in
variantBeanDescriptor.getPropertyDescriptors()
{
validateProperty(beanName, variantPropertyDescriptor.getName())
}
// execute rules for all the properties as defined in the core file,
// and which are not defined in the variant file
foreach corePropertyDescriptor in coreBeanDescriptor.getPropertyDescriptors()
{
if (variantBeanDescriptor.getPropertyDescriptor(
corePropertyDescriptor.getName()) == null)
{
validateProperty(beanName, corePropertyDescriptor.getName())
}
}
// NOTE: The bean-level override flag is merely to control the bean-level rules
// The property-level core rules will continue to be controlled via the
// override flag in the property definition
// execute the bean-level rules
if (variantBeanDescriptor exists &&
variantBeanDescriptor overrides the coreBeanDescriptor)
{
executeRules(variantBeanDescriptor)
} else if (variantBeanDescriptor exists &&
variantBeanDescriptor does not override the coreBeanDescriptor)
{
executeRules(coreBeanDescriptor)
executeRules(variantBeanDescriptor)
} else {
executeRules(coreBeanDescriptor)
}
}
executeRules(BeanDescriptor beanDescriptor)
{
// NOTE: We'll not execute the bean-level rules of the super class, if any
foreach rule in beanDescriptor.getRules()
execute rule on bean
}
Document Revision 5
Page 16 of 31
Jaffa Design Document
Bean Rules Engine
Based on Jaffa v2.0
Last Revised On 25-Aug-04
3.9 Rules File Grammar / Syntax
DTD for Bean and Property validators
ELEMENT property-validator
name, description?, bean-level-rule?, execution-realm?,
static-parameter*, input-parameter*, error?,
( client-script, client-global-script? )?,
( server-script | ( class, method? ) )
ELEMENT bean-validator
name, description?, execution-realm?, input-parameter*, static-parameter*,
error?, ( server-script | ( class, method? ) )
ELEMENT
ELEMENT
ELEMENT
ELEMENT
ELEMENT
ATTRIB
ATTRIB
ELEMENT
ATTRIB
ATTRIB
ELEMENT
ELEMENT
ATTRIB
ELEMENT
ELEMENT
ATTRIB
ELEMENT
ELEMENT
name #PCDATA
description #PCDATA
bean-level-rule #PCDATA (true | false) #PCDATA
execution-realm #PCDATA
static-parameter #PCDATA
static-parameter name
static-parameter errorArg
input-parameter #EMPTY
input-parameter name
input-parameter errorArg
error #PCDATA
client-script #PCDATA
client-script import
client-global-script #PCDATA
server-script #PCDATA
server-script language
class #PCDATA
method #PCDATA
Example Rules File
<rules>
<property name="domain"
environment="DOMAIN_NAME"
/>
<property cache="session"
name="myCostCenter"
sessionAttribute="org.jaffa.presentation.UserSession"
field="userData.costCenter"
/>
<bean class = "org.jaffa.user.domain.User"
executionRealm = "business">
<property name="emailAddress">
<mandatory/>
</property>
<property name="Password">
<mandatory/>
<string minLength='4'/>
</property>
<property name="costCenter">
<foreign-key domain="org.jaffa.user.domain.CostCenter"
field="centerCode"
lookupComponent="Jaffa.User.CostCenterLookup"
lookupField="centerCode"
Document Revision 5
Page 17 of 31
Jaffa Design Document
Bean Rules Engine
Based on Jaffa v2.0
Last Revised On 25-Aug-04
lookupRequestParams =
"centerCodeDd=BeginsWith&displayResultsScreen=true"
/>
</property >
<candidate-key
ck-fields="emailAddress"
pk-fields="userId"
/>
</bean>
<bean class = "org.jaffa.user.ui.UserForm"
extends = "org.jaffa.user.domain.User"
executionRealm = "presentation">
<property name="emailAddress">
<default>me@${domain}</default>
</property >
<property name="salary">
<hidden/>
</property >
<property name="costCenter">
<readOnly/>
<default member="m_costCenter">${myCostCenter}</default>
</property >
</bean>
</rules>
3.10 Reflect Rules in UI / Widgets
A new widget construct should be introduced to scope a collection of existing
widget to a given property. For Example
<Portlet:Form action=”aaa_bbb”>
<table>
<Portlet:Property field=’field1’>
<tr>
<td><Portlet:Label/></td>
<td><Portlet:EditBox/><Portlet:Lookup /></td>
<td><Portlet:Error/>
</tr>
</Portlet:Property>
<tr>
<td><Portlet:Label domain=”xx.yy” field=”zz”/></td>
<td><Portlet:EditBox field=”zz”/><Portlet:Lookup component=”xx” … … … /></td>
</tr>
</table>
The Editbox, Label and Lookup widgets when nested in a Property widget can
get the name of the field, and other properties about that widget from the new
outer Property widget.
The body of the tag of the Property widget will be ignored if the rules engine
defines this field as Hidden.
Document Revision 5
Page 18 of 31
Jaffa Design Document
Bean Rules Engine
Based on Jaffa v2.0
Last Revised On 25-Aug-04
The following widgets (Editbox, DateTime, Dropdown, Radio, CheckBox) will
generate a ‘display only’ version of themselves if the field they represent on the
form bean is defined as ‘Read-Only’. If one of these widgets is used out side the
context of a Property widget, and the rules engine defines that field as Hidden,
the widget will display a text string ‘(Restricted)’ in place of the widget
If a new <Portlet:Error field=””/> tag is introduced, that works by displaying errors
raised against this field, then it too can inherit the field name from the
<Portlet:Property> tag
Adding rules with browser based validations
At present only the EditBox supports adding in additional validation logic for
browser based validation. This is done via the onValidate tag attribute. It currently
complies with the following rules
a) there is a ‘isError’ Boolean variable that should be set if an error occurs
b) the current field element is available as a script variable ‘field’, its value
can be accessed by field.value.
c) the custom code is responsible for generating its own message if the
validation fails
d) the custom code MUST NOT execute a return statement
Currently the Editbox generates javascript to manage the automatic validations,
and custom widget level script code.
<Portlet:EditBox field='hoursFromGmt'
onValidate="if(field.value==1){alert('Not 1!');isError=true;}"/>
Will generate on the page the following code
<script type="text/javascript">
var user_timeZoneMaintenanceForm_hoursFromGmt_error = true;
var user_timeZoneMaintenanceForm_hoursFromGmt_tmp = "";
var user_timeZoneMaintenanceForm_hoursFromGmt_fired = false;
function user_timeZoneMaintenanceForm_hoursFromGmt_validate(field , mode) {
var isError = false;
if(field.value == user_timeZoneMaintenanceForm_hoursFromGmt_tmp &&
user_timeZoneMaintenanceForm_hoursFromGmt_error == false )
return false;
user_timeZoneMaintenanceForm_hoursFromGmt_error = false;
error = editBoxCheckNumeric(field,2,0,null,null,",",".");
if (error != "" && mode == "" &&
user_timeZoneMaintenanceForm_hoursFromGmt_fired == false)
{
user_timeZoneMaintenanceForm_hoursFromGmt_fired = true;
alert(error);
field.focus();
isError = true;
user_timeZoneMaintenanceForm_hoursFromGmt_error = true;
user_timeZoneMaintenanceForm_hoursFromGmt_fired = false;
} else {
if (error != "" && mode == "Global")
{
user_timeZoneMaintenanceForm_hoursFromGmt_fired = true;
alert(error);
Document Revision 5
Page 19 of 31
Jaffa Design Document
Bean Rules Engine
Based on Jaffa v2.0
Last Revised On 25-Aug-04
field.focus();
isError = true;
user_timeZoneMaintenanceForm_hoursFromGmt_error = true;
user_timeZoneMaintenanceForm_hoursFromGmt_fired = false;
}
}
try {
if (user_timeZoneMaintenanceForm_hoursFromGmt_fired == false) {
user_timeZoneMaintenanceForm_hoursFromGmt_fired = true;
//--onValidate ---if(field.value==1){alert('Not 1!');isError=true;}
//----------------user_timeZoneMaintenanceForm_hoursFromGmt_fired = false;
}
} catch (e) {
alert("Error When Running Custom Script for field hoursFromGmt");
}
return isError;
}
</SCRIPT>
<A id="user_timeZoneMaintenanceForm_hoursFromGmt_anchor">
<INPUT TYPE="text"
NAME="hoursFromGmtWV"
ID="user_timeZoneMaintenanceForm_hoursFromGmt"
CLASS="WidgetEditBox"
VALUE=""
SIZE="10"
MAXLENGTH="3"
STYLE=""
onBlur="javascript:editBoxTrim(this);
user_timeZoneMaintenanceForm_hoursFromGmt_validate(this,'');"
onFocus="javascript:user_timeZoneMaintenanceForm_hoursFromGmt_tmp =
this.value;">
The rules engine can augment this code with additional entries, in the above
case if we associate the mandatory and min-length functions to the field, they would
inject the following code before the final return isError statement
try {
if (user_timeZoneMaintenanceForm_hoursFromGmt_fired == false) {
user_timeZoneMaintenanceForm_hoursFromGmt_fired = true;
//--onValidate ---if(field.value==1){alert('Not 1!');isError=true;}
//--rule:mandatory-----if(!isError) {
isError = !eval("field.value != null && field.value != ''");
if(isError) alert("Hours From GMT is required");
}
//--rule:min-length-----if(!isError) {
var value = 4;
isError = !eval("field.value != null && field.value.length >= value");
if(isError) alert("Hours From GMT must be at least 4 characters");
}
user_timeZoneMaintenanceForm_hoursFromGmt_fired = false;
}
} catch (e) {
alert("Error When Running Custom Script for field hoursFromGmt");
}
Document Revision 5
Page 20 of 31
Jaffa Design Document
Bean Rules Engine
Based on Jaffa v2.0
Last Revised On 25-Aug-04
NOTE: Although this design section is based on changing the presentation tier
based on the Jaffa v2.x technology, the above design could equally be applied in
concept to JSF or other presentation technologies based around the web based
widgets/UI controls.
3.11 Bean Life Cycle
1. When a bean is constructed, and it is used by the ‘Rules Engine’, it will be
made to implement the IManagedBean interface, which will be implemented
via the ManagedBean class.
2. The information for this bean will be requested from the rules engine
(RulesEngine.getBeanInfo()). This will then create for each property that has
any rules against it, a list of those rules (RuleDescriptor[]). It will also keep a
list of all rules defined against the bean and properties, and for each, a
flag to indicate if it has been successfully executed. If a RuleDescriptor has a
‘RuleSet’ specified against it, and this rule set is disabled in the rules
engine, then that rule definition will not be included in this list.
3. On initial creation all these validations are set to false, a bean is born
sinful!
4.
Once the bean has bean created, we set all the default values, if a setter
is being used, this will fire off any related validations for the field. If any of
these fail, the creation of the bean will fail with a nested
VetoPropertyChangeException.
5. A property is considered valid if all rules that relate to that property have
been successfully executed. Note that only RuleDefinitions that are not
flagged as ‘isBeanLevelRule()’ will be tested for this property, the bean level
rules only get tested when isBeanValid() is invoked
6. A bean is considered valid if all rules in the bean has been successfully
executed
7. Invoking one of the isBeanValid() or isPropertyValid() methods, will attempt to
execute any related unsuccessful rule, and return true if all those rules
succeed
8. If the setter is called for a property, all non bean level rules, in the correct
execution realm are run, if all of these succeed, the property is updated,
else a VetoPropertyChangeException is generated, all other rules against this
property are flagged as invalid, so they will be revalidated next time a
isBeanValid() is invoked
Document Revision 5
Page 21 of 31
Jaffa Design Document
Bean Rules Engine
Based on Jaffa v2.0
Last Revised On 25-Aug-04
9. When a bean is created internally by the persistence layer, we assume
that the bean is created in a valid state (hence it can use the setBeanValid()
method after the object has been created)
A new property ‘ExecutionRealm’ has been added to a RuleDefinition. The realm can
be either ‘Presentation’, ‘Business’ or null. Null is interpreted as ‘All’ realms.
A bean will be defined within a given execution realm. Again if no realm is
specified it is assumes the bean is being used in ‘All’ realms and all rules will be
executed. If a realm is specified, only rules applicable to that realm are executed.
For example a <mandatory/> rule may be defined for ‘Presentation’, a <foreign-key/>
rule may be defined as ‘Business’. A Domain Object may not have a realm, and
want both the <mandatory/> and <foreign-key/> rules to be executed.
A form bean may extend the domain object, but only want to enforce the
presentation rules, as it does not have direct access to the database for business
rules, or does not want to repeat business rules that involve complex queries. In
this case the form bean can re-use all the rules on the domain object, but defined
its ExecutionRealm = ‘Presentation’ and hence only the <mandatory/> rule will be
executed, leaving the <foreign-key/> rule to the business tier.
Another new property for the RuleDefinition is the ‘isBeanLevelRule()’ allows field
level rules to be deferred until the bean level validation is executed. The classic
example of this is the <mandatory/> rule, which can really only be checked at the
bean level.
By injecting the ManagedBean functionality dynamically into plain java beans, this
new functionality is available at runtime by checking if the bean implements the
IManagedBean interface.
The possible automatic bean validation points in the Jaffa architecture are
Form Bean doValidate() method
In the middleware layer via a proxy for the transaction controller which will
validate any method input parameters that implement IManagedBean
In the persistence tier for any UOW add(), update() or delete() method
In the bean moulding framework in the case of the updateBean() or
deleteBean()
3.12 Injecting the functionality to the bean
The objective of any framework is to minimize the requirements and complexity
for the developer; this is not just for development, but also the build process and
test/debugging phase.
Document Revision 5
Page 22 of 31
Jaffa Design Document
Bean Rules Engine
Based on Jaffa v2.0
Last Revised On 25-Aug-04
To make development simple we will not put any mandatory constraints on the
development of the beans, the rules engine will be invisible to the developer. It is
not recommended that all business functionality be placed in a rules engine, only
business logic that is likely to be candidate for change. The will be all ways be
some performance impacts of an abstracted rules engine, so if that is a concern
code the logic directly.
The only consideration will make with respect to the developer needing to write
code for the rules engine, is in the way of using either Marker Interfaces or uses
Annotations/Javadoc to attach rules. This will be completely optional at this point.
The current approach to the rules engine is based on using Aspect Oriented
techniques to inject the rules engine functionality. This will involve identifying the
classes that need to have code injected as part of the project build phase. This
means any class that can have a rule defined on it down stream, needs to be
identified at build time. There are two schools of thought here, we enable all
beans, or we guess at a set of beans that this should apply to.
At this point the design will be based on the JBoss-AOP technology. In this case
there will be a jboss-aop.xml file which will define the beans that should be
‘prepared’ for use by the rules engine. JBoss-AOP allows these classes to be
identified either manually, or based on a Marker Interfaces, Annotation or Java
Doc tag.
This is the marker interface, which will cause the bean to be prepared for use by
the rules engine. They can also specify the javadoc tag @@jaffa.rules.Configurable
interface org.jaffa.rules.Configurable
This is the interface that will be added to a bean automatically by JBoss AOP
interface org.jaffa.beans.IManagedBean
boolean isModified(String propertyName)
void resetModified()
Object originalValue(String propertyName)
void addPropertyChangeListener(PropertyChangeListener listener)
void addPropertyChangeListener(String propertyName,
PropertyChangeListener listener)
void removePropertyChangeListener(PropertyChangeListener listener)
void removePropertyChangeListener(String propertyName,
PropertyChangeListener listener)
void hasListeners(String propertyName)
void firePropertyChange(String property, Object oldval, Object newval)
boolean isBeanValid()
boolean isPropertyValid(String property)
boolean setBeanValid(boolean vaild)
This is the implementation of the added interface which will be ‘mixed in’ with the
bean
org.jaffa.beans.ManagedBean implements IManagedBean
List getDependentRules(String property, String executionRealm)
Document Revision 5
Page 23 of 31
Jaffa Design Document
Bean Rules Engine
Based on Jaffa v2.0
Last Revised On 25-Aug-04
3.13 Loading and caching the bean rules
The following classes represent how for a given bean the rules for the bean will
be loaded. When these rules are parsed and loaded and how they are cached
will be address in the ‘bean life cycle’
Get information about a bean from the Rules Engine
BeanInfo RulesEngine.getBeanInfo(Class bean);
BeanInfo RulesEngine.getBeanInfo(Class bean, String Variation);
org.jaffa.rules.metadata.BeanInfo
BeanDescriptor getBeanDescriptor()
PropertyDescriptor[] getPropertyDescriptors()
Describe the properties and bean rules
org.jaffa.rules.metadata.BeanDescriptor (was ClassMetaData)
extends java.beans.BeanDescriptor
get/setExecutionRealm(String realm) [realm = ‘Presentation’,’Business’]
get/setClassName(Class className)
get/setProperties(PropertyDescriptor[] properties)
PropertyDescriptor getProperty(String name)
setProperty(String name, PropertyDescriptor property)
get/setRules(RuleDescriptor[] rules)
RuleDescriptor getRule(String name)
setRule(String name, RuleDescriptor rule)
//@todo inheritence? get/setExtends(BeanDescriptor extends)
Describe a property of a bean
org.jaffa.rules.metadata.PropertyDescriptor (was FieldMetaData)
extends java.beans.PropertyDescriptor
get/setLabel(String label)
get/setDefaultValue(Object defaultValue)
get/setDefaultMember(String memberVariable)
get/setRules(RuleDescriptor[] rules)
boolean hasRule(String name)
RuleDescriptor getRule(String name)
setRule(String name, RuleDescriptor rule)
//@todo inheritence? get/setExtends(PropertyDescriptor extends)
Describe a rule, at either the bean or property level
org.jaffa.rules.metadata.RuleDescriptor (was RuleMetaData)
get/setName(String name)
get/setRuleDefinition(RuleDefinition rule)
get/setParameter(String name, Object value)
Map getParameters()
boolean hasParameter(String name)
get/setRuleSet(String name)
Describe a rule definition for either a bean or property
org.jaffa.rules.metadata.RuleDefinition (was FieldValidatorMetaData)
get/setName(String name)
get/setClassName(String name)
get/setExecutionRealm(String realm)
is/setBeanLevelRule(boolean beanRule)
get/setParameter(String name, Object value)
Map getParameters()
//@todo add scripting fields
//@todo add expression cache
Document Revision 5
Page 24 of 31
Jaffa Design Document
Bean Rules Engine
Based on Jaffa v2.0
Last Revised On 25-Aug-04
3.14 Organization of Rule Definitions
The Rules Engine can be configured by setting the ‘rulesLocationList’ property.
/**
* This property holds the comma-separated list of urls for the files containing all
the rules.
* The default-value is 'classpath:///resources/rules.xml'
* Eg: 'classpath:///resources/rules.xml,file:///C:/customrules.xml,file:///xyz/**/*.xml'
* Ant-style expressions containg '*' and '**' will be supported for the 'file:///'
type url
* @param rulesLocationList The comma-separated list of urls for the files
containing all the rules.
*/
public void setRulesLocationList(String rulesLocationList)
In case, the Rules Engine is not configured through the invocation of the
setRulesLocationList() method, it’ll look for the framework property
‘framework.rules.location.list’ to determine the location for the files containing all
the rules.
# This property holds the comma-separated list of urls for the files containing all
# the rules. The default-value is 'classpath:///resources/rules.xml'
# Eg: 'classpath:///resources/rules.xml, file:///C:/custom-rules.xml,
file:///xyz/**/*.xml'
# Ant-style expressions containg '*' and '**' will be supported for the
# 'file:///' url protocol
framework.rules.location.list=classpath:///resources/rules.xml
A typical rules file will have the following structure. A JAFFA-powered application
will have a default set of rules. It can be customized by specifying variant rules
through the use of the ‘variation’ attribute. The variant rules can either extend or
override the default rules, through the use of the ‘override’ attribute. We can even
have variant <variable> and <rule-set>.
At this point, we don’t see any need for variant bean-validator and property-validator
definitions.
This is an example of the structure of a typical rules file
<rules>
<bean-validator>…</bean-validator>
<property-validator>…</property-validator>
<variable>…</variable>
<rule-set name=”xxx”>
<[rulename]/>
</rule-set>
<bean>
<property>
<[rulename]/>
<super/>
<[rulename]/>
<apply-rules rule-set=”xxx”/>
<script … />
</property>
<[rulename]/>
<script … />
<apply-rules rule-set=”xxx”/>
Document Revision 5
Page 25 of 31
Jaffa Design Document
Bean Rules Engine
Based on Jaffa v2.0
Last Revised On 25-Aug-04
</bean>
</rules>
This is an example of the structure of a typical variant rules file
<rules variation=”xx”>
<variable>…</variable>
<rule-set>
<[rulename]/>
</rule-set>
<bean [override=true]>
<property [override=true]>
<[rulename]/>
<super/>
<[rulename]/>
</property>
<[rulename]/>
</bean>
</rules>
To ease the management of rules at development time, we can write the rule
definitions in fragment files; these can be per module, component, class, etc.
These fragments will be merged into the main rules.xml file during the build
process, and therefore be in one large file at deployment (This is how we
manage other descriptors in Jaffa, like struts-config, tiles-config,
ApplicationResources)
The main rules.xml file may have the following structure.
<?xml version="1.0" encoding="ISO-8859-1"?>
<root>
<rules>
<bean-validator>…</bean-validator>
<property-validator>…</property-validator>
<variable>…</variable>
<rule-set>
<[rulename]/>
</rule-set>
<bean>
<property>
<[rulename]/>
<super/>
<[rulename]/>
</property>
<[rulename]/>
</bean>
</rules>
<!-- All the files named 'Rules.xfragment' will be merged in this comment block
//GEN-BEGIN:Rules.xfragment -->
<!-- //GEN-END:Rules.xfragment -->
</root>
The ‘configfileupdate’ ant-task can be used for merging the fragments during the
build process
Each rule, bean, definition, property will correlate to a source file and line number
so that parsing errors can be easily raised. However, we’ll not pin-point the
Document Revision 5
Page 26 of 31
Jaffa Design Document
Bean Rules Engine
Based on Jaffa v2.0
Last Revised On 25-Aug-04
source fragment for the definition, as these will have been merged into a single
file at deployment time.
3.15 Specify rules via Annotations
We’ll support the declaration of rules in the source files, via annotations after the
release of J2SE1.5
The following information is based on the J2SE1.5 Beta2 API.
A meta-annotation type will be created. This will serve as a marker for all the field
validator annotation types.
package org.jaffa.rules.fieldvalidators.annotations;
/** This is a meta-annotation type.
* It will be applied to the Annotation Type Declarations for all the different
field validators.
* Presence of this meta-annotation will help the tools to determine the field
validator Annotation Type Declarations
*/
@Retention(RetentionPolicy.CLASS)
@Target(ElementType.ANNOTATION_TYPE)
public @interface FieldValidator {}
Annotation Type Declarations will then have to be written for all the different field
validators
package org.jaffa.rules.fieldvalidators.annotations;
/** This is a annotation type declaration for indicating Mandatory properties.
*/
@Retention(RetentionPolicy.CLASS)
@Target(ElementType.METHOD)
@FieldValidator
public @interface MandatoryFieldValidator {}
/** This is a annotation type declaration for indicating String properties.
*/
@Retention(RetentionPolicy.CLASS)
@Target(ElementType.METHOD)
@FieldValidator
public @interface StringFieldValidator {
String pattern();
int length();
enum CaseType {MIXED, UPPER, LOWER} default MIXED;
int minLength();
}
/** This is a annotation type declaration for restricting the values to a given set
of values.
*/
@Retention(RetentionPolicy.CLASS)
@Target(ElementType.METHOD)
@FieldValidator
Document Revision 5
Page 27 of 31
Jaffa Design Document
Bean Rules Engine
Based on Jaffa v2.0
Last Revised On 25-Aug-04
public @interface InListFieldValidator {
String list();
String separator() default ",";
}
/** This is a annotation type declaration for enforcing the foreign-key check.
*/
@Retention(RetentionPolicy.CLASS)
@Target(ElementType.METHOD)
@FieldValidator
public @interface ForeignKeyFieldValidator {
String domainClass();
String property();
}
..
..
..
We’ll then apply the annotations in the source files.
APT (Annotation Processing Tool) will be used to generate rules.xml file from the
annotations in the source files.
An example application of annotation
import org.jaffa.rules.fieldvalidators.annotations.*;
Class X {
/**
*/
@MandatoryFieldValidator
@StringFieldValidator(
length = 20,
minLength = 4
)
@ForeignKeyValidator(
domainClass = "org.example.A",
property = "f1"
)
void setField1(String field1) {..}
3.16 Parsing and Validating Rules Files
Validation of rules.xml files
The file should have well-formed XML
The bean-validator should point to a valid class. Ensure that the
parameters are available on the class.
The property-validator should point to a valid class. Ensure that the
parameters are available on the class.
Perform the necessary checks on variable, ensuring that the required
parameters are passed for the different types.
Ensure that a rule-set contains valid rules
Document Revision 5
Page 28 of 31
Jaffa Design Document
Bean Rules Engine
Based on Jaffa v2.0
Last Revised On 25-Aug-04
A bean-level rule should point to a valid bean-validator. Ensure that the
parameters are available on the class
A property-level rule should point to a valid property-validator. Ensure that
the parameters are available on the class
A rule-set should contain valid rules of either property or bean level
Ensure that a reference to a rule-set is valid, and of the right type for its
context
The override attribute is to be specified only in variant rule declarations
3.17 Conditional Rules
If rules can be conditionally executed, then each rule can include a ‘condition’
attribute that will only cause that rule to execute if the expression it true. The
condition will be a scripting expression based on the default language of the Rule
Engine.
A condition on a rule can access and variables and either the property or bean
objects based on whether the rule is at the property or bean level
Conditional rules will be evaluated every time the rules engine needs to execute
a rule. If a condition is true, the rule is enforced, if it is false, the rule is ignored,
implying in the case of validation that this property or bean has passed that
validation.
3.18 Custom Scripting Triggers
Scripts can be placed at the property or bean level just like the rules, and they
will be executed in the order they are listed. The will also work based on the
same inheritance and variation logic that the bean and property rules work to.
<bean class=”package1.class1”>
<property name=”field1”>
<script language=”beanshell” event=”onSet”>
if(property == “”) property = “ABC”;
</script>
</propety>
<script language=”groovy” event=”onConstruction”>
bean.field1 = variable.defaultCode;
</script>
</bean>
Document Revision 5
Bean level events
onConstruction – executed once the bean has been constructed
and all the values initialized by the rule engine
Page 29 of 31
Jaffa Design Document
Bean Rules Engine
Based on Jaffa v2.0
Last Revised On 25-Aug-04
– executed when the isValid() method in called on a
bean, prior to executing of the validations
onValidation
Property level events
onSet – executed when the setter in called on a property, prior to
any of the rules engine validation logic being executed.
onChange – executed when a setter has been called, and the value
has been updated and all the ‘value changed’ property listeners
have been invoked
onValidate – executed when the isPropertyValid() method in called
on a property, prior to executing of the validations
onGet – executed when the getter is called on the property
3.19 Unit Testing
@todo…load new sets per test via RulesEngine.setRulesLocationList(String). Do
an implied flush() of cache
Allow rules to be defined in many XML files.
Each file has a ‘<rules>’ tag where all the rules are defined.
Add a new optional element to that called rule-set
By default all rules with no rule-set definitions are enabled by the Rules
Engine
Provide 2 new APIs for the rules engine
RulesEngine.setRuleSetEnabled(String name, boolean enabled)
RulesEngine.isRuleSetEnabled(String name)
Document Revision 5
It will be up to the unit test designer to develop rules for each test, into a
separated XML file, give the rules a rule set name, and in the setup() and
teardown() of the test enable and disable these rule sets.
Page 30 of 31
Jaffa Design Document
Bean Rules Engine
Based on Jaffa v2.0
Last Revised On 25-Aug-04
4 Extension Points
The current functionality in the ManagedBean implementation could allow
refactoring of the DomainDAO base class used in the bean moulding
framework.
Remove the perstence layer dependencies from the rules engine. No
reference to and org.jaffa.persistence.* classes. Currently the validators
are given a handle on a UOW, so they can all use the same one if
needed. Now they can internally create there own one if needed.
Look out phasing out all or some of the functionality in the
DomainObjectMeta classes. These hold many of the semantic attributes
associated to each domain field. Much of this can now be defined and
extended in the rules engine. We may also want to create an ‘rule file’
equivalent of the meta data as part of the domain object pattern.
If a property on a managed bean, is set on another managed bean can we flag
same rule is validated on target bean. If xx.field1 has had a foreign key validated
and we do yy.setField1(xx.getField1()) and yy.field1 also has the same validation,
can we prevent it from being fired.
Document Revision 5
Page 31 of 31
© Copyright 2026 Paperzz