[Documentation Index]

WSO2 BRS Developer Guide

This guide is to demonstrate a simple Insurance Application which uses business rules.

Contents

RuleEngineExecution

Overall Design

Intuitive Requirement: create facts, invoke the rule engine with the created facts, and then process the results from the rule engine execution

Expected Quality Attributes: Functionality, Performance, Modifiability, Reusability, Extensibility, and Conceptual Integrity

Rule Engine Execution

In order to achieve a better performance, the rule engine execution was divided into three main orthogonal functionalities. Each functionality can be viewed as a separated stage or layer.
Separating into stages or orthogonal functionalities is enabled to adopt architectural styles such as SEDA, and Pipe and Filters, which are widely acknowledged as architectural styles for achieving higher performance. For the Pipe, and Filters, a stage is a filter and for a SEDA, it is a SEDA stage.
Note: In the current implementation, we have not implemented any of above strategies for achieving a better performance.

Fact Adaptation

Facts have been named as inputs and the Input Manager is responsible for performing fact (input) adaptation. This stage is optional if the facts have already been formulated. However, if the facts have to be formulated based on the context in transit, the fact adaptation stage is required.

Fact Adaptation

ResourceDescription Encapsulates the meta-data about both facts/results. Furthermore, this can be used to capture any other configuration information. For example, the adapter configuration in the rule-component.conf

Resource Adapter

/**
 * Resources Adapter - This can be adapted input or output. It is needed to tell what can adapt
 * using OutputAdaptable, InputAdaptable , etc.
 */
public interface ResourceAdapter {
    
    /**
     * Type of  the resource that this adapter responsible for adapt. For an input, the type is the target type
     * for an output , it is a type of the source
     *
     * @return Resource type
     */
    String getType();
}
s

InputAdaptable To adapt a given object into a fact (input) type that the adapter is responsible.

/**
 * Adapts the given object based on the resource description. Note here that the given tobeAdapted is
 * data and only need to convert that into the correct target type.
 */
public interface InputAdaptable {

    /**
     * Converts provide object into the object type defined in the resource description
     *
     * @param resourceDescription Input ResourceDescription
     * @param tobeAdapted         The final calculated value ,
     *                            only need to convert that into correct type
     * @return Converted object representing expected type
     */
    Object adaptInput(ResourceDescription resourceDescription,
                      Object tobeAdapted);
}

There are a few implementations

POJOResourceAdapter Adapts a given object into a POJO

MessageFactAdapter Adapts a given object into a Message

DOMResorceAdapter Adapts a given object into a DOM node

OMElementResorceAdapter Adapts a given object into a OM Element

To get know who to registering new InputAdapters , please refer extending BRS guide

InputAdapterFactory - Create, Initiate and Manage Input Adapters

MessageInterceptor This is to access message being passed between components in the system that rule component is used. When a BRS is used as the mediator or the axis2 web service, facts have to be formulated based on the message context objects. MessageInterceptor enables to decouple the fact adaptation from the underlying system. There are two message MessageInterceptor implementations : SynapseMessageInterceptor and Axis2MessageInterceptor. The SynapseMessageInterceptor intercepts the synapse message context whereas the Axis2MessageInterceptor intercepts the axis2 message context.

InputAdaptationStrategy - Encapsulates an input Adaptation algorithm or logic. This formulates the inputs based on description, adapters, message interceptor, and context object.

Rule Engine Invocation

In order to invoke a rule engine, a session instance should be obtained. For that purpose, RuleEngine can be used. How to obtain a RuleEngine will be discussed in the Programming Model section.

Rule Engine Invoke

RuleEngine is to proxy the actual rule engine provider. It manages and controls the access to the rule engine provider. Actual rule engine provider is encapsulated with RuleBackendRuntime, which provide simplified API for accessing different rule engines, and allows plugging different rule engine. In that sense, it is analog to JSR 94 API. However, RuleBackendRuntime is simple and provide only functions required for the WSO2 BRS. It also provides a way to use native rule engines' API based on the requirement of WSO2 BRS. RuleBackendRuntime instance should be created only from RuleBackendRuntimeFactory. RuleEngine is responsible for loading correct RuleBackendRuntimeFactory and creating RuleBackendRuntime. Furthermore, it proxies all method calls to RuleBackendRuntime and do parameter and return value validation. Therefore, users must only access the rule engine provider through RuleEngine not from the RuleBackendRuntime.

RuleBackendRuntime

/**

* Encapsulates the rule service provider or the engine implements a rule engine. This adapts an

* existing rule engine implementation.

* <p/>

* This class exposes functionality required to create a rule execution set, a session associated

* with a rule execution set and remove a registered execution set. Furthermore, this provide a

* way to destroy underlying rule engine.

*/

public interface RuleBackendRuntime {

/**

* Registers a rule set. The rule set should be given in the RuleSetDescription as the rule source.

* Within this method , an executable rule set is created and registered with the rule engine

*

* @param description information about the rule set

* @return Registered URI of the rule set

*/

public String addRuleSet(RuleSetDescription description);

/**

* Create a session based on the given SessionDescription. The session can be stateful or stateless

*

* @param sessionDescription information about the session to be created

* @return a valid <code>Session</code> object, either stateful or stateless

*/

public Session createSession(SessionDescription sessionDescription);

/**

* Removed a already registered rule set

*

* @param description information about the rule set to be removed

*/

public void removeRuleSet(RuleSetDescription description);

/**

* Cleanup any resources used by the rule engine

*/

public void destroy();

}

RuleBackendRuntimeFactory

/**
 * A factory for creating a RuleBackendRuntime. This should provide a properly initiated
 * RuleBackendRuntime instance. It is recommended to use this class as the only means for creating
 * RuleBackendRuntime instances
 */
public interface RuleBackendRuntimeFactory {
    /**
     * Returns a  properly initiated RuleBackendRuntime instance.
     *
     * @param properties  properties to be used when creating the underlying rule service provider
     * @param classLoader class loader to be used by the underlying rule service provider to load
     *                    facts and other required classes
     * @return properly initiated <code>RuleBackendRuntime</code>  instance if there are on exceptions.
     *         Otherwise, <code>LoggedRuntimeException/code> should be thrown.
     */
    public RuleBackendRuntime createRuleBackendRuntime(Map<String, PropertyDescription> properties,
                                                       ClassLoader classLoader);
}

Session is the runtime connection between the rule engine and its users.

**
 * Runtime connection between the rule engine and its users
 */
public interface Session {

    /**
     * Execute the rule engine by providing inputs as a List of Objects
     *
     * @param facts facts as a list of objects
     * @return a list of results from the rule engine execution
     */
    List execute(List<Object> facts);

    /**
     * Releases the session
     */
    void release();
}

Results Adaptation

Results adaptation process is almost similar to the fact adaptation process. The difference is that the results adaptation deals with the results from the rule engine invocation. Results are used to enrich the context object being passed between components of the system uses the rule component. This stage is also an optional.

Result Adaptation

Output Adaptable Interface

/**
 * Adapts a result based on output description. Before call for adapt ,
 * it is recommended to explicitly checks for whether result can be adapted or not
 */

public interface OutputAdaptable {

    /**
     * Adapts the result according to the corresponding output description. The result of adaptation
     * is put into the context based on the information in the output description.
     *
     * @param description        Output ResourceDescription
     * @param result             Result from the engine
     * @param context            the context to be used for looking up resources
     * @param messageInterceptor a helper class to locate resources from given context
     * @return True if Successfully adapt
     */
    boolean adaptOutput(ResourceDescription description,
                        Object result,
                        Object context,
                        MessageInterceptor messageInterceptor);

    /**
     * Explicitly checks for whether result can be adapted or not
     *
     * @param description Output ResourceDescription
     * @param output      Result from the engine
     * @return True if it is possible to successfully adapt
     */
    boolean canAdaptOutput(ResourceDescription description,
                           Object output);

    /**
     * Adapts the result according to the corresponding output description.
     *
     * @param description information about target object
     * @param result      the object to be adapted
     * @return Adapted Object
     */
    public Object adaptOutput(ResourceDescription description, Object result);
}

Programming Model

Rules component has exposed as an OSGI service. Therefore, the programming model to adopt rules component is coupled to the OSGI service model.

The OSGI Service

RuleServerManagerService is the OSGI service interface for exposing rules functionality.

/**
 * RuleServerManagerService is to provide the functionality to create main components belong to the
 * rule server, and information belong to it. There are three main components
 * <p/>
 * <ul>
 * <li> Rule Engine
 * <li> Input Manager
 * <li> OutPut Manager
 * </ul>
 * <p/>
 * Rule Engine encapsulates the underlying rule service provider. Input Manager is to adapt
 * given objects into target facts and OutPut Manager is to adapt results from the rule engine
 * into target objects
 */
public interface RuleServerManagerService {

    /**
     * Factory method to create a RuleEngine instance. This method returns a new RuleEngine instance.
     * The RuleEngine is created based on the RuleEngineProvider defined in the rule-component.conf
     *
     * @param ruleEngineClassLoader The class loader to be used by the RuleEngine instance
     * @return a valid <code>RuleEngine<code> instance
     */
    RuleEngine createRuleEngine(ClassLoader ruleEngineClassLoader);

    /**
     * Create an input manager instance based on the given input descriptions.
     *
     * @param inputs             A list of ResourceDescriptions presenting information abouts facts
     * @param messageInterceptor MessageInterceptor to be used for accessing the data in the message
     *                           being transit
     * @return a valid <code>InputManager </code>  instance
     */
    InputManager createInputManager(List<ResourceDescription> inputs,
                                    MessageInterceptor messageInterceptor);

    /**
     * Create an output manager instance based on the given input descriptions.
     *
     * @param outputs            A list of ResourceDescriptions presenting information abouts results
     * @param messageInterceptor MessageInterceptor to be used for accessing the data in
     *                           the message being transit
     * @return a valid <code>OutputManager </code>  instance
     */
    OutputManager createOutputManager(List<ResourceDescription> outputs,
                                      MessageInterceptor messageInterceptor);

    /**
     * Returns the InputAdapterFactory in the rule server. This is useful when it
     * is needed to access available input adapters and to add a new input adapter
     *
     * @return a valid <code>InputAdapterFactory</code> instance
     */
    public InputAdapterFactory getFactAdapterFactory();

    /**
     * Returns the OutputAdapterFactory in the rule server. This is useful when it
     * is needed to access available result adapters and to add a new result adapter
     *
     * @return a valid <code>OutputAdapterFactory</code> instance
     */
    public OutputAdapterFactory getResultAdapterFactory();
}

Integration Process

Step 1 : Add an OSGI service reference and required maven dependencies

        * @scr.component name="ruleservices.component" immediate="true"
        * @scr.reference name="ruleservermanager.component"
        * interface="org.wso2.carbon.rule.server.RuleServerManagerService" cardinality="1..1"
        * policy="dynamic" bind="setRuleServerManagerService" unbind="unSetRuleServerManagerService"

    <dependencies>
        <dependency>
            <groupId>org.wso2.carbon</groupId>
            <artifactId>org.wso2.carbon.rule.core</artifactId>
            <version>3.0.0</version>
        </dependency>
        <dependency>
            <groupId>org.wso2.carbon</groupId>
            <artifactId>org.wso2.carbon.rule.server</artifactId>
            <version>3.0.0</version>
        </dependency>
        <dependency>
            <groupId>org.wso2.carbon</groupId>
            <artifactId>org.wso2.carbon.rule.engine.drools</artifactId>
            <version>3.0.0</version>
        </dependency>
        <dependency>
            <groupId>org.wso2.carbon</groupId>
            <artifactId>org.wso2.carbon.rule.engine.jsr94</artifactId>
            <version>3.0.0</version>
        </dependency>
    </dependencies>
    

Step 2 : Implementing MessageInterceptor

This step is optional and required if the facts should be formulated from a message.

MessageInterceptor Interface

/**
 * Provides an abstraction intercepting the context object being passed between components of
 * the system the rule component is used
 * <p/>
 * Provides required functionality to extractPayload data from the context and enrich the
 * context with some data.
 * <p/>
 * This enables to decouple the context message from the implementation of the rule component
 */
public interface MessageInterceptor {

    /**
     * Looking up for a resource with the given key. The look up processes is coupled to
     * the implementation
     *
     * @param key          an identifier to locate the resource
     * @param message      the context object being passed between components of
     *                     the system the rule component is used
     * @param defaultValue default value in the case of there is no resource for the given key
     * @return resorce corresponded to the given key if there is a resource for the give key.
     *         Otherwise, the default value should be returned
     */
    public ReturnValue extract(String key, Object message, Object defaultValue);

    /**
     * Looking up for a resource with the given XPath expression. The look up processes is coupled
     * to the implementation
     *
     * @param xPath        an expression to locate the resource
     * @param message      the context object being passed between components of
     *                     the system the rule component is used
     * @param defaultValue default value in the case of there is no resource for the given expression
     * @return resorce corresponded to the given key if there is a resource for the give expression.
     *         Otherwise, the default value should be returned
     */
    public ReturnValue extract(XPath xPath, Object message, Object defaultValue);

    /**
     * Looking up for the default resource. The value of the default resource is coupled to
     * the implementation
     *
     * @param message the context object being passed between components of
     *                the system the rule component is used
     * @return The default resource
     */
    public ReturnValue extractPayload(Object message);

    /**
     * Extracts the envelope of the message
     *
     * @param message the context object being passed between components of
     *                the system the rule component is used
     * @return the envelope of the message
     */
    public ReturnValue extractEnvelope(Object message);

    /**
     * Enrich the context message with the given object based on the given key.
     * The enrichment process is coupled to the implementation
     *
     * @param key     an identifier for the resource
     * @param message the context object being passed between components of
     *                the system the rule component is used
     * @param value   data to be used for enriching context message
     */
    public void enrich(String key, Object message, Object value);
}      
    

Step 3 : Using Rules Component

// Creating Rule Engine Instance 
RuleEngine  ruleEngine = ruleServerManager.createRuleEngine(classLoader);

// registering a rule set 
String bindURI = ruleEngine.addRuleSet(ruleSetDescription);

// creating a Session to be used to access rule engine runtime
SessionDescription sessionDescription = // get or create 
sessionDescription.setRuleSetURI(bindURI);
Session session = ruleEngine.createSession(sessionDescription);

// to adapt facts from a message. This is optional
InputManager  inputManager = ruleServerManager.createInputManager(getFactDescriptions(), interceptor); 
List<Object> facts = inputManager.processInputs(msgCtx);

// to execute the rules 
List results = session.execute(facts);

// to process results. This is optional
OutputManager outputManager = ruleServerManager.createOutputManager(getResultsDescriptions(),interceptor);
outputManager.processOutputs(results, msgCtx);     
    

Note: The ruleServerManager is the RuleServerManagerService OSGI service. The ruleServerManager, outputManager and inputManager should be created once (Otherwise can be a minor performance issue).