Oracle® Fusion Middleware User's Guide for Oracle Business Rules 11g Release 1 (11.1.1.4.0) E10228-05 |
|
![]() Previous |
![]() Next |
This appendix contains frequently asked questions about Oracle Business Rules.
Section C.2, "What are the Differences Between Oracle Business Rules RL Language and Java?"
Section C.3, "How Does a RuleSession Handle Concurrency and Synchronization?"
Section C.5, "How Do I Use a Property Change Listener in Oracle Business Rules?"
Section C.6, "What Are the Limitations on a Decision Service with Oracle Business Rules?"
Section C.8, "Can I Use Java Based Facts in a Decision Service with BPEL?"
Section C.9, "How Do I Enable Debugging in a BPEL Decision Service?"
Section C.10, "How Do I Support Versioning with Oracle Business Rules?"
Section C.11, "What is the Priority Order Using Priorities with Rules and Decision Tables?"
Section C.12, "Why do XML Schema with xsd:string Typed Elements Import as Type JAXBElement?"
Section C.13, "Why Are Changes to My Java Classes Not Reflected in the Data Model?"
Section C.14, "How Do I Use Rules SDK to Include a null in an Expression?"
Section C.15, "Is WebDAV Supported as a Repository to Store a Dictionary?"
Section C.16, "Using a Source Code Control System with Rules Designer"
When a Java object has been asserted and then the object is changed without using the modify action, the object must be re-asserted in the Rules Engine. Therefore, if a rule associated with the changed Java object does not fire, this means that the Rules Engine did not reevaluate any rule conditions and did not activate any rules. Thus, when a Java object changes without using the modify action, the object must be re-asserted in the Rules Engine.
For more information on the differences between Oracle Business Rules RL Language and Java, see Appendix A in Oracle Fusion Middleware Language Reference Guide for Oracle Business Rules.
Method calls on an Oracle Business Rules RuleSession object are thread-safe such that calls by multiple threads do not cause exceptions at the RuleSession level. However, there are no exclusivity or transactional guarantees on the execution of methods. The lowest-level run
method in the Rules Engine is synchronized, so two threads with a shared RuleSession
cannot both simultaneously execute run
. One call to run
must wait for the other to finish.
Oracle Business Rules functions are not synchronized by default. Like Java methods, Oracle Business Rules functions can execute concurrently and it is the programmer's responsibility to use synchronized blocks to protect access to shared data (for instance, a HashMap
containing results data).
Any set of actions that a user wants to be executed as in a transaction-like form must synchronize around the shared object. Users should not synchronize around a RuleSession
object because exceptions thrown when calling RuleSession
methods may require the RuleSession
object to be discarded.
For most uses of a RuleSession
object in Oracle Business Rules, each thread or servlet instance should create and use a local RuleSession
object. This usage pattern is roughly analogous to using a JDBC connection in this manner.
The following examples demonstrate how to use a shared RuleSession
object.
For the case where Thread-1 includes the following:
ruleSession.callFunctionWithArgument("assert", singleFact1); ruleSession.callFunctionWithArgument("assert", singleFact2);
and Thread-2 includes the following:
ruleSession.callFunction("run"); ruleSession.callFunction("clear");
In this case, the execution of the two threads might proceed as shown in Example C-1.
Example C-1 Using a Shared RuleSession Object in Oracle Business Rules
Thread-1: ruleSession.callFunctionWithArgument("assert", singleFact1); Thread-2: ruleSession.callFunction("run"); Thread-2: ruleSession.callFunction("clear"); Thread-1: ruleSession.callFunctionWithArgument("assert", singleFact2);
In Example C-1, the two facts Thread-1 asserted are never both in the RuleSession
during a call to run
. Notice also that only one thread calls the run
method. If you use a design where multiple threads can call run
on a shared RuleSession
, this can create extremely hard to find bugs and there is usually no gain in performance.
All accesses to a shared RuleSession
object must be synchronized to ensure the intended behavior. However, a RuleSession
instance may throw an exception and not be recoverable, so do not use this object as the synchronization object. Instead, use another shared object as the synchronization point.
One can envision a shared server process producer-consumer model for RuleSession
use. In this model, multiple threads assert facts to a shared RuleSession
and one thread periodically calls run
, reads any results, and outputs them. This ensures that thread conflicts cannot occur, because the two code segments must be executed serially and cannot be intermingled. For example, the code with shared objects, producer code, and consumer code in Example C-2, Example C-3, and Example C-4.
Example C-2 RuleSession Shared Objects
RuleSession ruleSession; Object ruleSessionLock = new Object();
Example C-3 RuleSession Producer Code
public String addFacts(FactTypeA fa, FactTypeB fb, FactTypeC fc){ String status = ""; synchronized(ruleSessionLock){ try { ruleSession.callFunctionWithArgument("assert", fa); ruleSession.callFunctionWithArgument("assert", fb); status = "success"; } catch (Exception e) { // a method that creates a new RuleSession loads it with rules initializeRuleSession(); status = "failure"; } return status; }
Example C-4 RuleSession Consumer Code
public List exec(){ synchronized(ruleSessionLock){ try { ruleSession.callFunction("run"); List results = (List)ruleSession.callFunction("getResults"); ruleSession.callFunction("clearResults"); return results; } catch (Exception e) { // a method that creates a new RuleSession loads it with rules initializeRuleSession(); return null; } } }
Note: When multiple threads are sharing aRuleSession object, if more than one of the threads calls the run method, this can create extremely hard to find bugs and there is usually no gain in performance. |
When working with facts, there are cases where the runtime behavior of Oracle RL may produce surprising results.
Consider the Oracle RL code in Example C-5.
Example C-5 Self-Join Using Fact F
class F {int i; }; rule r1 { if (fact F f1 && fact F f2) { println("Results: " + f1.i + ", " + f2.i); } } assert(new F(i:1)); assert(new F(i:2)); run();
How many lines print in the Example C-5 output? The answer is 4 lines because the same fact instance can match for both f1
and f2
.
Thus, Example C-5 gives the following output:
Results: 2, 2 Results: 2, 1 Results: 1, 2 Results: 1, 1
Using the same example with a third F
, for example (assert(new F(i:3));
) then nine lines are printed and if, at the same time, a third term && fact F F3
is added then 27 lines are printed.
If you are attempting to find all combinations and orders of distinct facts, you need an additional term to in the test, as shown in Example C-6.
Example C-6 Find All Combinations of Fact F
rule r1 { if (fact F F1 && fact F F2 && F1 != F2) { println("Results: " + F1.i + ", " + F2.i); } }
The code in Example C-6 gives the following output:
Results: 2, 1 Results: 1, 2
The simplest, although not the fastest way to find all combinations of facts, regardless of their order, is to use the code shown in Example C-7.
Example C-7 Finding Combinations of Fact F
rule r1 { if (fact F F1 && fact F F2 && id(F1) < id(F2)) { println("Results: " + F1.i + ", " + F2.i); } }
Because the function id()
shown in Example C-7 takes longer to execute in a test pattern than a direct comparison, the fastest method is to test on a unique value in each object. For example, you could add an integer value property "oid" to your class that is assigned a unique value for each instance of the class.
Example C-8 shows the same rule using the oid value.
Example C-8 Fast Complete Comparison
rule r1 { if (fact F F1 && fact F F2 && F1.oid < F2.oid) { println("Results: " + F1.i + ", " + F2.i); } }
This problem may also arise if you attempt to remove all duplicate facts from the Oracle Rules Engine, using a function as shown Example C-9.
Example C-9 Retracting Duplicate Facts Incorrect Sample
rule rRemoveDups { if (fact F F1 && fact F F2 && F1.i == F2.i) { retract(F2); } }
However, this rule removes all facts of type F
, not just the duplicates because F1
and F2
may be the same fact instance. Example C-10 shows the correct version of this rule.
The Oracle Rules Engine supports the Java PropertyChangeListener
design pattern. This allows an instance of a Java fact that uses the PropertyChangeSupport
class to automatically notify the Oracle Rules Engine when property values have changed. Java facts are not required to implement this pattern to be used by Oracle Rules Engine.
Typically, changes made to values of a property of a Java object that has previously been asserted to the Oracle Rules Engine requires that the object be re-asserted in order for rules to be reevaluated with the new property value. For properties that fire PropertyChangeEvent
, changing the value of those properties both changes the value and re-asserts the fact to the Oracle Rules Engine.
To implement the PropertyChangeListener
design pattern in a class, do the following:
Import this package in the class:
import java.beans.PropertyChangeSupport;
Add a private member variable to the class:
private PropertyChangeSupport m_pcs = null;
In the constructor, create a new PropertyChangeSupport
object:
m_pcs = new PropertyChangeSupport(this);
Then for each setter, add the call to firePropertyChange
:
public void setName( String name ){ String oldVal = m_name; m_name = name; m_pcs.firePropertyChange( "name", oldVal, m_name ); }
Implement addPropertyChangeListener
method (delegate to m_pcs
):
public void addPropertyChangeListener(PropertyChangeListener pcl){ m_pcs.addPropertyChangeListener( pcl ); }
Implement removePropertyChangeListener
method (delegate to m_pcs
):
public removePropertyChangeListener(PropertyChangeListener pcl){ m_pcs.removePropertyChangeListener( pcl ); }
When deciding whether to design your application to always explicitly re-assert modified objects or implement the PropertyChangeListener
design pattern, consider the following:
Explicitly re-asserting modified objects allows a user to group several property changes and making them visible to the rules all at once. This is most useful when a concurrent thread is executing rules, and the rules should see only a complete group of property changes.
Explicit assert reduces the computational cost of rule re-evaluation when multiple properties are changed. If multiple properties are changed at the same time, this results in multiple re-evaluations of rule conditions that reference the fact type. This occurs because each property change event results in a re-assertion of the object. Using an explicit assert instead of the PropertyChangeListener
pattern eliminates this extra computational cost.
Explicit assert is required when a rule modifies a fact that is also tested in its condition, but the automatic reassert triggered by the PropertyChangeListener
before a guard condition property is set would cause the rule to refire itself endlessly.
Explicit assert must be used when modifying Oracle RL facts and XML facts, because these cannot be defined to support the PropertyChangeListener
design pattern.
PropertyChangeListener
-enabled facts allow a Java application to communicate property changes to the rule engine without having to change the application to perform explicit asserts. This also means that code that modifies a property of an object does not need to have a reference to the RuleSession
object in scope.
PropertyChangeListener
support prevents the common error of neglecting to re-assert a fact after changing its properties.
There are some limitations for using Business Rules with a BPEL process, including the following:
Only visible XML fact types may be specified as the input for a decision service.
Only visible XML fact types may be specified as the output of a decision service.
For an additional restriction, see Appendix D, "How Are Decision Service Input Output Element Types Restricted?".
For information on setting XML fact type visible option, see Section 3.2, "Working with XML Facts".
You do not actually put Java code in a rule. However, you can invoke a Java method from a rule condition or action.
Oracle BPEL PM can invoke only decision functions exposed as a decision service, and this means that the decision function inputs and outputs must be XML fact types.
You can use an existing ruleset or decision function that uses Java fact types if you convert the input XML facts to Java facts. For example, you could create some rules in a ruleset, named convertFromXML
, and put this ruleset before the Java ruleset in the decision function ruleflow. Similarly, you could create a ruleset to convert from Java facts to output XML facts and put this ruleset after the Java ruleset in the decision function ruleflow.
Alternatively, if your rules use only properties, and no methods or fields, from the Java fact types you can replace the Java fact types with XML fact types as follows:
Delete the Java fact types (first making careful note of the aliases of the fact types and properties).
Import similar XML fact types and edit the aliases of the fact types and properties to be the same as the deleted Java fact types and properties.
To enable debugging output during ruleset execution for a BPEL Decision Service, you enable the SOA rules logger. When the SOA rules logger is set to TRACE
level then the output of watchAll
is logged to the SOA diagnostic log. When you change the logging level using Fusion Middleware Control Console, you do not need to redeploy the application to use the specified level.
For information on using the SOA oracle.soa.service.rules and oracle.soa.services.rules.obrtrace loggers, see Oracle Fusion Middleware Administrator's Guide for Oracle SOA Suite and Oracle Business Process Management Suite .
Versioning is supported in Oracle Business Rules in two ways:
At design time, the dictionary is stored as an XML file in a JDeveloper project. The dictionary can be versioned in a source control system in the same way as any other source file.
At runtime, the dictionary is stored in MDS. If MDS is database backed then versioning is supported using MDS.
Note: It is possible for a server application to respond to dictionary changes as they are made visible to the application in MDS. The rule service engine (decision service) does this automatically. For non-SCA application, this can be done using the RuleRepository interface. At this time, they way to support an "in-draft" version is by using the sandbox feature of MDS. The Oracle Business Rules RuleRepository interface supports this.
The priority for rules and decision tables is highest to lowest, with the higher priority rule or Decision Table executing first. For example, if you create rules with priorities 1-4, they would be executed in the execution priority order 4,3,2,1. Using Rules Designer you can select a priority from a predefined named priority list or enter a positive or negative integer to specify your own priority level. The default priority is medium
(with the integer value 0). For more information, see Section 4.5.5, "How to Set a Priority for a Rule".
Note, however, you should try to avoid priorities as much as possible since they break the purely declarative model of rules. If you find yourself using a lot of priorities, then generally it is best to try to restructure your rule patterns and tests to avoid conflicts, or divide the rules into multiple rulesets using ruleflow if they are intended to be run in a certain order. A conflict is a case when more than one rule in a ruleset is able to fire. For example, if a "gold customer" rule says to make a customer that spends over $1000 a gold customer, and a "silver customer" rule says to make a customer that spends over $500 a silver customer, then when a customer spends $1100 there is a conflict. Rather than prioritize the rules, it is more declarative to change the "silver customer" rule to test for customers that spend between $500 and $1000. This conflict analysis and conflict avoidance is particularly easy if you use Decision Tables. For more information on Decision Tables, see Chapter 5, "Working with Decision Tables".
You use ruleflow, that is the ruleset stack, to order rulesets. For information on working with the ruleset stack, see Oracle Fusion Middleware Language Reference Guide for Oracle Business Rules.
According to the JAXB 2.0 spec, the default type mapping for elements that have minOccurs="0"
and nillable="true"
is JAXBElement<
T>
, where T is the default mapping of the type defined for the element. For example, xsd:string
maps to JAXBElement<String>
, xsd:int
maps to JAXBElement<Integer>
, and xsd:integer
maps to JAXBElement<BigInteger>
. This is because nillable="true"
means the user has defined a semantic difference between a element not being defined in a document, with minOccurs=0
, it does not have to be defined, and an element being defined but having the attribute nil="true"
. This is a subtle difference and is often used to define the difference between an unknown value and a value known to be "no value".
To use the JAXBElement-typed property in a rule, the property must be first checked for non-null, and then the "value" property or getValue()
method can be used retrieve a value of the underlying type:
fact FactType1 && FactType1.prop1 != null && FactType1.prop1.value == "abc"
Alternatively, you may want to define a customized JAXB binding so nillable elements are mapped to type T rather than JAXBElement<
T>
. However, this is a lossy conversion, as you no longer are able to determine the difference between a non-existent element and a nil one. This does make the nillable attribute less useful, but it does allow you to explicitly define an element as nil in your document, similarly to how in Java an Object-typed field is initialized to null by default or you can explicitly initialize it to null.
There are several ways to do this. In both cases, add these attributes to the top-level xsd:schema
element start tag:
xmlns:jaxb="http://java.sun.com/xml/ns/jaxb" jaxb:version="2.0"
To specify ALL properties to use the binding, add this immediately inside the xsd:schema opening tag:
<xsd:annotation> <xsd:appinfo> <jaxb:globalBindings generateElementProperty="false"/> </xsd:appinfo> </xsd:annotation>
To specify only specific properties use the binding, add an annotation like this to each desired element:
<xsd:element name="stringElement2" type="xsd:string" minOccurs="0" nillable="true"> <xsd:annotation> <xsd:appinfo> <jaxb:property generateElementProperty="false" /> </xsd:appinfo> </xsd:annotation> </xsd:element>
Add the definitions to an external customizations file and pass it as an argument when adding the schema to the datamodel. This can only be done when programmatically calling the SchemaBrowser class and is not exposed in Rule Designer.
Do not import classes that have been compiled into the "SCA-INF/classes" directory. Classes in this directory cannot be reloaded into the datamodel when they change.
You can use the following Rules SDK code to include a null value:
SimpleTest test = pattern.getSimpleTestTable().add(); test.getLeft().setValue(attr); test.setOperator(Util.TESTOP_NE); test.getRight().setValue("null");
The Web Distributed Authoring and Versioning (WebDAV) repository is not supported to store a dictionary in Oracle Fusion Middleware 11g Release 1 (11.1.1) Oracle Business Rules. Oracle Business Rules supports using an MDS (file backed or Database backed) repository for storing dictionaries.
There are special considerations when you use Rules Designer and a source control system, such as CVS or Subversion. When you use a source code control system with Rules Designer you need to specify that rule dictionary files in your project are recognized as "binary" files instead of "text" files. The rule dictionary files are XML documents and by default the source code control system treats these files as text files. However, rule dictionary files cannot be merged because the files contain semantic structure. If a rule dictionary file is treated as a text file and then changed, the source control system attempts to merge the file with a "trivial" merge. Using a trivial merge creates a semantically invalid dictionary file which cannot be unmarshalled into a RuleDictionary object.
Thus, when you use a source code control system with rule dictionary files, .rules files, you need to make sure the source code control system treats the files as binary files. There are configuration options you need to set to specify that the system treats dictionary files as binary files. For example, in the Subversion source code control system you can set the MIME type with the svn:mime-type
file property. For more information, see
When you set the source code control system options to specify the binary file type, this allows the source code control system, for example tortoiseSVN, to treat the rules dictionary files correctly, as binary files.