This page gives a short overview of the RuleEngine Framework, its functioning and concepts.
RHS | The right-hand side of a rule, which is the conditional part |
---|---|
LHS | The left-hand side of a rule, which is the part that is executed if the RHS evaluates to |
The rule engine framework is to provide an abstract way of executing rules on business objects (like products, shipping conditions, etc.). The user/developer does not need to know any specific rule engine language as long as he uses the interfaces provided in bc_ruleengine. He can create, load, store, and remove rules by using the RuleRepository in conjunction with the RuleLoader. He can administrate these rules using the RuleAdministrator and execute them using the RuleRuntime.
The picture shows a RuleRepositoryProvider which will return a caller a RuleRuntime based upon the given RuleRepository and an array of package names. The RuleRuntime will be updated with rules from a RuleAdministrator which will be loaded by a RuleLoader to this administrator.
The gray objects show implementations by Drools.
A semantic rule is split into two parts: the if-condition (left-hand side) and the when-block (right-hand side). In addition, the execution of a rule can be parametrized with some attributes.
This leads to the following scheme, from which the objects used in bc_ruleengine were created:
For the grouping of rules, they must be put into packages (these packages seem to be the same as Java packages, but it is nothing like a file structure, it is just a simple string). Rules with the same package name semantically belong together:
So a rule needs to have a package, rule name, right-hand side, and left-hand side. The attributes used for the rule execution are optional.
Currently there are two ways of storing a rule:
The difference between both ways is the "language" of storing. As in files, the rules language has to be very clear (like Drools), but in the data base it is very abstract (using the objects from bc_ruleengine). So if you want to use files to store the rules, you need to do this in the rule language you prefer. You also need a RuleLoader that is able to read these files and to provide the rules in there to your RuleEngine.
Using the data base at this point makes you independent from any specific rule language. The rules are stored there in an abstract way, so that while creating your rules you do not need to know which language-specific RuleEngine will read and execute the rules. You can also use the API of the RuleRepository to persist and load the rules. Currently, there is no way to save rules in files using an API function.
The major aspects are:
If necessary, a RuleRuntime can be pushed into a business component (a component that will make use of rules). Usually, this runtime will not directly be pushed into the business component, it will rather be requested from a RuleRuntimeProvider component. For this, the provider uses a repository and a list of rule package names to determine the proper runtime. If the runtime still does not exist, the provider can instantiate the runtime and return it.
From the runtime, the calling business component can obtain a javax.rules.RuleSession.
The RuleAdministrator will provide functionality to administrate a rule runtime. That involves loading rule/s, resetting in a runtime and so on. The runtime usually will be wired to the administrator using the component framework - or one class will implement both interfaces.
A component for loading rules is the RuleLoader. The implementation(s) of this interface should be able to load rules from different sources (like files, databases, and so on) and will provide a common way to load these rules into the RuleAdministrator. So after the administrator received new/updated rules, it will update the runtime that it is connected to.
The RuleRepository is a component to permanently create/update/remove abstract rules in the database. The rules are called abstract, as they do not have a specific rule language. So you are able to transform these abstract rules with a specialized rule loader into different rule languages. The RuleRepository will return RulePackages that need to be pushed into the loader which will transform them.
If you choose to store/retrieve your rules from the rule repository, the usage of JBoss Drools is fairly transparent to you. You will not recognize that Drools is in usage because you do not have to deal with it. You do not need to know any syntax, any semantics and so on. Use the RuleRepository for administrating the rule packages, have them loaded by the TemplateRuleLoader and have them executed by the DroolsKnowledgeBaseImpl- which of course is wired by the component framework.
If you decide to read the rules from files, make sure to put them into <cartridge-name>/release/rules/<rule-language-name>/<purpose>. In the case of Drools, <rule-language-name> needs to be "drools", which indicates that these folders contain only files that a Drools-specific rule loader implementation can read (which actually uses a Drools implementation to load the files). The file extension has to be *.drl
. The EnfinityStudio provides a way to create syntax correct files for JBoss Drools (refer to EnfinityStudio documentation).
Using the file system Drools loader gives you another way of logically grouping the rules you can load: the purpose. As the name indicates, the files are grouped by purpose, which may be shipping, checkout, tests, ... and so on. However, the Drools loaders implementation only returns rules for certain purposes. You cannot ask it to get all rules, you always have to ask for the proper purpose.
There are currently two different ways of loading rules. One is by reading/loading them from files, the other is by getting them from the repository.
For the first way we use a DirectoryRuleLoader or the DroolsDirectoryLoader. This one will look up the cartridge list and will search for Drools-specific files in any cartridge release directory. If it was successful, it will push the found Drools files URLs to the wired RuleAdministrator.
The other loader is the TemplateRuleLoader or the DroolsTemplateRuleLoader. This one takes a collection of RulePackages and converts them using a template (in this special case the Drools language) to provide a stream to the wired RuleAdministrator which contains the rules.
Both loaders need to be wired with a rule language-specific (which is Drools) implementation of the RuleAdminstrator because both provide only rule language-specific output.
The rule repository is the "bridge" between a rule as its abstract object representation and the rule persisted in the data base. The repository itself is only used to load or save rules. You can create new rules by invoking the repositories create...
functions, which will return parts of the rule that should be created. The repositories get...
function will return rule packages, based upon the given identifiers.
While the rule repository and rule loader are responsible for providing rules, the rule runtime is the part that does actually execute the rules. Therefore, it needs a RuleAdministrator wired which is able to load, reload, update, and reset the rules in the rule runtime. The rule runtime itself only provides two functions, one is for creating a stateful and the other is to create a stateless rule session. Both session types can be executed afterwards with your business objects.