At runtime, there may be many potential paths through a BPEL process, controlled by conditional statements such as switch or while activities. Typically, the business rules that govern which path to take at any given point are written as XPath expressions embedded within the appropriate activity.
Although this is an acceptable approach, we often find that while the process itself may be relatively static, the business rules embedded within the activities may change on a more frequent basis. This will require us to update the BPEL process and redeploy it, even though the process flow itself hasn't changed.
In addition, by embedding the rule directly within the decision point, we often end up having to reimplement the same rule every time it is used, either within the same process or across multiple processes. Apart from being inefficient, this can lead to inconsistent implementations of the rules, as well as requiring us to update the rules in multiple places every time it changes.
The Oracle Business Rules engine that comes as part of the SOA Suite provides a declarative mechanism for defining business rules externally to our application. This not only ensures that each rule is used in a consistent fashion, but in addition, it makes it simpler and quicker to modify. We only have to modify a rule once and can do this with almost immediate effect, thus increasing the agility of our solution.
For those of you familiar with 10gR3, you will notice that JDeveloper comes with a new rules editor which is a lot more intuitive and simpler to use than the old browser-based editor. In addition, 11gR1 introduces decision tables, which provide a spreadsheet-like format for defining rules. While still very much a developer-oriented tool, these improvements make the tool a lot friendlier for business analysts, allowing them to better understand the rules that have been written as well as make simple changes.
In this article, we will introduce the new rules editor and look at how we can use it to define a decisions service to automate the approval of leave requests. Then, once we've done this, we'll see how to invoke the rule from the leave approval BPEL process. We will first implement these as a standard set of rules and then examine how we can simplify these rules by using a decision table.
Business rule concepts
Before we implement our first rule, let's briefly introduce the key components which make up a business rule. These are:
- Facts: Represent the data or business objects that rules are applied to.
- Rules: A rule consists of two parts, namely, an IF part that consists of one or more tests to be applied to a fact(s), and a THEN part that lists the actions to be carried out, should the test evaluate to true.
- Rule Set: As the name implies, it is just a set of one or more related rules that are designed to work together.
- Dictionary: A dictionary is the container of all components that make up a business rule. It holds all the Facts, Rule Sets, and Rules for a business rule.
In addition, a dictionary may also contain decision tables, functions, variables, and constraints. We will introduce these in more detail later in this article.
To execute a business rule, you assert (submit) one or more facts to the rules engine. It will apply the rules to the facts, that is, each fact will be tested against the IF part of the rule, and if it evaluates to true, then it will perform the specified actions for that fact. This may result in the creation of new facts or the modification of existing facts (which may result in further rule evaluation).
The rule engine supports four types of facts: Java Facts, XML Facts, RL Facts, and ADF Facts. The type of fact that you want to use typically depends on the context in which you will be using the rules engine.
For example, if you are calling the rule engine from Java, then you would work with Java Facts as this provides a more integrated way of combining the two components. As we are using the rule engine within a composite, it makes sense to use XML facts. The rule editor uses XML schemas to generate JAXB 2.0 classes, which are then imported to implement the corresponding XML facts. Using JAXB, particularly when used in conjunction with BPEL, places a number of constraints on how we define our XML schemas, including:
- Within BPEL, you can only define variables based on globally defined elements. Thus all input and output facts passed to the decision service must be defined as global elements within our XML schemas.
- When defining the input and output facts for any complexType (for example, tLeaveRequest), there can only be one global element of that type (for example, leaveRequest).
- The element naming convention for JAXB means that elements or types with underscores in their names can cause compilation errors.
To invoke a business rule within a composite, we need to go through a number of steps. First, we must create a session with the rules engine, then we can assert one or more facts, before executing the ruleset and finally we can retrieve the results.
We do this via a decision service (or function). This is essentially a web-service wrapper around a rules dictionary, which takes care of managing the session with the rules engine as well as governing which ruleset we wish to apply.
The wrapper allows a composite to assert one or more facts, execute a ruleset(s) against the asserted facts, retrieve the results, and then reset the session. This can be done within a single invocation of an operation or over multiple operations.
Leave approval business rule
For our first rule, we are going to build on Adding in Human Workflow. It's a simple process requiring every leave request to go to an individual's manager for approval. However, what we would like is a rule that automatically approves a request as long as it meets certain company guidelines.
To begin with, we will write a simple rule to automatically approve a leave request that is of the type Vacation and only for one day's duration. This is a pretty trivial example, but once we've done this, we will look at how to extend this rule to handle more complex examples.
Creating a decision service
Within JDeveloper, open up your LeaveApproval application (or alternately open the sample provided with the article). Open up the composite.xml file for the application and then from the Component Palette, drag-and-drop a Business Rule onto the composite, as shown in the following screenshot:
This will launch the Create Business Rules dialog, as shown in the following screenshot:
The first step is to give our dictionary a name, such as LeaveApprovalRules, and a corresponding <>bPackage name.
In addition, we need to specify the Input and Output facts that we will pass to our decision service. For our purpose, we will pass in a single leave request. The rule engine will then apply the rules that we define and update the status of the leave request to either Approved or Manual (to indicate the request needs to be manually approved).
So we need to define a single input fact and output fact, both of type leaveRequest. To do this, click on the plus symbol (marked in the preceding screenshot), and select Input.
This will bring up the standard Type Chooser window; browse the LeaveRequest.xsd and select leaveRequest. Do the same again to specify an Output fact.
Next, click the Advanced tab. Here we can see that JDeveloper has given the default name LeaveApprovalRules_DecisionService_1 to our decision service. Give it a more meaningful name such as LeaveApprovalDecisonService.
Now click OK. JDeveloper will inform you that it is creating the business rule dictionary for LeaveApprovalRules. Once completed, your composite should now look as shown in the following screenshot:
We are now ready to implement our business rules. Double-click on the LeaveApprovalRules component, and this will launch the rules editor, which is shown in the next screenshot.
Implementing our business rules
The rules editor allows you to view/edit the various components which make up your business rules. To select a particular component, such as Facts, Functions, Globals, and so on, just click on the corresponding tab down the left-hand side.
You will see that, by default, JDeveloper has created a skeleton rules dictionary-based on the inputs we just specified.
Select the Facts tab (as shown in the preceding screenshot). You will see that it contains two XML facts (TLeaveRequest and com.packtpub.schemas.leaverequest. ObjectFactory), which are based on the inputs/outputs we defined earlier as well as a set of standard Java facts, which are automatically included within a rules dictionary.
Next, select the Decision Functions tab. You will see that it contains a single decision function LeaveApprovalDecisonService (that is, the name we specified on the Advanced tab when creating our business rule).
We will introduce some of the other tabs later in this article, but for the time being, we will start by defining our first rule. By default, the rules editor will have created a single ruleset with the name Ruleset_1. Click on the Ruleset_1 tab to open up the ruleset within the editor.
Expand the ruleset to show its details by clicking on the plus symbol (circled in the following screenshot). We can see that the ruleset has three properties: Name, Description, and Effective Date.
The Effective Date enables us to specify a period in time for which the ruleset will be applied, allowing you to define multiple versions of the same ruleset. For example, a current ruleset and a future version that we wish to come into effect at a defined time in the future.
Rename the ruleset to something more meaningful, for example, Employee Leave Approval Policy; add a description if you want and ensure that Effective Date is set to Always Valid.
Adding a rule to our ruleset
To add a rule, click the green plus symbol on the top-right-hand corner, and select Create Rule, as shown in the following screenshot (alternatively click on the Create Rule button, circled in the following screenshot).
This will add a rule to our ruleset with the default name Rule_1, as shown in the following screenshot. Here, we can see that a rule consists of two parts, an IF part, which consists of one or more tests to be applied to a fact or facts, and a THEN part, which specifies the actions to be carried out, should the test evaluate to true.
To give the rule a more meaningful name, simply click on the name and enter a new name (for example, One Day Vacation). By clicking on the
Creating the IF clause
For our leave approval rule, we need to define two tests, one to check that the request is only for a day in duration, which we can do by checking that the start date equals the end date, and the second to check that the request is of type Vacation.
To define the first test, click on <insert test>. This will add the line <operand> = = <operand> under the IF statement where we can define the test condition.
Click on the first <operand>. This will display a drop-down list listing the valid facts and their attributes that we can test. From here, we can select the value to be tested, for example, TLeaveRequest.startDate in our case.
Next from the operator drop-down list, select the test to be applied to the first operand (== in our case). We can either choose to compare it to a specified value or a second Operand. For our purpose, we want to check that the request.startDate equals the request.endDate, so click on the operand and select this from the drop-down list.
To create our second test, we follow pretty much the same process. This time we want to test that the operand leaveRequest.leaveType is equal to the value Vacation, so select the right-hand operator and type this in directly:
Note, the rule editor has automatically inserted an and clause between our two tests. If you click on this, you have the option of changing this to an or clause.
Creating the Then clause
Now that we have defined our test, we need to define the action to take if the test evaluates to true. Click on <insert action>. This will display a drop-down list where you need to specify the Action Type you wish to carry out.
The rule editor allows us to choose from the following action types:
- assert new: We use this to create and assert a new fact, for example, a new LeaveRequest. Once asserted, the new fact will be evaluated by the rules engine against the ruleset.
- modify: We can use this to either assign a value to a variable or a fact attribute; in our case we want to assign a status of Approved to the requestStatus property.
- retract: This enables you to retract any of the facts matched in the pattern (for example, TLeaveRequest) so that it will no longer be evaluated as part of the ruleset.
- call: This allows you to call a function to perform one or more actions.
The actions assert new and retract are important when we are dealing with rulesets that deal with multiple interdependent facts, as this allows us to control which facts are being evaluated by the rule engine at any particular time.
For our purposes, we want to update the status of our leave, so select modify. Our rule should now look as shown in the following screenshot:
The next step is to specify the fact to be modified. Click on the <target> element and you will be presented with a list of facts that are within scope. In our case, this will only be the TLeaveRequest that has just been matched by the IF clause, so select this. Our rule will now appear, as shown in the following screenshot:
We now need to specify the properties we wish to modify, click on <add property> to open the Properties dialog. This will display a list of all the facts properties, allowing us to modify them as appropriate.
Select the Value cell for requestStatus. From here, you can directly enter a value, select a value from the drop-down list, or launch the expression builder. For our purposes, just enter the string Approved, as shown in the following screenshot, and then click Close.
We don't need to specify values for any of the other properties, as the rules engine will only update those properties where a new value has been specified.
This completes the definition of our first rule. The next step is to wire it into our BPEL process.