Introduction
RuleRunner is a light-weight Java class that leverages hash maps and a simple decisioning algorithm to determine the best result value
for a given key field within the context of the lookup. The service supports separation of business rules from the application code and promotes
clean generic code practices. Rules are externalized to a properties file which can be loaded upon request or loaded at application startup to increase performance.
It is especially handy when you need to determine field values based on boolean conditions that frequently change. These conditions (or rules) are configured
in a properties file and therefore don't require recompilation. The rules can handle nested levels of decisioning criteria to derive the appropriate result.
This service will not compute results; it will decide which result best fits a given field based on relevant context information provided at the time of the lookup.
The way it works is simple. Similar to a properties file lookup, you call the service with a key and it returns a value. The difference is that you can also
optionally pass up to 3 context field values which will be used to derive the best possible match. The algorithm uses decision tree and fall-through logic
that starts searching for the property with all supplied context field values. If a rule (tested as a boolean expression) is not found for all context fields provided,
it starts to generalize the search and apply wild cards.
For example, if you search for a property called "welcomeStatement
" and you specifically want the welcome statement for the company "MSN" (context field1)
and the country "USA" (context field2), you would submit the lookup for the property along with the 2 context field values. The algorithm would first search
for a specific welcomeStatement value where the company is "MSN" and the country is "USA". If it finds one, it returns
the property value. If it does not, it will see if there is a more generic welcome statement for MSN that applies to all countries (country becomes
a wildcard "*"). If it finds one, it will return this value. If it does not, it will search for a generic welcome statement for all
companies (company becomes a wildcard "*") in the country "USA". If it finds one, it will return this value. If it does not,
it will search for a generic welcomeStatement that applies to all companies and all countries (both become wildcards "*") and act very much like
a standard property lookup. The fall through logic has a specific sequence which must be clearly understood prior to writing rules. It is well documented
in the Java class. This fall-through logic is powerful since it avoids the need to spell out every single permutation. For example, if you were to write a query
in the form "select value from welcomeStatement_table where company = X and country = Y, you'd need to enter all possible permutations in the table.
Background
On a recent assignment, I was tasked with re-factoring a 4000+ line of code java class that was riddled with hardcoding and other poor development practices.
Given that the code would be subject to change, I developed RuleRunner to externalize the rules so that a Business Analyst and QA tester could interpret and maintain rules
as appropriate. I extracted over 1000 rules and was able to train BA members to configure rules going forward. The zip file attached contains a document written in laymen
terms meant for non-technical folks to quickly grasp how to write rules. I used this tool to populate default values (with slight variances based on conditions) when building
an outgoing web service xml document but I think it would work just as well for a number of different applications. In fact, demo2 attached shows how you can populate
all default values in a single call to the service and have the properties file drive the process.
While developing the service, I found that the decisioning algorithm was useful but the service could be greatly improved if it could handle dynamic values.
Remember, the service doesn't calculate/derive values but does a good job at decisioning. I was able to achieve this by including a hashmap of key/value pairs for the
dynamic content (example, key="randomMessage" with an associated value as calculated/derived by the calling class prior to the service call).
The trick to this is to prefix the value for the given rule in the properties file with a dollar sign ($) to indicate that the value should NOT be substituted
as is (static). The service will go through the regular decisioning/fall-through logic and do a final step to determine if the value found from the rule is static
or dynamic. If the value starts with a "$" (dynamic), the algorithm will strip the "$" and search the dynamic content hashmap
using the rule value found as the key to derive the calculated value to return.
Below is a code snippet demonstrating how RuleRunner could be used to refactor code. It externalizes all conditional logic to a properties file and replaces
the class code with one simple call to retrieve the value for a property from the RuleRunner service. The example also includes the hVars hashmap to address
dynamic content for "randomMessage" as described above.
Using the code
Extract all files to a folder on your computer where the java.exe launcher is in your path and run the demo classes as standalone java applications passing two arguments,
company and country. Read (1) Writing rules with Rule Runner.doc below for more context. The attached ZIP file contains the following:
1 | Writing rules with Rule Runner.doc | This document is written in laymen terms to describe how to read and write rules for RuleRunner. |
2 | RuleRunner.java RuleRunner.class | The RuleRunner engine. |
3 | RuleRunnerDemo1.java RuleRunnerDemo1.class | This demo illustrates use of RuleRunner to execute a rule that will retrieve a value for a key field based
on two context fields. This example will follow the tutorial example to retrieve the correct welcome statement given the company and country
it applies too. Although RuleRunner supports 3 context fields per rule and rules can be linked to support endless context fields, this example is purposely
limited to 2 context fields to facilitate understanding. Execute this demo as a stand alone java application passing 2 arguments: company and country.
(E.g., java RuleRunnerDemo1 MSN Spain) |
4 | RuleRunnerDemo2.java RuleRunnerDemo2.class | This demo illustrates use of RuleRunner to execute a call that will run all rules in the properties file.
In order to run individual rules, RuleRunner does not require that all key fields have the same context columns to dictate their criteria,
but in order to use this feature, all key fields must adhere to this restriction. In this example, there are 3 key fields in the Rules file (welcomeStatement,
logo and goodbyeStatement). All key fields in this example use the same context columns to determine their values, company and country. The call will evaluate
each key field and retrieve the correct values relative to the company and country context fields. Execute this demo as a stand alone java
application passing 2 arguments: company and country. (E.g.: java RuleRunnerDemo2 MSN Spain) |
5 | demoRules.properties | This properties file contains the rules for both demos above. |
History
Initial release v0.1 06/2009.