Rules4JBI Quick Start Guide
This quick start guide will demonstrate how to expose the rules defined in JSR 94 TCK as a service inside Open ESB, as well as a SOAP Web Service for use by external clients. For a background on rules engines and JSR 94 in general, and the example presented in this quick start guide in particular, please see Getting Started With the Java Rule Engine API (JSR 94): Toward Rule-Based Applications
. Note that the article uses Jess
rules engine (the JSR 94 reference implementation), while we will use Drools
, an open source alternative.
- Select File | New Project from the top-level menu in NetBeans. New Project wizard opens. Choose category SOA and Rules Module project type. Click Next
- Specify project's name and location. Click Next
- Select JSR 94 configuration values from the pre-installed values or enter them manually. Click Finish
- New project gets created. We will now import the ruleset that we will later expose as a service inside Open ESB and as a SOAP Web Service for use by external clients. To invoke the import file wizard click on the Import File icon. This will import selected filetype into the main project. Alternativelly, you can right-click on the Rules Module project node and select Import File from the context menu
- Import File wizard opens. Select Ruleset File and click Next
- Save jsr94tck.drl
to a location on the disk and browse for it. Click Finish
- Imported file opens in the editor
- The imported ruleset contains one rule definition, which says (quoting directly from the JSR 94 TCK):
If the credit limit of the customer is greater than the amount of the invoice and the status of the invoice is unpaid, then decrement the credit limit with the amount of the invoice and set the status of the invoice to paid.
- Because multiple ruleset files can be stored (and versioned) within the project, we now need to specify which one will be used for this deployment. Right-click on the project node and select Properties
- Select the Rules Engine panel and click on the Browse button
- Select the imported file and click OK
- In this example, we will use a "global" rules engine installed per GlassFish domain, so we will leave Rules Engine Libraries empty. Click OK to close the Project Properties dialog
- As you can see from the imported ruleset file, it uses two Java classes, called business objects, Customer and Invoice. You are free to create those directly inside the NetBeans IDE; the Rules Module project has good integration with the Java editing capabilities. However, to demonstrate the two other options, and simulate arguably more real-world conditions, we will assume that we have an existing XML Schema describing the structure of the Customer class, and that we have the Invoice class created with the third-party tooling, packaged in a jar file. First, lets import the XML Schema file. Download customer.xsd
and either use the Import File wizard or just right-click on the XML Schema Files node and import it into the project
- The imported XML schema file opens in the editor. The next step is to compile it to generate Java source files. Right-click on the project node and select Compile XML Schemas
- You should see the information dialog box with the message "Successfully generated business objects source files!". Click OK
- Build the project to compile the newly generated Java source files.
- Next, we will import the invoice.jar
file. Download the file and import it under the Business Objects Libraries node
- Below is the source code of Invoice class:
package com.foobar;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
@XmlAccessorType(XmlAccessType.FIELD)
@XmlRootElement(name="bill", namespace="http://www.example.org/xml/ns/class")
@XmlType(name="billType", namespace="http://www.example.org/xml/ns/class")
public class Invoice {
@XmlAttribute
private int amount;
@XmlElement(required=true, namespace="http://www.example.org/xml/ns/class")
private String description;
@XmlElement(required=true, namespace="http://www.example.org/xml/ns/class")
private String status;
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public int getAmount() {
return amount;
}
public void setAmount(int amount) {
this.amount = amount;
}
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
@Override
public String toString() {
return "Invoice; description: " + description + ", amount: $" + amount + ", status: " + status;
}
}
Notice that JAXB annotations are used to customize the XML Schema that will describe the instance documents. JAXB is used internally to serialize/deserialize objects to/from XML. It is recommended that you use JAXB annotations yourself in order to have full control of how the instance documents will look like. In case you are not able or not allowed to modify the source code of the imported classes, they will be introduced via bytecode injection at runtime.
- We are now ready to create the WSDL document that will describe the deployed service unit. Right-click on the project node and select Create WSDL
- The WSDL wizard opens. Click Add to specify which input and output objects we are going to use
- Specify Customer and Invoice as input objects with cardinality one and Customer class as output object, also with cardinality one. Note that the output objects serve also as the filter to the JSR 94 rule session. Other objects returned by the rules engine will be discarded. Click Next
- Specify the configuration values that will identify the deployed service unit. We will stick with the defaults here. Click Next
- Finally, you can specify the name of the WSDL document. Again, we will stick with the default name. Click Finish
- The WSDL document describing the deployed service unit along with the accompanied jbi.xml file get created and open in the editor area
rules.wsdl:
<?xml version="1.0" encoding="UTF-8"?>
<wsdl:definitions name="Rules" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
targetNamespace="http://www.example.org/rules"
xmlns:tns="http://www.example.org/rules"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:types="http://www.milanfort.com/xml/ns/jbi/rules/types"
xmlns:jbi="http://java.sun.com/xml/ns/jbi/binding/service+engine">
<wsdl:types>
<xs:schema version="1.0" targetNamespace="http://www.example.org/xml/ns/class"
xmlns:tns="http://www.example.org/xml/ns/class"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="bill" type="tns:billType"/>
<xs:complexType name="billType">
<xs:sequence>
<xs:element form="qualified" name="description" type="xs:string"/>
<xs:element form="qualified" name="status" type="xs:string"/>
</xs:sequence>
<xs:attribute name="amount" type="xs:int" use="required"/>
</xs:complexType>
</xs:schema>
<xs:schema elementFormDefault="qualified" version="1.0"
targetNamespace="http://www.example.org/rules/types"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="customer">
<xs:complexType>
<xs:sequence>
<xs:element name="name" type="xs:string"/>
<xs:element name="creditLimit" type="xs:int"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
<xs:schema elementFormDefault="qualified" version="1.0"
targetNamespace="http://www.milanfort.com/xml/ns/jbi/rules/types"
xmlns:tns="http://www.milanfort.com/xml/ns/jbi/rules/types"
xmlns:ns1="http://www.example.org/rules/types"
xmlns:ns2="http://www.example.org/xml/ns/class"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:import namespace="http://www.example.org/xml/ns/class"/>
<xs:import namespace="http://www.example.org/rules/types"/>
<xs:element name="InputData">
<xs:complexType>
<xs:sequence>
<xs:element ref="ns1:customer"/>
<xs:element ref="ns2:bill"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="OutputData">
<xs:complexType>
<xs:sequence>
<xs:element ref="ns1:customer"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
</wsdl:types>
<wsdl:message name="OutputMessage">
<wsdl:part name="OutputPart" element="types:OutputData">
</wsdl:part>
</wsdl:message>
<wsdl:message name="InputMessage">
<wsdl:part name="InputPart" element="types:InputData">
</wsdl:part>
</wsdl:message>
<wsdl:portType name="RulesPortType">
<wsdl:operation name="execute">
<wsdl:input name="executeInput" message="tns:InputMessage">
</wsdl:input>
<wsdl:output name="executeOutput" message="tns:OutputMessage">
</wsdl:output>
</wsdl:operation>
</wsdl:portType>
<wsdl:binding name="RulesBinding" type="tns:RulesPortType">
<jbi:binding/>
</wsdl:binding>
<wsdl:service name="RulesService">
<wsdl:port name="RulesPort" binding="tns:RulesBinding">
</wsdl:port>
</wsdl:service>
<plnk:partnerLinkType xmlns:plnk="http://docs.oasis-open.org/wsbpel/2.0/plnktype" name="RulesServiceType">
<plnk:role name="RulesServiceProvider" portType="tns:RulesPortType" />
</plnk:partnerLinkType>
</wsdl:definitions>
jbi.xml:
<?xml version="1.0" encoding="UTF-8"?>
<jbi version="1.0" xmlns="http://java.sun.com/xml/ns/jbi" xmlns:ns1="http://www.example.org/rules">
<services binding-component="false">
<provides interface-name="ns1:RulesPortType" service-name="ns1:RulesServiceType" endpoint-name="RulesServiceProvider"/>
</services>
</jbi>
- Build the project to create the service unit. This is an important step. The Rules Module project is currently not an Ant-based project and therefore the Composite Application that we create in the next step will not be able to automatically build (or clean) the project. Hence, every time you make a change to the content of the service unit (e.g. you modify the rules), you have to build the project manually.
- Using the New Project wizard, create a new Composite Application
- Enter the name SampleCompositeApp and click Finish. New Composite Application project gets created
- Drag and drop the Rules Module project onto the CASA editor's JBI Modules area
- Drag and drop the SOAP WSDL Binding from the palette onto the WSDL Ports area of the CASA editor
- Build the Composite Application and connect the SOAP endpoint to the Rules Module endpoint. Build the Composite Application once again
- If you haven't done so already, copy the Drools jars into the lib/ext folder of your GlassFish domain and (re)start GlassFish
- Make sure you have installed fort-rules-engine and sun-http-binding
- Deploy the composite application
- Follow the prompts. Use the SampleCompositeApp WSDL and select the execute method.
- Fill in arbitrary input test values
- Right-click on the newly created test case and select Run
- Re-run the test case and observe the output