Tuesday May 21, 2013

Enable developers to deploy SOA Suite Composites Using Hudson

Lately I have been working with a customer who wants to enable their developers to deploy their own
SOA suite composites to a protected QA Server based on a checkout from Subversion.

As part of the customer's current composite promotion process, the developers check their unit tested code
into Subversion and then email a build manager to checkout, build and deploy the code to the QA environment.
This approach introduces delay and places a signification administration burden on the person in the build manager role.

This blog post provides an example of how Hudson can be used as a self service web page that allows developers
to initiate the automated checkout, build and deploy process.
It may be counter-intuitive to use Hudson as a manual build tool, but it fits the requirement nicely.  

Read the full post here

Monday Feb 18, 2013

Create Oracle Business Rules using the Java API

SOA Suite and BPM Suite 11g developers and end users normally develop and edit Rule sets
using graphical tools such as the JDeveloper Rule editor and the SOA Composer Web Application.

But Rules can also be created and manipulated using the Oracle Business Rules Java API,
in fact both of the graphical editors leverage the API to perform their work.

This blog post provides a Rules API Example that illustrates how to:

  •     Load an existing Rules Dictionary from the file system.
  •     Add a Java Fact to the dictionary Data Model.
  •     Add a new List of Values BucketSet to the Dictionary Data Model.
  •     Create and add a new Rule Set to the dictionary.
  •     Associate the new BucketSet with a given Fact property.
  •     Create a new If / Then Rule
  •     Create a new Decision Table with multiple Rules that leverage the new Bucketset.
  •     Validate and Save the updated Rules Dictionary

Read the full post and get the example here

Thursday Jun 28, 2012

Create an Asynchronous JAX-WS Web Service and call it from Oracle BPEL 11g

This posting is the result of a simple requirement to take an existing JAX-WS Web service,
convert it to be asynchronous and call it from Oracle BPEL 11g

It turned out that this is not a trivial task...
BPEL has some very specific expectations about the WSDL for an asynchronous process.

One approach is to develop the service starting from a WSDL document that meets BPEL's requirements.
This is possible but requires considerable WSDL authoring skills.

The other approach is to modify the WSDL generated by Web Service Annotations in Java code
(Bottom up development) and instruct JAX-WS to use that WSDL instead of dynamically generating one from annotations.

This is the approach taken in this article. This posting details how to:

  • Modify a JAX-WS Web Service developed using a "Bottom up " approach to have an asynchronous method and callback.

  • Call the Asynchronous Service from Oracle BPEL 11g.

Read the full posting here.

Wednesday May 02, 2012

Making sequential calls using Oracle Mediator

Many people are unaware that Oracle mediator can be configured to make sequential calls.
This posting presents an example of a mediator component  that calls a web service,
then combines the web service response data with the original mediator request data
and calls a File adapter with the combined payload.

The composite diagram for this example looks this

Composite.xml

 The processing is completed using a single Mediator component with a single Routing Rule as follows:

Static_Rules

Create this example by following the steps in the full article.


Tuesday Feb 22, 2011

SOA Suite 11g Native Format Builder Complex Format Example

This rather long posting details the steps required to process a grouping of fixed length records using Format Builder.   If it’s 10 pm and you’re feeling beat you might want to leave this until tomorrow. 
But if it’s 10 pm and you need to get a Format Builder Complex template done, read on…


The goal is to process individual orders from a file using the 11g File Adapter and Format Builder


Sample Data
===========
001Square Widget            0245.98
102Triagular Widget         1120.00
403Circular Widget           0099.45
ORD8898302/01/2011
301Hexagon Widget         1150.98
ORD6735502/01/2011


The records are fixed length records representing a number of logical Order records.
Each order record consists of a number of item records starting with a 3 digit number,
followed by a single Summary Record which starts with the constant ORD.


How can this file be processed so that the first poll returns the first order?

001Square Widget            0245.98
102Triagular Widget         1120.00
403Circular Widget           0099.45
ORD8898302/01/2011

And the second poll returns the second order?

301Hexagon Widget           1150.98
ORD6735502/01/2011

Note: if you need more than one order per poll, that’s also possible,
see the “Multiple Messages” field in the “File Adapter Step 6 of 9” snapshot further down.

To follow along with this example you will need

- Studio Edition Version 11.1.1.4.0    with the  
- SOA Extension for JDeveloper 11.1.1.4.0 installed

Both can be downloaded from here:  http://www.oracle.com/technetwork/middleware/soasuite/downloads/index.html

You will not need a running WebLogic Server domain to complete the steps and Format Builder tests in this article.

To see the full steps refer to the Full Post


Thursday Jul 01, 2010

Testing Oracle 11g Business Rules directly from Java


In my earlier post Testing Business Rules with JDeveloper Rule Designer I presented an example of testing rules using the JDeveloper Rules Designer.

While that’s nice, testing rules from a simple Java Class is even better.

This article presents an example of how the 11g rule engine can be called locally from a standalone Java application.

With this technique, the rules and decision functions authored using JDeveloper are loaded from the file system and called directly from Java.

Tests can be run from a command line or from within JDeveloper,  No Application Server is required. 

Let me repeat that part,  because I really like it,
Rule Tests can be run without a running WebLogic Server

This provides a simple and efficient way to write rules, rapidly test them and analyze the results without the need to deploy the composite. 

The approach uses two Java Classes,

The RuleTester.java Class is a generic testing harness that will call the Rules Engine, pass input data and return results.
The RuleTester.java class does not need to be modified by the developer.

The second Java Class must be implemented by the developer.
This class creates the required test data and invokes RuleTester.
An example of both classes are provided below.

The following example illustrates the following points;

  • Loading a rule dictionary from the file system.
  • Creating the necessary Java and XML test facts.
  • Calling the decision service and running the rules
  • Output of the decision service results

RuleTester.java

package rulesproject;

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 oracle.rules.sdk2.decisionpoint.*;
import oracle.rules.sdk2.dictionary.RuleDictionary;
import oracle.rules.sdk2.exception.SDKWarning;
import oracle.rules.rl.RuleSession;
import oracle.rules.rl.extensions.trace.RuleEngineState;
import oracle.rules.rl.extensions.trace.TraceAnalysis;
import oracle.rules.rl.trace.DecisionTrace;
import oracle.rules.rl.trace.FactTrace;
import oracle.rules.rl.trace.RuleTrace;
import oracle.rules.rl.trace.TraceEntry;

/**
 * A Java Class that demonstrates how to load an Oracle 11g Rules Dictionary
 * from a file system location and execute a decision Function.
 * The class is intended to be used only as Rule development / prototyping tool.
 * The rules and decision service to be tested are expected to located in a .rules
 * file on the file system created using the JDeveloper 11g Rules Editor.
 * The rules should be contained in a JDeveloper SOA Rules Composite Application,
 * for example a "Composite Business Rule" application.
 * Rules are tested in a slave JVM automatically started by JDeveloper.
 * A running WebLogic Application Server is not required.
 */
public class RuleTester {
 public RuleTester() {
 super();
 }
 
 /* Initialize loads the Rule dictionary from the specified file system location and creates a Rule Decision Point.
 * @param dictionaryLocation The full path to the .rules file.
 * @param decisionFunctionName The name of the Decision Function that will be called.
 * The function is defined using the JDevleloper rule editor 
 */
 private static DecisionPointInstance initialize(String dictionaryLocation, String decisionFunctionName) throws Exception {
 
 DecisionPointInstance pointInstance = null;
 if(dictionaryLocation == null || decisionFunctionName == null)
 throw new Exception("RuleProcessor must have all input properties to successfully initialize.");
 
 // Load Decision Point using Dictionary on File System
 DecisionPoint decisionPoint = new DecisionPointBuilder()
 .with(decisionFunctionName)
 .with(loadRuleDictionary(dictionaryLocation)) 
 .build();
 pointInstance = decisionPoint.getInstance(); 
 RuleSession session = pointInstance.ruleSession();
 session.callFunctionWithArgument("setDecisionTraceLevel", RuleSession.DECISION_TRACE_DEVELOPMENT);
 
 System.out.println("RuleTester Initialized");
 return pointInstance;
 }
 
 
 
 /**
 * Loads the rule dictionary from the specified dictionaryPath
 * @param dictionaryLocation The full path to the .rules file.
 * @return A rule dictionary object
 * 
 */
 private static RuleDictionary loadRuleDictionary(String dictionaryLocation) throws Exception{
 RuleDictionary dict = null;
 Reader reader = null;
 Writer writer = null;
 
 try {
 reader = new FileReader(new File(dictionaryLocation));
 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);
 }
 
 } finally {
 if (reader != null) { try { reader.close(); } 
 catch (IOException ioe) {ioe.printStackTrace();}}
 if (writer != null) { try { writer.close(); } 
 catch (IOException ioe) {ioe.printStackTrace();}}
 }
 
 return dict;
 }
 
 /**
 * Produces a trace of the Rules Session to the Standard Output device
 * @param pointInstance The decision point instance for the test session.
 
 */
 private static void outputTrace(DecisionPointInstance pointInstance) throws Exception
 {
 DecisionTrace trace = pointInstance.decisionTrace();
 TraceAnalysis tAnalysis = new TraceAnalysis(trace);
 
 List<TraceEntry> traceList = trace.getTraceEntries();
 System.out.println("Trace list size is " + traceList.size() );
 
 if(traceList.size() > 0) {
 // Iterator<TraceEntry> it = traceList.iterator();
 for(int t=0; t<traceList.size(); t++)
 {
 TraceEntry entry = traceList.get(t);
 
 RuleEngineState engineState = tAnalysis.getRuleEngineState(entry);
 
 // Fact Trace List for Trace Entry
 List<FactTrace> factTraceList = engineState.getFacts();
 System.out.println("Trace entry " + (t+1) + " Engine has " + factTraceList.size() + " Facts");
 for(int i=0; i< factTraceList.size(); i++)
 { 
 FactTrace fTrace = (FactTrace) factTraceList.get(i);
 System.out.println("Fact " + (i+1) + ": " + fTrace.getFactType());
 }
 
 // Output Fired Rules for Trace Entry
 List<RuleTrace> firedRulesList = engineState.getFiredRules();
 for(int i=0; i< firedRulesList.size(); i++)
 { 
 RuleTrace frTrace = (RuleTrace) firedRulesList.get(i);
 System.out.println("Fired rule " + frTrace.getRuleName());
 }
 }
 }
 }
 
 /**
 * Execute the Rule Decision Function and return the results.
 * @param dictionaryLocation The fully qualified path of the .rules file containing the rules.
 * Created by JDeveloper rule editor.
 * @param decisionFunctionName The name of the Decision Function in the Rules Dictionary that will be called.
 * @param inputs An ArrayList containing the parameters required by the decision service in order or appearance.
 * @param trace A boolean flag indicating whether a trace for the session should be output to the standard out device.
 * @return A modified list of objects returned by the specified rule decision service
 */
 public static List runRules(String dicrtionaryLocation, String decisionFunctionName, ArrayList inputs, boolean trace) throws Exception { 
 
 List<Object> ruleResult = null; 

 DecisionPointInstance pInstance = initialize(dicrtionaryLocation, decisionFunctionName);
 
 if(pInstance == null)
 throw new Exception("RuleTester not intialized.");
 
 System.out.println("Running Rules");
 
 if (inputs != null)
 { 
 pInstance.setInputs(inputs);
 
 // invoke the decision point with our inputs
 ruleResult = pInstance.invoke();
 
 if (ruleResult == null || ruleResult.isEmpty()){
 System.out.println("RuleTester: No results returned by rules"); 
 }
 else System.out.println("RuleTester: " + ruleResult.size() + " result(s) returned.");
 
 if(trace)
 outputTrace(pInstance);
 }
 return ruleResult;
 }
 
}

Download RuleTester.java

MyShippingCostTester.java

package rulesproject;

import example.rules.OrderT;
import example.rules.ObjectFactory;
import java.io.ByteArrayInputStream;
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;

public class MyShippingCostTester {
 public MyShippingCostTester() {
 super();
 }
 
 /**
 * Creates a list of Shipping Codes Java Facts for input to the rule engine. 
 * @return A populated List of ShipCode objects
 */
 public static List<ShipCode> createShipCodesTestData()
 { 
 ShipCode row1 = new ShipCode();
 row1.setProductId("001");
 row1.setShipCode("C");
 
 ShipCode row2 = new ShipCode();
 row2.setProductId("002");
 row2.setShipCode("A");
 
 List<ShipCode> codes = new ArrayList<ShipCode>();
 codes.add(row1);
 codes.add(row2);
 
 return codes;
 }
 
 /**
 * Creates a sample order for input to the rule engine. Order is an XML type Fact.
 * @return A populated sample OrderT object
 * @throws JAXBException If unmarshalling is unsuccessful
 */
 public static OrderT createOrderTestData() throws JAXBException {
 
 String sampleOrder =
 "<Order xmlns=\"http://www.rules.example\">\n" + 
 " <orderId></orderId>\n" + 
 " <items>\n" + 
 " <item>\n" + 
 " <productId>001</productId>\n" + 
 " <quantity>3</quantity>\n" + 
 " <unitPrice>5.99</unitPrice>\n" + 
 " <extraCharge>false</extraCharge>\n" + 
 " </item>\n" + 
 " <item>\n" + 
 " <productId>123</productId>\n" + 
 " <quantity>1</quantity>\n" + 
 " <unitPrice>34.99</unitPrice>\n" + 
 " <extraCharge>false</extraCharge>\n" + 
 " </item>\n" + 
 " </items>\n" + 
 "</Order>";
 
 OrderT order = null;
 
 // XML Facts are represented by JAXB types. 
 // Use the Generated types to parse an xml test message
 JAXBContext jaxbContext2 = JAXBContext.newInstance("example.rules");
 Unmarshaller unMarsh = jaxbContext2.createUnmarshaller();
 ByteArrayInputStream is = new ByteArrayInputStream(sampleOrder.getBytes());
 Object obj = unMarsh.unmarshal(is);
 JAXBElement jobj = (JAXBElement) obj;
 order = (OrderT) jobj.getValue();
 
 // Write input Order to stdout for later comparison to Order returned by rule engine 
 System.out.println("Input Order Is:");
 Marshaller marshaller2 = jaxbContext2.createMarshaller();
 marshaller2.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
 marshaller2.marshal( obj, System.out );
 
 return order; 
 } 
 
 public static void main(String args[]) throws Exception {

 final String dictionaryLocation = "C:\\projects\\BobsRulesApp\\oracle\\rules\\rulesproject\\OracleRules1.rules";
 final String decisionServiceName = "DetermineShippingCosts";
 
 // Create an array of inputs that match the Rules Decision Function parameters
 ArrayList inputs = new ArrayList();
 inputs.add(createOrderTestData()); // OrderT
 inputs.add(createShipCodesTestData()); // Ship Code List
 
 // Execute the Rules passing in the Test Order and Shipping Codes
 List resultList = RuleTester.runRules(dictionaryLocation, decisionServiceName, inputs, true); // trace on
 
 // Output the results
 if(resultList != null)
 {
 // Only a single Order object returned from rules decision function
 OrderT result = (OrderT) resultList.get(0); 
 
 // Output Modified Order returned from Rules Engine to stdout 
 JAXBContext jaxbContext = JAXBContext.newInstance(OrderT.class);
 Marshaller marshaller = jaxbContext.createMarshaller();
 marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
 ObjectFactory objF = new ObjectFactory();
 marshaller.marshal( objF.createOrder(result), System.out );
 }
 }
 
 

}


Download MyShippingCostTester.java


Running a test
The rules are tested within JDeveloper by right clicking on the MyShippingCostTester.java file and choosing Run.

JDeveloper launches a JVM using the application class path which includes the required rule engine jars.

When run within JDeveloper, the test output is displayed in the Log window as shown below.

In the modified order that is returned, rules have updated the extraCharge element of the first item to indicate additional shipping charges will apply for this item.


A Note about Java and XML Rule Types

If the rules to be tested are based on Java facts then the Java Classes that were provided to the rule engine during fact creation will be on the classpath at runtime.
If the rules to be tested are based on XML, then JDeveloper has generated JAXB content model classes for the input types sent to the rules engine.
The java types for XML facts are generated by JDeveloper during rule creation in the rule editor.
The JAXB types and stored in the  .rulesdesigner folder under the project, which is not visible from within jdeveloper.
These are the types that must be used to create input test data for xml facts.

If XML Facts are being tested from within JDeveloper,
The .rulesdesign/Jaxb_classes folder must be added to project classpath so a run of MyShippingCostTester.java and RuleTester.java can find the types at run time.

Download the complete JDeveloper Project

This provided zip contains a JDeveloper 11.1.1.5 project.

Localizing the Project

The MyShippingCostTester.java file contains a fully qualified path to the .rules file.
The path must be updated in the source to point to the location of the .rules file on the local file system.
To run a test, right click on the MyShippingCostTester.java file and choose Run
The JDeveloper Log window should display the results shown above in the screen snapshot.

The complete example can be downloaded in a zip file from GitHub here

About

Picture of Bob

I am an Oracle Architect specializing in Service Oriented Architecture and Business Process Management.


Any code presented is for educational purposes only, no warranty or support is implied.

Search

Categories
Archives
« April 2014
SunMonTueWedThuFriSat
  
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
   
       
Today