Oracle Business Rules SDK (Rules SDK) lets you write applications that access, create, modify, and execute rules in Oracle Business Rules dictionaries (and work with the contents of a dictionary). This chapter provides a brief description of Rules SDK and shows how to work with the Rules SDK Decision Point API.
This chapter includes the following sections:
Section 7.1, "Introduction to Rules SDK and the Car Rental Sample Application"
Section 7.2, "Creating a Dictionary for Use with a Decision Point"
Section 7.3, "Creating a Java Application Using Rules SDK Decision Point"
Section 7.5, "What You Need to Know About Using Decision Point in a Production Environment"
Section 7.6, "What You Need to Know About Decision Point and Decision Tracing"
For more information, see Oracle Fusion Middleware Java API Reference for Oracle Business Rules.
The Rules SDK consists of four areas:
Engine: provides for rules execution
Storage: provides access to rule dictionaries and repositories
Editing: provides a programatic way to create and modify dictionary components
Decision Point: provides an interface to access a dictionary and execute a decision function
Other than for explanation purposes, there is not an explicit distinction between these areas in Rules SDK. For example, to edit rules you also need to use the storage area of Rules SDK to access a dictionary. These parts of the Rules SDK are divided to help describe the different modes of usage, rather than to describe distinct Rules SDK APIs.
The Decision Point API provides a concise way to execute rules. Most users create Oracle Business Rules artifacts, including data model elements, rules, Decision Tables, and rulesets using the Rules Designer extension to Oracle JDeveloper. Thus, most users do not need to work directly with the engine, storage, or editing parts of Rules SDK.
To work with the Rules SDK Decision Point package you need to understand three important classes:
DecisionPoint
: is a helper class that follows the factory design pattern to create instances of DecisionPointInstance
. In most applications there should be one DecisionPoint
object that is shared by all application threads. A caller uses the getInstance()
method of DecisionPoint
to get an instance of DecisionPointInstance
which can be used to call the defined Decision Point.
DecisionPointBuilder
: follows the Builder design pattern to construct a Decision Point.
DecisionPointInstance
: users call invoke()
in this class to assert facts and execute a decision function.
The DecisionPoint
classes support a fluent interface model so that methods can be chained together. For more information, see
http://www.martinfowler.com/bliki/FluentInterface.html
A Decision Point manages several aspects of rule execution, including:
Use of oracle.rules.rl.RuleSession
objects
Reloading of a dictionary when the dictionary is updated
To create a Decision Point in a Java application you need the following:
Either the name of a dictionary to be loaded from an MDS repository or a pre-loaded oracle.rules.sdk2.dictionary.RuleDictionary
instance.
The name of a decision function stored in the specified dictionary.
This chapter shows a car rental application that demonstrates the use of Rules SDK and the Decision Point API. You can obtain the sample application in a ZIP file, CarRentalApplication.zip
. This ZIP contains a complete JDeveloper application and project.
The source code for Oracle Business Rules-specific samples is available online at
http://www.oracle.com/technology/sample_code/products/rules
For SOA samples online visit
http://www.oracle.com/technology/sample_code/products/soa
To work with the sample unzip CarRentalApplication.zip
into an appropriate directory. The car rental application project contains a rules dictionary and several Java examples using Rules SDK.
The Car Rental sample application shows you how to work with the Rules SDK Decision Point API.
To open the car rental sample application:
Start Oracle JDeveloper.
Open the car rental application in the directory where you unzipped the sample. For example, from the File menu select Open... and in the Open dialog navigate to the CarRentalApplication folder.
In the Open dialog select CarRentalApplication.jws and click Open.
In the Application Navigator, expand the CarRentalApplication, expand Application Sources and Resources. This displays the Oracle Business Rules dictionary named CarRental.rules
and several Java source files.
The car rental sample uses the Rules SDK Decision Point API with either a pre-loaded Oracle Business Rules dictionary or a repository stored in MDS. When you are working in a development environment you can use the Decision Point API with the pre-loaded dictionary signature. In a production environment you would typically use a Decision Point with the MDS repository signature.
The CarRental dictionary is pre-defined and is available in the car rental sample application.
To work with the Decision Point API you need to create a dictionary that contains a decision function (the car rental sample application comes with a predefined dictionary and decision function).
You perform the following steps to create a dictionary and a decision function:
Section 7.2.1, "How to Create Data Model Elements for Use with a Decision Point"
Section 7.2.2, "How to View a Decision Function to Call from the Decision Point"
Section 7.2.3, "How to Create Rules or Decision Tables for the Decision Function"
You need the following to add to a decision function when you create an application with a Decision Point.
A dictionary containing data model elements that you use to create rules or Decision Tables and when working with ADF Business Components fact types, you need to add links for the Decision Point support dictionary. For more information, see Chapter 2, "Working with Data Model Elements". For more information, see Chapter 10, "Working with Oracle Business Rules and ADF Business Components"
.
A dictionary containing fact definitions. For more information, see Chapter 3, "Working with Facts and Bucketsets".
To view the data model in the supplied car rental sample application:
In Rules Designer, click the Facts navigation tab.
Select the Java Facts tab, as shown in Figure 7-1.
The Java Facts tab shows four fact types imported, in addition to the fact types provided as built-in to the dictionary.
The Driver
Java Fact is imported from the Driver
Java class in the project.
The Denial
Java Fact is imported from Denial
Java class in the project.
The LicenseType
and VehicleType
facts are imported from the nested enum classes defined in the Driver
class.
Figure 7-1 Defined Java Facts for the Car Rental Sample Application
When you use a Decision Point with Rules SDK, you call a decision function in a specified dictionary. The decision function that you call can contain one or more rulesets that are executed as part of the Decision Point.
To view the ruleset in the supplied car rental sample application:
In Rules Designer, expand the CarRentalApplication.
In the CarRentalApplication, expand Resources.
Double-click the CarRental.rules.
When you work with the Decision Point API you use decision functions to expose an Oracle Business Rules dictionary. For more information on decision functions, see Chapter 6, "Working with Decision Functions".
To view the decision function in the car rental sample application:
In Rules Designer, click the Decision Functions navigation tab. This displays the available decision functions in the CarRental dictionary, as shown in Figure 7-2.
Figure 7-2 Car Rental Sample Decision Function
Select the row with CarRentalDecisionFunction and double-click the decision function icon. This opens the Edit Decision Function dialog as shown in Figure 7-3.
The decision function Inputs table includes a single argument for a Driver fact type.
The decision function Outputs table includes a single argument for a Denial fact type.
The decision function Rulesets and Decision Functions area shows Denial Rules:if-then in the Selected box.
Figure 7-3 Car Rental Decision Function for the Car Rental Sample Application
The car rental sample includes two rulesets, one with IF/THEN rules and another containing a Decision Table. You can use either IF/THEN rules or Decision Tables or both in your application if you are using a Decision Point.
To view the rules in the car rental sample application:
In Rules Designer click the Denial Rules:if-then ruleset, as shown in Figure 7-4.
Figure 7-4 Ruleset with IF/THEN Rules for the Car Rental Sample Application
The Denial Rules:if-then ruleset includes two rules:
under age: this rule defines the minimum age of the driver. The rule compares the Driver
instance age
property to the global Minimum driver age
. If the driver is under this age, then a new Denial
fact is asserted. A call to the decision function collects this Denial
fact, as defined in its output. The rule also calls a user-defined function, audit
, to provide some auditing output about why the Denial
is created.
too many accidents: this rule defines an upper threshold for the number of accidents a driver can have before a rental for the driver is denied. The rule also calls a user-defined function, audit
, to provide some auditing output about why the Denial
is created.
To view the Decision Table in the car rental application:
In Rules Designer, click the Denial Rules:decision table ruleset, as shown in Figure 7-5.
Figure 7-5 Ruleset with Decision Table for the Car Rental Sample Application
The car rental sample application includes the Denial Rules: decision table ruleset. To switch to use a Decision Table in the supplied decision function sample, move the Denial Rules:if-then from the Selected area in the decision function and add the Denial Rules: decision table ruleset, which uses a Decision Table to define similar rules, as shown in Figure 7-6.
Figure 7-6 Decision Function for Car Rental Sample with Decision Table Ruleset
When use Rules SDK in a development environment you of the option of using Decision Point API with a pre-loaded dictionary. In a production environment you typically use the Decision Point API with the MDS repository signature and the dictionary is stored in MDS. For more information on using a Decision Point with , see Section 7.5, "What You Need to Know About Using Decision Point in a Production Environment".
The source code for Oracle Business Rules-specific samples is available online at
http://www.oracle.com/technology/sample_code/products/rules
For SOA samples online visit
http://www.oracle.com/technology/sample_code/products/soa
The CarRentalProject project includes the com.example.rules.demo
package that includes the car rental sample file, CarRentalWithDecisionPointUsingPreloadedDictionary.java
.
The project also includes several .java
source files that support different variations for using Decision Point. Table 7-1 provides a summary of the different versions of the car rental sample.
For more information on working with the Rules SDK Decision Point API, see Oracle Fusion Middleware Java API Reference for Oracle Business Rules.
Table 7-1 Java Files in the Decision Point Sample CarRentalProject
Base Java Filename | Description |
---|---|
|
This is the base class for all of the examples. It contains constant values for using the CarRental dictionary and a method |
|
Contains a static attribute of type |
|
Contains an example of creating a Decision Point that uses MDS to access and load the rule dictionary. In a production environment, most applications use the Decision Point API with MDS. |
|
Contains an example of creating a Decision Point from an instance of the |
|
Contains an advanced usage of the Engine API that is documented further in the comments. |
|
Contains an advanced usage of the Engine API that is documented further in the comments. |
|
Contains the class that defines the |
|
Contains the class that defines the |
|
Contains the class which can be used as a thread for simulating concurrent users invoking the Decision Point. |
To use a Decision Point you create a DecisionPoint
instance using DecisionPointBuilder
, as shown in Example 7-1.
Example 7-1 Using the Decision Point Builder
static { try { // specifying the Decision Function and a pre-loaded // RuleDictionary instance m_decisionPoint = new DecisionPointBuilder() .with(DF_NAME) .with(loadRuleDictionary()) .build(); } catch (SDKException e) { System.err.println("Failed to build Decision Point: " + e.getMessage()); e.printStackTrace(); } }
Example 7-1 shows the
DecisionPointBuilder
supports a fluent interface pattern, so all methods can easily be chained together when you create a Decision Point. The three most common methods for configuring the Decision Point with DecisionPointBuilder
are overloaded to have the name with()
. Each with()
method takes a single argument of type RuleDictionary
, DictionaryFQN
, or String
. The DecisionPointBuilder
also supports similar set and get methods: getDecisionFunction()
, setDecisionFunction()
, getDictionary()
, setDictionary()
, getDictionaryFQN()
, setDictionaryFQN()
.
This chain shown in Example 7-1 includes the following steps:
The first step is to create a DecisionPointBuilder
instance with code such as the following:
new DecisionPointBuilder()
The with()
method using a String
argument defines the name of the decision function that the Decision Point executes. Calling this method is mandatory.
.with(DF_NAME)
The DF_NAME
specifies the name of the decision function you define for your application. For example for the sample car rental application DF_NAME
is defined in CarRental.java
as CarRentalDecisionFunction
.
Call only one of the other two with()
methods. In this case the sample code uses a pre-loaded Rule Dictionary instance, containing the specified decision function. The loadDictionary()
method loads an instance of RuleDictionary
from a file. Example 7-2 shows the
loadDictionary()
method. For more information, see Section 7.3.2, "How to Use a Decision Point with a Pre-loaded Dictionary".
.with(loadRuleDictionary())
Call the build()
method to construct and return a DecisionPoint
instance.
The DecisionPoint
instance is shared among all instances of the application, which is why it is a static attribute and created in a static block. Another way of initializing the DecisionPoint
would be to initialize the m_decisionPoint
attribute with a static method that created and returned a DecisionPoint
instance.
Example 7-2 shows the
loadRuleDictionary()
method that loads an instance of RuleDictionary from a file.
When reading or writing a dictionary directly from a file as shown in Example 7-2, ensure to set the encoding to
UTF-8
. If this is not done, Unicode characters used in the dictionary are corrupted. The UTF-8
option must be set explicitly in the FileInputStream
or OutputStreamWriter
constructor. Do not use Java classes such as FileReader
and FileWriter
, as these classes always use the platform default encoding which is usually an ASCII variant rather than a Unicode variant.
Example 7-2 Load Rule Dictionary Method
private static RuleDictionary loadRuleDictionary(){ RuleDictionary dict = null; BufferedReader reader = null; try { reader = new BufferedReader( new InputStreamReader( new FileInputStream( new File(DICT_LOCATION)), "UTF-8")); dict = RuleDictionary.readDictionary(reader, new DecisionPointDictionaryFinder(null)); List<SDKWarning> warnings = new ArrayList<SDKWarning>(); dict.update(warnings); if (warnings.size() > 0) { System.err.println("Validation warnings: " + warnings); } } catch (SDKException e){ System.err.println(e); } catch (FileNotFoundException e){ System.err.println(e); } catch (IOException e){ System.err.println(e); } finally { if (reader != null) { try { reader.close(); } catch (IOException ioe) {ioe.printStackTrace();}} } return dict; }
The car rental sample allows you to use Oracle Business Rules and simulate multiple concurrent users. Example 7-3 shows use of the Java
ExecutorService
interface to execute multiple threads that invoke the Decision Point. The ExecutorService
is not part of the Rules SDK Decision Point API.
Example 7-3 Checking Drivers with Threads that Invoke Decision Point
ExecutorService exec = Executors.newCachedThreadPool(); List<Driver> drivers = createDrivers(); for (int i = 0; i < NUM_CONCURRENT; i++) { Driver driver = drivers.get(i % drivers.size()); exec.execute(new DriverCheckerRunnable(driver)); }
Example 7-3 includes the following code for the sample application:
Create the Executor Service:
ExecutorService exec = Executors.newCachedThreadPool();
Call method createDrivers()
, defined in CarRental.java
, to create a list of Driver
instances.
List<Driver> drivers = createDrivers();
A loop through a list of Driver
instances to fill the driver list with drivers.
A loop to start multiple threads from DriverCheckerRunnable
instances. These instances open a Decision Point and run the rules on each driver. For information on this code, see Section 7.3.4, "How to Create and Use Decision Point Instances".
Example 7-4 shows the code that waits for the threads to complete.
The DriverCheckerRunnable
instances call the checkDriver()
method. Example 7-5 shows the
checkDriver()
method that is defined in CarRentalWithDecisionPoint
. The checkDriver()
method handles invoking Decision Point with a Driver
instance.
Example 7-5 Code to Create a Decision Point Instance with getInstance()
public class CarRentalWithDecisionPoint extends CarRental { protected static DecisionPoint m_decisionPoint; public static void checkDriver(final Driver driver) { try { DecisionPointInstance instance = m_decisionPoint.getInstance(); instance.setInputs(new ArrayList<Object>() { { add(driver); } }); List<Object> outputs = instance.invoke(); if (outputs.isEmpty()) System.err.println("Oops, no results"); java.util.List<Denial> denials = (java.util.List<Denial>)outputs.get(0); if (denials.isEmpty()) { System.out.println("Rental is allowed for " + driver.getName()); } else { for (Denial denial : denials) { System.out.println("Rental is denied for " + denial.getDriver().getName() + " because " + denial.getReason()); } } } catch (RLException e) { e.printStackTrace(); } catch (SDKException e) { e.printStackTrace(); } } }
Example 7-5 shows the following:
Getting a DecisionPointInstance
from the static DecisionPoint
defined with the DecisionPointBuilder
, with the following code.
DecisionPointInstance instance = m_decisionPoint.getInstance();
Add inputs according to the signature of the decision function associated with the Decision Point. This defines one argument of type List
as the input. This List
contains the Driver
instances:
instance.setInputs(new ArrayList<Object>() { { add(driver); } });
Invoke the Decision Point and store the return value. The return type follows the same pattern as the decision function which is being called in the Decision Point.
List<Object> outputs = instance.invoke();
In this case the invoke()
returns a List
of length one, containing a List
of Denial
instances.
If the return is a List
of any other size than one, then this is an error:
if (outputs.isEmpty()) System.err.println("Oops, no results");
The first entry that is returned from the Decision Point is caste it to a List of type List<Denial>
:
java.util.List<Denial> denials = (java.util.List<Denial>)outputs.get(0);
If the denials list is empty, then no Denial
instances were asserted by the rules. This indicates that it is OK to rent a car to the driver. Otherwise, print the reasons why the driver rental was rejected:
if (denials.isEmpty()) { System.out.println("Rental is allowed for " + driver.getName()); } else { for (Denial denial : denials) { System.out.println("Rental is denied for " + denial.getDriver().getName() + " because " + denial.getReason()); } }
In the car rental sample installed on your system, for the code shown in Example 7-2, modify the value of
DICT_LOCATION
to match the location of the dictionary on your system.
To run the car rental sample on your system:
In the Application Navigator, select the dictionary and from the Edit menu select Copy Path.
In the CarRental.java
file, paste the path value into the DICT_LOCATION
value.
In the CarRentalProject select the CarRentalWithDecisionPointUsingPreloadedDictionary.java file.
Right-click and in the dropdown list select Run.
Example 7-6 shows sample output.
Example 7-6 Output from Car Rental Sample
Rental is allowed for Carol Rental is allowed for Alice Rental is allowed for Alice Rental is allowed for Carol Rental is denied for Bob because under age, age was 15, minimum age is 21 Mar 13, 2009 11:18:00 AM oracle.rules.rl.exceptions.LogWriter flush INFO: Fired: under age because driver age less than minimum threshold for license number d222 Mar 13, 2009 11:18:00 AM oracle.rules.rl.exceptions.LogWriter flush INFO: Fired: under age because driver age less than minimum threshold for license number d222 Rental is denied for Bob because under age, age was 15, minimum age is 21 Rental is allowed for Alice Rental is allowed for Eve
In a production environment you can use an MDS repository to store Oracle Business Rules dictionaries. When you use an MDS repository to store the dictionary, the steps shown in Section 7.3.1, "How to Add a Decision Point Using Decision Point Builder" and Section 7.3.2, "How to Use a Decision Point with a Pre-loaded Dictionary"
change to access the dictionary. The
CarRentalWithDecisionPointUsingMdsRepository
shows sample code for using Decision Point with MDS.
To see a complete example with deployment steps showing the use of a Decision Point to access a dictionary in MDS, see Section 9.4, "Adding a Servlet with Rules SDK Calls for Grades Sample Application".
Example 7-7 shows the use of
DictionaryFQN
with DecisionPointBuilder
to access a dictionary in an MDS repository. The complete example is shown in the sample code in CarRentalWithDecisionPointUsingMdsRepository
.
Example 7-7 Using Decision Point Builder with MDS Repository
static { try { // specifying the Decision Function and Dictionary FQN // loads the rules from the MDS repository. m_decisionPoint = new DecisionPointBuilder() .with(DF_NAME) .with(DICT_FQN) .build(); } catch (SDKException e) { System.err.println("Failed to build Decision Point: " + e.getMessage());
Similar to the steps in Example 7-1, Example 7-7
shows the following:
The first step is to create a DecisionPointBuilder
instance with.
new DecisionPointBuilder()
The with()
method using a String
argument defines the name of the decision function that the Decision Point executes. Calling this method is mandatory.
.with(DF_NAME)
The DF_NAME
specifies the name of the decision function you define for your application. For example for the car rental application this is defined in CarRental.java
a CarRentalDecisionFunction
.
Call only one of the other two with()
methods. In this case the sample code calls a DictionaryFQN
to access an MDS repository. Example 7-8 shows the routing that uses the dictionary package and the dictionary name to create the
DictionaryFQN
.
.with(DICT_FQN)
Call the build()
method to construct and return a DecisionPoint
instance.
Example 7-8 Using the DictionaryFQN Method with MDS Repository
protected static final String DICT_PKG = "com.example.rules.demo"; protected static final String DICT_NAME = "CarRental"; protected static final DictionaryFQN DICT_FQN = new DictionaryFQN(DICT_PKG, DICT_NAME); protected static final String DF_NAME = "CarRentalDecisionFunction";
The Rules SDK API contains methods to assist with processing a decision trace. These methods process a decision trace to replace the RL names used in the trace with the aliases used in the associated dictionary. This makes the decision trace naming consistent with the naming used in the Oracle Business Rules dictionary.
The basic API for processing a decision trace requires a RuleDictionary
object and a DecisionTrace
object:
RuleDictionary dict = ...; DecisionTrace trace = ...; dict.processDecisionTrace(trace);
This code shows the processing call that converts the naming in the decision trace to use the same names, with aliases, as in the dictionary.
The Rules SDK Decision Point API contains methods that allow you configure decision tracing and retrieve the resulting trace when you invoke a decision point. The trace you retrieve from the Decision Point is internally processed using the processDecisionTrace()
method, thus you do not need to call this method to process the decision trace when you are working with a decision trace from a Decision Point.
Table 7-2 shows the Decision Point API methods for setting decision trace options. For more information on these methods, see Oracle Fusion Middleware Language Reference Guide for Oracle Business Rules
.
Table 7-2 Decision Point Decision Tracing Methods
Method | Description |
---|---|
|
Get the decision trace produced from the call to invoke. Returns |
|
Get the decision trace level to be used by the RuleSession. This value defaults to
Return Type: String |
|
Get the decision trace limit, or maximum number of trace elements which are retrieved for the trace. Return Type: int |
|
Set the decision trace level to be used by the RuleSession. This parameter value is a String. Possible values are:
|
|
Set the decision trace limit, or maximum number of trace elements which are retrieved for the trace. |
Example 7-9 shows a sample usage of decision tracing with DecisionPoint API.
Example 7-9 Using Decision Trace from Decision Point API
DecisionPoint dp = new DecisionPointBuilder() .with(new DictionaryFQN("com.foo", "Bar")) .with("MyDecisionFunction") .setDecisionTraceLevel(DecisionPointBuilder.DECISION_TRACE_DEVELOPMENT) .setDecisionTraceLimit(24000) .build(); ... DecisionPointInstance dpi = dp.getInstance(); dpi.invoke(); DecisionTrace trace = dpi.decisionTrace(); // with aliases replaced