Foreword
In my previous blog post on Creating and Testing Complex Business Rules in Oracle SOA Suite 11g, I have pretty much covered how we can create a fairly complex rule and test it in multiple ways. In the same article I have explored how we can Test business rules at design time using RL functions or use Enterprise Manager to test them. We have also seen as how rules in Oracle SOA Suite can be exposed as standard soap service and thus can be invoked by multiple parties.
In my previous blog post on Creating and Testing Complex Business Rules in Oracle SOA Suite 11g, I have pretty much covered how we can create a fairly complex rule and test it in multiple ways. In the same article I have explored how we can Test business rules at design time using RL functions or use Enterprise Manager to test them. We have also seen as how rules in Oracle SOA Suite can be exposed as standard soap service and thus can be invoked by multiple parties.
However one practical problem in using these above approaches is that we may have Business Rules that might run into hundreds of assertions and rule conditions that may prove to be a nightmare to test.
So how do we go about it. One elegant way would to create Test cases for business rules in SOAP UI and test the rule service like we generally test any other soap services. SOAP UI provides a wonderful framework for testing soap based services where in we can use assertions on the responses and even print test reports.
Another approach and an old school one is to write Java classes to invoke your Business rules. Oracle Business rules can be instantiated and invoked using Java code as well. This opens up the possibility of creating JUnit type test cases and test suites for your Business rules and test them extensively. This way we can further use any code coverage tool also to see how much of the rules are we testing already.
In the following article I will show how we can write a custom Java class to invoke and test a Business rule created in Oracle SOA Suite. For the purpose of this tutorial I will use the same example and code as used in the previous article about Oracle Rules. You can find the article at this link and even download the source code.
Prerequisites
Oracle JDeveloper 11g with SOA Suite Extensions
The Solution
Download the Business rules sample from the previous tutorial from the above link and Open it in JDeveloper.
Create a New Java Project inside the same application i.e BusinessRuleApplication wherein we can create our Java class to run our business rules.
Now Create a new Java Class called RuleTester in the Java Project that has been created.
To create a Java Class that can initialize and invoke Oracle Business rules we would need to add the standard Oracle Rules jars to our project’s classpath. Add the following libraries to the project dependencies as shown in the figure.
Also note that when we create any Business rules in Oracle SOA Suite 11g in JDeveloper it creates the JAXB classes for them. Make sure that even these classes are added to the project classpath.
The libraries to be added are also mentioned in the table below.
Oracle Rules Editor Component |
Oracle Rules Dictionary Component |
Oracle Rules |
JDeveloper Runtime |
Java EE 1.5 |
Oracle JDBC |
ADF Model Runtime |
ADF DVT Faces Databinding Runtime |
Trinidad Runtime 11 |
Oracle JEWT |
We are good now as far as creating the Project and adding rules libraries is concerned. Now let us jump on to creating the Java code to invoke and test the rule.
The code used to test the decision function created in the rule is given below.
package com.beatechnologies.sample.rules.testclient; import com.wordpress.beatechnologies.gradeallocation.CandidateInformationType; import com.wordpress.beatechnologies.gradeallocation.ObjectFactory; import com.wordpress.beatechnologies.gradeallocation.CandidateGradeType; import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileReader; import java.io.IOException; import java.io.Reader; import java.io.Writer; import java.util.ArrayList; import java.util.List; import javax.xml.bind.JAXBContext; import javax.xml.bind.JAXBElement; import javax.xml.bind.JAXBException; import javax.xml.bind.Marshaller; import javax.xml.bind.Unmarshaller; import oracle.rules.sdk2.decisionpoint.DecisionPoint; import oracle.rules.sdk2.decisionpoint.DecisionPointBuilder; import oracle.rules.sdk2.decisionpoint.DecisionPointDictionaryFinder; import oracle.rules.sdk2.decisionpoint.DecisionPointInstance; import oracle.rules.sdk2.dictionary.RuleDictionary; import oracle.rules.sdk2.exception.SDKWarning; public class RuleTester { public RuleTester() { super(); } private DecisionPointInstance decPointInstance = null; // Initialize the Rule Tester Class public void initialize(String ruleDictionary, String decisionFunctionName) throws Exception { if (ruleDictionary == null || decisionFunctionName == null) throw new Exception("Enter rule dictionary location and decision service name for Rule Engine to successfully initialize."); // Load Decision Point using Dictionary on File System DecisionPoint decPoint =new DecisionPointBuilder().with(decisionFunctionName).with(loadRuleDictionary(ruleDictionary)).build(); decPointInstance = decPoint.getInstance(); System.out.println("Rule Tester Class is now Initialized"); return; } // Loads the rule dictionary from the specified dictionaryPath to return a rule dictionary object private static RuleDictionary loadRuleDictionary(String dictionaryPath) throws Exception { RuleDictionary ruledictionary = null; Reader reader = null; Writer writer = null; try { reader = new FileReader(new File(dictionaryPath)); ruledictionary = RuleDictionary.readDictionary(reader, new DecisionPointDictionaryFinder(null)); List<SDKWarning> warnings = new ArrayList<SDKWarning>(); ruledictionary.update(warnings); if (warnings.size() > 0) { System.err.println("Rule Dictionary returned the followingv validation warnings: " + warnings); } } finally { if (reader != null) { try { reader.close(); } catch (IOException ioe) { ioe.printStackTrace(); } } if (writer != null) { try { writer.close(); } catch (IOException ioe) { ioe.printStackTrace(); } } } return ruledictionary; } // Executes the Rule Decision Function by taking candidateInformationlist input and return the results. public List runRules(ArrayList candidateInformationInputList) throws Exception { List<Object> candidateGradeList = null; if (decPointInstance == null) throw new Exception("RuleTester not intialized."); System.out.println("Running Rules"); if (candidateInformationInputList != null) { decPointInstance.setInputs(candidateInformationInputList); // invoke the decision point with Candiate Marks Information candidateGradeList = decPointInstance.invoke(); if (candidateGradeList == null || candidateGradeList.isEmpty()) { System.out.println("RuleTester: No results returned by Rules Service"); } else System.out.println("RuleTester: " + candidateGradeList.size() + " result(s) returned."); } return candidateGradeList; } // Creates a test data candidate Information object for input to the rule engine which is an XML type Fact. public static CandidateInformationType createTestData() throws JAXBException { // Create the sample input XML as string String candidateMarksInformation = "<exam:CandidateInformation xmlns:exam=\"http://beatechnologies.wordpress.com/GradeAllocation\">\n" + " <exam:name>Arun</exam:name>\n" + " <exam:rollNumber>7000001</exam:rollNumber>\n" + "<exam:class>X</exam:class>\n" + " <exam:section>SCIENCE</exam:section>\n" + " <exam:remarks>Good</exam:remarks>\n" + "<exam:subject>\n" + "<exam:subjectName>ENGLISH</exam:subjectName>\n" + "<exam:subjectCode>001</exam:subjectCode>" + "<exam:subjectMark>85</exam:subjectMark>" + "</exam:subject>" + "<exam:subject>" + "<exam:subjectName>GEOGRAPHY</exam:subjectName>" + "<exam:subjectCode>002</exam:subjectCode>" + "<exam:subjectMark>85</exam:subjectMark>" + "</exam:subject>" + "</exam:CandidateInformation>"; CandidateInformationType candidateInformation = null; // XML Facts are represented by JAXB types. // Use the Generated types to parse an xml test message into an JAXB element JAXBContext jaxbContext = JAXBContext.newInstance("com.wordpress.beatechnologies.gradeallocation"); Unmarshaller unMarsh = jaxbContext.createUnmarshaller(); ByteArrayInputStream is =new ByteArrayInputStream(candidateMarksInformation.getBytes()); Object obj = unMarsh.unmarshal(is); JAXBElement jaxbElement = (JAXBElement)obj; candidateInformation = (CandidateInformationType)jaxbElement.getValue(); // Print Candidate Information input to stdout by marshalling it back to xml System.out.println("Candidate Information Avaliable Is:"); Marshaller marshaller = jaxbContext.createMarshaller(); marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT,Boolean.TRUE); marshaller.marshal(obj, System.out); return candidateInformation; } // Main funtion to Test the Business Rule as a Java class in JDeveloper public static void main(String[] args) throws Exception { RuleTester tester = new RuleTester(); // Load the Rules Dictionary, provide location of .rules file and Decision Function name tester.initialize("C:\\JDeveloper\\mywork\\BusinessRulesApplication\\BusinessRulesProject\\" + "oracle\\rules\\com\\obr\\sample\\app\\GradeAllocationRule.rules","GradeAllocationRule_decideGrade"); // Create an array of inputs that match the CandidateInformationType xml used in the Rules Decision Function ArrayList candidateInformationInputList = new ArrayList(); candidateInformationInputList.add(createTestData()); // Execute the Rules passing in the candidateInformation input List candidateGradeList = tester.runRules(candidateInformationInputList); if (candidateGradeList != null) { //Candidate Grade Object returned from rules decision function CandidateGradeType result = (CandidateGradeType)candidateGradeList.get(0); // Print Candidate Grade XML returned from Rules Engine to stdout JAXBContext jaxbContext =JAXBContext.newInstance(CandidateGradeType.class); Marshaller marshaller = jaxbContext.createMarshaller(); marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT,Boolean.TRUE); ObjectFactory objFactory = new ObjectFactory(); marshaller.marshal(objFactory.createCandidateGrade(result), System.out); } } }
On a high level here is what the code does
Initializes the Decision function inside the Rule project. We have to provide the absolute path to .rule file that gets created in the Rules project directory and also the name of decision service.
It then reads a hard coded xml string that is a mocked input to the decision service. However we cannot pass a string content to the rule function. We have to unmarshall the xml string into a JAXB object before we can send it to the service.
You can see more about JAXB (Java Architecture for XML Binding) at the below link
We invoke the business rule service and get the response in a list. Simply take the first child of the list and marshall it into an xmlstring from a JAXB object.
Also write some sysouts to display the input as well as the output to the console.
We are now ready to run this Java class and test the rule. Remember we do not need to have a server runtime for this. We can simply run this as a simple java program.
After the Java class compiles and is executed we can see the following output in the console
We can now create a JUNIT test suite to read from multiple input files and assert the rule results. This approach would be very beneficial if we have hundreds of rules created in a composite and we require a through testing for them.
The Java Project created for the tutorial can be downloaded from here.
No comments:
Post a Comment