OFM 12C: An example of the Business Rule Engine with Java facts

One of the components of the Oracle SOA Suite is the Business Rule Engine. The Oracle Business Rules engine….and I quote ‘allows the externalization of specific business logic and
parameters. Business analysts can easily define, update, and manage key parameters and decision trees that are likely to change based on business evolution (for instance discount levels, credit rates, etc.) without having to involve IT and developers.

This all sounds pretty nice but I haven’t seen it implemented anywhere yet but lately I have been working for a customer which has a good case to actually use the BRE so I had time look into it. In this blog I will show you a basic way to use the BRE using a decisions table and with the use of some custom Java facts.

A simple explanation: We have persons who wants to insure themselves from a certain date with a certain type of insurance. We are going to use the BRE to determine whether they are allowed to do this. We are going to use a decision table instead of if-then-else structures and in the decision table we are going to use java facts which we created to make certain functions more easy to define.

Lets start of with a simple SOA project. I created a simple schema with a input and output.



  
    
      
        
        
        
          
            
              
              
              
            
          
        
      
    
  
  
    
      
        
      
    
  

Lets add a Mediator to the composite and expose it as a web service.

createMediator

Now lets create the BRE component. Drag the Business Rule into the composite. Input a name and select the input and output where you can select the request and response we have previously defined in the insurance.xsd and press OK.

createBR

If you edit the Business Rule you have different tabs.

BRtabs

Settings has some general settings as expected. The Facts tabs hold the request and response data structures which you will be using. These should already have the breExampleRequest and breExampleResponse in there. Also the checkbox visible should have been checked as we are going to use them.

BRfacts

In the Globals tab we can define globals which we might be using. We are going to define here the AdultAge.

BRglobal

Now lets create the actual ruleset. Go to the generated RuleSet1. Rename the RuleSet1 to InsuranceRuleSet. Then add a DecisionTable.

BRruleset

When you create the DecisionTable it opens automatic. A decision table is like a matrix. You have Conditions which together with Rules result into Actions. Lets insert a Condition first.

BRCondition

Now select the InsuranceType from the breExampleRequest.

BRConditionConfig

After this we can input the possible value which will be “Basic”. Now lets attach an action to it.

BRAction

Double-click the action and the popup opens. Here we can select from the facts the breExampleResponse and select the fact its parameterized.

BRActionConfig

Now press OK. Then add a value to the action which will be the simple boolean value “true”.

BRActionValue

Now we have 1 Rule but we want 3 so we at least can see the difference which different inputs so lets create an two extra Rules with the following values:

R1R2R3

So basically this ruleset should return “true” when I pick the insurance of type “Basic” and “false” if pick the insurance of type “Better” or “Best”. Before we connect it all together we have to define the interface for the BusinessRule component on the Decision Functions tab. Here we will configure the webservice interface which we will use to call the business rule. Go to the Decisions Functions tab and double click on the already created decision function. In the next popup you can define the name, the name of the operation of the webservice and the input and output. Also make sure the ruleset is selected on the Rule Sets & Decision Functions tab.

DecisionFunction

Now connect the mediator to the business rule. As you can see the BusinessRule component as an interface to which we can connect.

connecting

Make sure you map the input and output in the mediator.

Mapping

Now that it is all hooked up together, lets deploy it and give it a spin. I use SoapUI to call the service and lets see what happens.

SoapUITest

As you can see, we get TRUE for the ‘Basic’ insurance but FALSE for the ‘Better’ and ‘Best’ option. So now we have seen how to make use of a simple decision table. In a business environment, you probably want some more complex functions in the decision table. There are possibilities to make use of functions but from what I have seen, these are quite limited and not very clear on their usages. Another option is to write functions yourself using Java. You create a jar file which contain classes with your helper functions. I will demonstrate below how this works.

First create a simple class with a helper function. I created a simple class with a static function which determines if a person is an adult on a certain date. This will look like this:

package nl.redrock.bre;

import java.util.Calendar;
import javax.xml.datatype.XMLGregorianCalendar;

/**
 *
 * @author Hugo Hendriks
 */
public class Insurance {
    
    /**
     * Check whether the person is already an adult at the startDate
     * @param aStartDate the starting date
     * @param aBirthDate the day of birth
     * @param aAdultAge the legal age a person becomes an adult
     * @return true or false
     */
    public static boolean isAdultAtDate(XMLGregorianCalendar aStartDate, XMLGregorianCalendar aBirthDate, int aAdultAge){
     
        boolean result = false;
        
        Calendar birthdayLegalAge = aBirthDate.toGregorianCalendar();
        birthdayLegalAge.add(Calendar.YEAR, aAdultAge);
        
        if(birthdayLegalAge.before(aStartDate.toGregorianCalendar())){
            result = true;
        }
        return result;
    }
    
}

Keep in mind that xsd:date is tranformed to an XMLGregorianCalendar object. As a good practice write a JUnit test for it to check if it works as desired and build a jar. I am using maven so this would be the outcome.

Scanning for projects...

Using the builder org.apache.maven.lifecycle.internal.builder.singlethreaded.SingleThreadedBuilder with a thread count of 1
                                                                        
------------------------------------------------------------------------
Building BRE-Test 1.0
------------------------------------------------------------------------

--- maven-clean-plugin:2.5:clean (default-clean) @ BRE-Test ---
Deleting D:\Amazon EC2\SVN\trunk\java\BRE-Test\target

--- maven-resources-plugin:2.6:resources (default-resources) @ BRE-Test ---
Using 'UTF-8' encoding to copy filtered resources.
skip non existing resourceDirectory D:\Amazon EC2\SVN\trunk\java\BRE-Test\src\main\resources

--- maven-compiler-plugin:2.5.1:compile (default-compile) @ BRE-Test ---
Compiling 1 source file to D:\Amazon EC2\SVN\trunk\java\BRE-Test\target\classes

--- maven-resources-plugin:2.6:testResources (default-testResources) @ BRE-Test ---
Using 'UTF-8' encoding to copy filtered resources.
skip non existing resourceDirectory D:\Amazon EC2\SVN\trunk\java\BRE-Test\src\test\resources

--- maven-compiler-plugin:2.5.1:testCompile (default-testCompile) @ BRE-Test ---
Compiling 1 source file to D:\Amazon EC2\SVN\trunk\java\BRE-Test\target\test-classes

--- maven-surefire-plugin:2.12.4:test (default-test) @ BRE-Test ---
Surefire report directory: D:\Amazon EC2\SVN\trunk\java\BRE-Test\target\surefire-reports

-------------------------------------------------------
 T E S T S
-------------------------------------------------------
Running nl.redrock.bre.InsuranceTest
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.098 sec

Results :

Tests run: 1, Failures: 0, Errors: 0, Skipped: 0


--- maven-jar-plugin:2.4:jar (default-jar) @ BRE-Test ---
Building jar: D:\Amazon EC2\SVN\trunk\java\BRE-Test\target\BRE-Test-1.0.jar
------------------------------------------------------------------------
BUILD SUCCESS
------------------------------------------------------------------------
Total time: 4.922 s
Finished at: 2015-01-09T12:17:07+01:00
Final Memory: 13M/126M
------------------------------------------------------------------------

Now I have a jar containing the class which I want to use but how do I get in into my designtime environment? Very simple…just drop the jar file into the \SOA\.rulesdesigner\jaxb_classes folder….in my case BreTestProject\SOA\.rulesdesigner\jaxb_classes. Now lets go back to the Business Rule component. Go to the Facts tab and the Java Fact tab below and click Add.

AddJavaFact

In the popup select Add and select the .jar file we have just copied. When selected, the tree below should display the classes. Select the Insurance class and click OK.

SelectJar

You should see the class now in the list of Java facts. Now for the magic. Go to your decision table and add a new condition. Double click it and click the calculator icon on the top right. In the popup select the method from the Functions tab and select the right input parameters from the variables tab. We also put in there the AdultAge from the Constant tab we had defined before.

JavaFactCondition

The table will look like this now.

JavaFactDT

Now compile and deploy again. Don’t forget to add the jar into the /lib directory and restart your domain. When we run our SoapUI test you will see it also takes the Java check in the decision table into account.

Down the line the business rule component can help you, by putting some business logic into your middleware layer which can be edited through the soa-composer. The Java facts work quite nicely when the IDE is stable and the decision table works as it should. Sorting certain business functionality in the decision table instead of the decision tables takes some head twisting.

Thing to take into consideration:

  • There is a memory leak in the DecisionTable IDE editor. When you open it, the table will start to eat resources and eventually crash especially when you have a lot of columns and rows. There is a patch available but will also introduce a new bug which crashes the IDE completely! Un-patching using opatch seemed to get rid of the crash and of the memory leak.
  • There are classes being generated when you define your interface. If you want to change the interface later on, you have to delete and create the classes again or else you will run into errors here.
  • The BRE component has it’s fits. Sometimes you will have strange glitches or unexplainable behavior. I think this comes to the fact that not many customers use it so it doesn’t receive much love from Oracle 🙂

5 Replies to “OFM 12C: An example of the Business Rule Engine with Java facts”

  1. Hey Hugo!

    First of all, thanks for this guide as it was really helpful.

    I ran into some issues, I guess, minor issues but annoying:

    When you say “add the jar into the /lib directory and restart your domain”, did you mean to add the jar path to the project properties and, after deploying, restar the SOA domain?

    I ask, because after running the SOA Composite, the following error is raised: default/CompositeName!1.0*soa_a9fdeea5-ba7d-46aa-adb0-255371c9d53d/RuleName

    Thanks in advance.

    • Hi Emmanuel, you physical have to move the created jar into the lib folder of your WL domain. A reference is not enough. Also the errors you get in the EM when calling the BRE will allways be very unhelpfull. If you want a bit more information you can look in the server log. Usually there is a bit more information on the error.

      regards,
      Hugo

  2. Pingback: SOA Suite 12C : Patch to fix the JDev business rule component performance -

  3. Pingback: SOA Suite 12C : Patch to fix the JDev business rule component performance - REDROCK

  4. Pingback: patch-fix-jdev-business-rule-component-performance

Leave a Reply

Your email address will not be published. Required fields are marked *

*

This site uses Akismet to reduce spam. Learn how your comment data is processed.