Open Your Business Rules!
Rules-based
Operational Decision Services
External Rules Coming From GUI
Rule Project "ExternalRulesFromGUI"
OpenRules allows you to keep your business rules in Excel data tables that correspond to the columns (conditions and actions) of Excel's templates based on which the proper rule tables will be executed.Step 1. Defining GUI
Step 2. Defining Implementation ApproachStep 3. Setting Up Rule Templates
Step 4. Supporting Java Classes
Step 5. Creating Graphical Form Layouts in Excel
Step 6. Deploying and Executing the Web Application
Step 1. Defining Graphical User Interface
This project intends to create a web application that will consist of two parts:
1) Data input and Rule Engine Execution
2) Online Rules Editing
The view "Generate Customer Greeting" will allow to enter basic information about a customer and generate a greeting like "Good Morning, Mrs. Robinson!" based on the current time. Here is an example of the proper view:
By clicking on the button "Generate Greeting" a user could produce a new greeting in accordance with the latest greeting and salutation rules. By clicking on the buttons "Greeting Rules" a user will be taken to a web-based rule editor to modify the Greeting Rules:
By clicking on the buttons "Salutation Rules" a user will be taken to a web-based rule editor to modify the Salutation Rules:
This editors shows not how to make changes in the rule attributes but also allows a user to add rules by clicking on "the hyperlink "Add Rule" or to delete rules by clicking on the red cross.
Step 2. Defining Implementation Approach ►top
We will build this web application using OpenRules Forms by defining 3 Excel-based layouts for every of the above views and using navigation logic described as processing flow rules. We will deploy our application on the Tomcat server. As usual, we will create the following files:
File | Directory | Purpose |
HelloExternalRulesFromGUI.xls | ./war/rules/main | Describes the Environment table and the main method that will be executed during every interaction with a web client |
HelloForms.xls | ./war/rules/gui | Describes all screen layouts and processing flow rules |
Dialog.xls | ./war/rules/gui | The standard OpenRules file borrowed from the project openrules.forms.lib |
HelloData.xls | ./war/rules/data | Rule templates |
index.jsp | ./ | The entry point to this JSP-based web application |
What makes this application special is a need to reinitialize the rule engine that generates a greeting every time when the greetings and/or salutations have been modified. However, we do not have to reinitialize a rule engine associated with an already opened OpenRulesSession with all layouts and related rule tables. So, we have to carefully distributes greeting generation information and GUI information between two different rule engines making sure that re-initialization of the first engine dome really quickly.
When we start the application for the first time, we want to display the default rules (defined in an Excel file) and we also want to use the default data about a customer (defined in another Excel file).
In this implementation, we will define a special Java class HelloManager whose responsibilities will include these and other data management tasks. The manager will support two rule engines:
- A rule engine that reads the default
greeting and salutation rules from the file
war/rules/main/HelloDefaultRules.xls. Only this
engine will deal with greeting rules and rule templates
presented in the file
war/rules/logic/HelloTemplates.xls.
- A rule engine associated with the OpenRulesSession that will handle all GUI problems and will also read the default data about a customer from the Excel file HelloData.xls.
Thus, the entry point to our web application "index.jsp" will look as follows:
<%@ page import="com.openrules.forms.gui.jsp.*" %> <%@ page import="com.openrules.forms.*" %> <%@ page import="hello.rules.*" %>
<%@ page import="com.openrules.ruleengine.*" %> <% String s_attr = "openrules_session"; OpenRulesSession openrules_session = (OpenRulesSession) session.getAttribute(s_attr); if (openrules_session == null ) { // Create manager using data from HelloDefaultRules.xls String xlsMainRules = "file:./webapps/HelloExternaRulesFromGUI/rules/main/HelloDefaultRules.xls"; HelloManager man = new HelloManager(xlsMainRules); // Create OpenRulesSession using HelloExternaRulesFromGUI.xls String xlsMainForms = "file:./webapps/HelloExternaRulesFromGUI/rules/main/HelloExternaRulesFromGUI.xls"; openrules_session = new OpenRulesSession(xlsMainForms); session.setAttribute( s_attr, openrules_session); System.out.println("NEW SESSION based on " + xlsMainForms); man.setFormsEngine(openrules_session.getOpenRulesEngine()); // Read default rules and data from Excel files man.getDefaults(); Dialog dialog = openrules_session.getDialog(); dialog.put("manager",man); } %> <HTML><HEAD><TITLE>OpenRules</TITLE></HEAD> <body> <% System.out.println("PROCESS REQUEST"); openrules_session.processRequest(session, request, out); %> </body> </HTML> |
The first rule engine will be created by the constructor HelloManager(xlsMainRules), and the second rule engine automatically created by the OpenRulesSession will be set for the HelloManager by the statement:
man.setFormsEngine(openrules_session.getOpenRulesEngine());
The Environment table for the first rule engine is located in the file HelloDefaultRules.xls:
Environment | |
import.java | hello.rules.* |
include | ../logic/HelloTemplates.xls |
The Environment table for the second rule engine is located in the file HelloExternaRulesFromGUI.xls:
Environment | |
import.static | com.openrules.tools.Methods |
import.java | hello.rules.* |
include | ../gui/Dialog.xls |
../data/HelloData.xls | |
../gui/HelloForms.xls |
The main execution loop is implemented by the following method:
Method TableLayout main(Dialog dialog) |
HelloManager man = (HelloManager)
dialog().get("manager"); if (man == null) return fatalErrorLayout("HelloManager is not defined if index.jsp"); defineNextProcessingStep(man); if (dialog().errors == 0) { processingFlowRules(man); defineNextProcessingStep(man); } return mainLayout(); |
Step 3. Setting Up Rule Templates ►top
The business logic for producing greetings and salutations is presented in the Excel file HelloTemplates.xls. The first template
Rules void defineGreeting(App app, int hour) | ||
C1 | C2 | A1 |
min <= hour | hour <= max |
app.greeting = greeting; |
int min | int max | String greeting |
Hour From | Hour To | Set Greeting |
Unknown Greeting |
specifies how to define different greetings (Good Morning, Good Afternoon, etc.) based on the hour of the day. If the parameter "hour" belongs to the interval [min;max] defined by a concrete rule then the attribute "greeting" of the parameter "app" will be set to the proper greeting. If no rules are satisfied, this multi-hit table will use the default greeting "Unknown Greeting".
The second template
Rules void defineSalutation(App app, Customer c) | |||
C1 | C2 | C3 | A1 |
c.gender.equals(gender) | c.maritalStatus.equals(status) | c.age < age |
app.salutation = salutation; |
String gender | String status | int age | String salutation |
Gender | Marital Status | Age Less Than | Set Salutation |
Unknown Salutation |
specifies how to define different salutations (Mr., Mrs., etc.) based on customer attributes Gender, Marital Status, and Age. If no rules are satisfied, this multi-hit table will use the default salutation "Unknown Salutation".
Step 4. Creating Supporting Java Classes ►top
We define a Java package "hello.rules" with the following classes:
Customer (Customer.java)
- String name
- String gender
- String maritalStatus
- int age
App (App.java)
- Customer customer
- String greeting
- String salutation
GreetingRule (GreetingRule.java)
- int from
- int to
- String greeting
These classes are basic Java beans used inside rules and forms. To demonstrate the use of a more complex rule editor, we will implement the rule table for salutation rules as an OpenRules dynamic table. To do this we will define two classes:
SalutationRule implementes Checkable (SalutationRule.java)
- String gender
- String maritalStatus
- String maxAge
- String salutation
- HelloManager manager
and the class SalutationRules that extends DynamicTable (SalutationRules.java) by defining two methods:
public
String getHeaderLayoutName() {return "salutationsTableHeader";
}
public
String getRowLayoutName() {return "salutationsTableRow";
}
And finally the main Java class is a placeholder for all other objects:
HelloManager (HelloManager.java)
-
OpenRulesEngine ruleEngine
-
OpenRulesEngine formsEngine
-
GreetingRule[] greetingRules
-
SalutationRule[] defaultSalutationRules
-
SalutationRules salutationRules
-
App app
- ExternalRules externalRules
The object "ruleEngine" is defined in the constructor for the object "formsEngine" defined in the index.jsp. At the application initialization the manager executes the method "getDefaults":
public void getDefaults() { greetingRules = (GreetingRule[])ruleEngine.run("getDefaultGreetingRules"); defaultSalutationRules = (SalutationRule[])ruleEngine.run("getDefaultSalutationRules"); salutationRules = new SalutationRules(formsEngine); for (int i = 0; i < defaultSalutationRules.length; i++) { SalutationRule rule = defaultSalutationRules[i]; rule.setManager(this); salutationRules.addNewRow(rule); } createExternalRules(); externalRules.setModified(true); ruleEngine.log("There is " + getExternalRules().getRuleTables().size() + " external tables"); Customer customer = (Customer) formsEngine.run("getDefaultCustomer"); app = new App(); app.setCustomer(customer); } |
This method receives the greetingRules from the file HelloDefaultRules.xls using the method "getDefaultGreetingRules". It receives the defaultSalutationRules using the method "getDefaultSalutationRules" and then creates salutationRules to support the proper dynamic graphical table. Then it creates an instance of the type ExternalRules using this method:
public void createExternalRules() { String[][] greetingGrid = new String[greetingRules.length][3]; for (int i = 0; i < greetingRules.length; i++) { GreetingRule rule = greetingRules[i]; greetingGrid[i] = new String[] { Integer.toString(rule.from), Integer.toString(rule.to), rule.greeting }; } String[][] salutationGrid = new String[salutationRules.getRows().size()][4]; for (int i = 0; i < salutationRules.getRows().size(); i++) { SalutationRule rule = (SalutationRule)salutationRules.getRows().get(i); salutationGrid[i] = new String[] { rule.gender, rule.maritalStatus, rule.maxAge, rule.salutation }; } externalRules = new ExternalRules(); externalRules.addRuleTable( "greetingRules", //table name "defineGreeting", //template name greetingGrid); externalRules.addRuleTable( "salutationRules", //table name "defineSalutation", //template name salutationGrid); externalRules.setModified(false); ruleEngine.setExternalRules(externalRules); } |
And finally the manager creates the default application "app" with a customer received from the file HelloData.xls:
Data Customer customers | |||
customer.name | customer.maritalStatus | customer.gender | customer.age |
Customer Name | Marital Status | Gender | Age |
Robinson | Married | Female | 24 |
Smith | Single | Male | 19 |
Method Customer getCustomer() | |||
return customers[0]; |
Step 5. Creating Graphical Layouts in Excel ►top
All GUI realted forms and rules are descibed in the file HelloForms.xls. The mainLayout specifies a general layout for all three layout:
Layout TableLayout mainLayout() | ||
properties | width | 100% |
cellspacing | 4 | |
cellpadding | 2 | |
border | 1 | |
style | background-color:lightblue | |
dialog().nextLayout | ||
<a href="http://openrules.com"> <font size="1" face="Arial" color="blue"> OpenRules, Inc. </font></a> |
The layout "GenerateGreeting":
Layout TableLayout generateGreetingLayout(App app, Customer c) | ||
<h3>Generate Customer Greeting </h3> | ||
currentTime() | ||
"Name:" | [c.name] | |
"Age:" | [c.age] | |
"Gender:" | [c.gender]["Male,Female"] | |
"Marital Status:" | <F type="radio" >[c.maritalStatus]["Single,Married"] </F> | |
<hr/> | ||
actionButton("Generate Greeting"); | actionButton("Greeting Rules"); | actionButton("Salutation Rules"); |
"Generated Greeting:" | <strong><C> app.result </C></strong> |
There two layouts to support "GreetingRules":
Layout TableLayout greetingRulesLayout(HelloManager man) | ||
<h3>Greeting Rules</h3> | ||
<strong>Rules "Define Greeting" </strong> | ||
greetingRulesTable(man); | ||
actionButton("Save Changes"); | actionButton("Salutation Rules"); | actionButton("Generate Greeting"); |
Layout TableLayout greetingRulesTable(HelloManager man) | ||
<b>Hour From</b> | <b>Hour To</b> | <b>Greeting</b> |
[man.greetingRules[0].from] | [man.greetingRules[0].to] | [man.greetingRules[0].greeting][getPossibleGreetings()] |
[man.greetingRules[1].from] | [man.greetingRules[1].to] | [man.greetingRules[1].greeting][getPossibleGreetings()] |
[man.greetingRules[2].from] | [man.greetingRules[2].to] | [man.greetingRules[2].greeting][getPossibleGreetings()] |
[man.greetingRules[3].from] | [man.greetingRules[3].to] | [man.greetingRules[3].greeting][getPossibleGreetings()] |
This form has a fixed number of rules (row), so a user may change only values of rules attributes.
The layouts "SalutationRules" represent a dynamic table:
Layout TableLayout salutationRulesLayout(HelloManager man) | ||
<h3> Salutation Rules</h3> | ||
<strong>Rules "Define Salutation" </strong> | ||
man.salutationRules.createTable(); | ||
actionHyperlink( "Add Rule"); | ||
actionButton("Save Changes"); | actionButton("Greeting Rules"); | actionButton("Generate Greeting"); |
Layout TableLayout salutationsTableHeader() | ||||
<b>Gender</b> | <b>Marital Status</b> | <b>Age Less Than</b> | <b>Salutation</b> | <b>Delete</b> |
Layout TableLayout salutationsTableRow(SalutationRule rule) | ||||
[rule.gender]["Male,Female"] | [rule.maritalStatus]["Married,Single"] | [rule.maxAge] | [rule.salutation][getPossibleSalutations()] | deleteRuleButton(rule); |
Layout TableLayout deleteRuleButton(SalutationRule rule) | ||
<F type="image"
src="../openrules.forms.lib/images/delete.png"> [][] [rule.manager.salutationRules.deleteRow(rule); dialog().setLastAction("Delete Rule")] </F> |
Her is the rule table that specifies processing flow:
Rules void processingFlowRules(HelloManager man) | |||
IF Current Step is |
AND Action is |
THEN Execute Code |
AND Go To The Step |
Init | GenerateGreeting | ||
GenerateGreeting | { man.cleanUp(); } | GenerateGreeting | |
GenerateGreeting | Generate Greeting | { man.generateGreeting(); } | GenerateGreeting |
GenerateGreeting | Greeting Rules | GreetingRules | |
GenerateGreeting | Salutation Rules | SalutationRules | |
GreetingRules | Save Changes | { man.updateRules(); } | GreetingRules |
GreetingRules | Salutation Rules | SalutationRules | |
GreetingRules | Generate Greeting | GenerateGreeting | |
SalutationRules | Save Changes | { man.updateRules(); } | SalutationRules |
SalutationRules | Add Rule | { man.addSalutationRule(); } | SalutationRules |
SalutationRules | Delete Rule | SalutationRules | |
SalutationRules | Greeting Rules | GreetingRules | |
SalutationRules | Generate Greeting | GenerateGreeting |
As you can see, the action "Save Changes" leads to the execution of the manager's method "updateRules":
public void updateRules() { createExternalRules(); getExternalRules().setModified(true); showRules(); } |
This method will create a new instance of the external rules (based on the latest changes introduced by rule editors), will mark external rules as "modified" that will force a rule engine to reinitialize itself before next run of the method "generateGreeting":
public void generateGreeting() { ruleEngine.run("greetingRules",app); ruleEngine.run("salutationRules",app); } |
Step 6. Deploying and Executing the Web Application
To deploy this web application on the Tomcat server specified in the file build.properties, it is enough to double-click on deploy.bat. To start the application make sure that your Tomcat is up and running and double-clock on run.bat.