Osmanthus 规则+流程引擎

What is the Osmanthus?

Osmanthus is a framework for rules & flow engines, a lightweight library and based on MVEL library. It is more convenient than drools, developers define rules & flows with XML file, it supplies below useful XML node:

Rule node

  • rule: a single rule
<rule id="rulefirst" name="rulefirst" priority="0" exclusive="true" multipleTimes="5" valid="true">
    <condition><![CDATA[true]]></condition>
    <action><![CDATA[System.out.println("this is a rule,hah")]]></action>
</rule>
  • ruleset: set of rules, it extends the rule, so that means it has all attributes that rule has.
<ruleset name="set" id="set">
    <rule id="rulefirst" name="rulefirst" priority="0" exclusive="true" multipleTimes="5" valid="true">
        <condition><![CDATA[true]]></condition>
        <action><![CDATA[System.out.println("this is the first rule,hah")]]></action>
    </rule>
    <rule id="rulesecond" name="rulesecond" priority="0" exclusive="true" multipleTimes="5" valid="true">
        <condition><![CDATA[true]]></condition>
        <action><![CDATA[System.out.println("this is the second rule,hah")]]></action>
    </rule>
</ruleset>

Flow Control node

  • flow, whole flow control node, it can contain the rule,ruleset,split,paraller and merge node.
<?xml version="1.0" encoding="UTF-8"?>
<flow id="singleflow1">
    <start id="start" toNodeId="feerule"/>
    <ruleset id="feerule" fromNodeId="start" toNodeId="split1" external="true"/>
    <split id="split1" fromNodeId="feerule">
        <constraint toNodeId="card">
            <condition><![CDATA[fee>1]]></condition>
        </constraint>
        <constraint toNodeId="end">
            <condition><![CDATA[fee<=1]]></condition>
        </constraint>
    </split>
    <ruleset id="card" fromNodeId="split1" toNodeId="p1" external="true"/>
    <rule id="p1" fromNodeId="card" toNodeId="p2">
        <condition><![CDATA[1==1]]></condition>
        <action><![CDATA[rule1="p1"]]></action>
    </rule>
    <rule id="p2" fromNodeId="p1" toNodeId="p3">
        <condition><![CDATA[2==2]]></condition>
        <action><![CDATA[rule2="p2"]]></action>
    </rule>
    <rule id="p3" fromNodeId="p2" toNodeId="p4">
        <condition><![CDATA[1==1]]></condition>
        <action><![CDATA[rule3="p3"]]></action>
    </rule>
    <rule id="p4" fromNodeId="p3" toNodeId="p5">
        <condition><![CDATA[2==2]]></condition>
        <action><![CDATA[rule4="p4"]]></action>
    </rule>
    <rule id="p5" fromNodeId="p4" toNodeId="end">
        <condition><![CDATA[2==2]]></condition>
        <action><![CDATA[rule5="p5"]]></action>
    </rule>
    <end id="end"/>
</flow>
  • split, one flow control node, also extends rule node, has more than one “constraint”, but only one “constraint“‘s condition is true.
<split id="split1" >
    <constraint toNodeId="card">
        <condition><![CDATA[fee>1]]></condition>
    </constraint>
    <constraint toNodeId="end">
        <condition><![CDATA[fee<=1]]></condition>
    </constraint>
</split>
  • parallel, also a flow control node, it controls the execution of rules with parallel model. if your defined flow XML file contains this sort of node, only run this flow with MultipleThreadExecutor Java class. one “line” node that means the system will create a new thread to execute its next nodes.
    “sync” attribute means the main thread have to wait, until all its sub thread run over.
    “timeout”, unit is second. e.g, timeout=100, that means the mail thread will wait 100s, at this time, if there is any sub thread is running, will throw timeout exception.
<parallel id="multiple" sync="true" timeout="100">
    <line toNodeId="p1"/>
    <line toNodeId="p2"/>
</parallel>
  • merge, a merge node, that means this node can merge the execution results of multiple thread, the merge node don’t any thing, it equals “End” node.
<merge id="merge" fromNodeId="p3,p4" toNodeId="p5"/>

Features

  • Lightweight library and easy to learn API
  • Development with xml programming model
  • All conditions & Actions supports MVEL expression languages
  • Useful abstractions to define business rules and apply them easily with Java
  • The ability to create composite rules from primitive xml codes.
  • Parallel mode to execute multiple rules.
  • Support decision tree and score cards rules.

Hello world-Score Card Rules

Rules in XML file:

<?xml version="1.0" encoding="UTF-8"?>
<ruleset name="card" id="card">
    <rule id="gc1">
        <condition><![CDATA[fee<=1]]></condition>
        <action><![CDATA[weight=weight-100]]></action>
    </rule>
    <rule id="gc2">
        <condition><![CDATA[isBlackName]]></condition>
        <action><![CDATA[weight=weight-200]]></action>
    </rule>
    <rule id="gc3">
        <condition><![CDATA[name=='test']]></condition>
        <action><![CDATA[weight=weight-500]]></action>
    </rule>
    <rule id="gc4">
        <condition><![CDATA[reg ~= "[1-9][0-9]{4,}"]]></condition>
        <action><![CDATA[weight=weight-600]]></action>
    </rule>
</ruleset>

The rules of Decision Tree

In order to implments the decision tree data structure of rules, we have to use the “SPlIT” node to help us to create more than one branch, but here we call the banrach as “constraint”, that contatins one condition, if its condition is true, it will go to “toNodeId” node.

<?xml version="1.0" encoding="UTF-8"?>
<flow id="flow1">
    <start id="start" toNodeId="feerule"/>
    <ruleset id="feerule" fromNodeId="start" toNodeId="split1" external="true"/>
    <split id="split1" fromNodeId="feerule">
        <constraint toNodeId="card">
            <condition><![CDATA[fee>1]]></condition>
        </constraint>
        <constraint toNodeId="end">
            <condition><![CDATA[fee<=1]]></condition>
        </constraint>
    </split>
    <ruleset id="card" fromNodeId="split1" toNodeId="p5" external="true"/>
    <rule id="p5" fromNodeId="merge" toNodeId="end">
        <condition><![CDATA[2==2]]></condition>
        <action><![CDATA[rule5="p5"]]></action>
    </rule>
    <end id="end"/>
</flow>

Execute rules with Multiple Thread

If you want to run rules with multiple thead model, you have to use the “Parallel” node to help us to create more than one line, xml code as below:

<?xml version="1.0" encoding="UTF-8"?>
<flow id="multiflow">
    <start id="start" toRuleId="feerule"/>
    <ruleset id="feerule" fromRuleId="start" toRuleId="split1" external="true"/>
    <split id="split1" fromRuleId="feerule">
        <constraint toRuleId="card">
            <condition><![CDATA[fee>1]]></condition>
        </constraint>
        <constraint toRuleId="end">
            <condition><![CDATA[fee<=1]]></condition>
        </constraint>
    </split>
    <ruleset id="card" fromRuleId="split1" toRuleId="mlines" external="true"/>
    <parallel id="mlines" fromRuleId="card" sync="true" timeout="100" toRuleId="merge">
        <line toRuleId="p1"/>
        <line toRuleId="p2"/>
    </parallel>
    <rule id="p1" fromRuleId="mlines" toRuleId="end">
        <condition><![CDATA[1==1]]></condition>
        <action><![CDATA[rule1="p1"]]></action>
    </rule>
    <rule id="p2" fromRuleId="mlines" toRuleId="mlines2">
        <condition><![CDATA[2==2]]></condition>
        <action><![CDATA[rule2="p2"]]></action>
    </rule>
    <parallel id="mlines2" fromRuleId="p2" sync="true" timeout="100" toRuleId="merge">
        <line toRuleId="p3"/>
        <line toRuleId="p4"/>
    </parallel>
    <rule id="p3" fromRuleId="mlines2" toRuleId="merge">
        <condition><![CDATA[1==1]]></condition>
        <action><![CDATA[rule3="p3"]]></action>
    </rule>
    <rule id="p4" fromRuleId="mlines2" toRuleId="merge">
        <condition><![CDATA[2==2]]></condition>
        <action><![CDATA[rule4="p4"]]></action>
    </rule>
    <merge id="merge" fromRuleId="p3,p4" lineCnt="2" toRuleId="p5"/>
    <rule id="p5" fromRuleId="merge" toRuleId="end">
        <condition><![CDATA[2==2]]></condition>
        <action><![CDATA[rule5="p5"]]></action>
    </rule>
    <end id="end"/>
</flow>

JAVA Unit Test Code

package com.github.wei86609.osmanthus;

import java.util.concurrent.Executors;

import junit.framework.TestCase;

import com.github.wei86609.osmanthus.event.DefaultEventListener;
import com.github.wei86609.osmanthus.event.Event;
import com.github.wei86609.osmanthus.event.ParallelEventListener;
import com.github.wei86609.osmanthus.rule.executor.EndNodeExecutor;
import com.github.wei86609.osmanthus.rule.executor.MergeNodeExecutor;
import com.github.wei86609.osmanthus.rule.executor.MvelRuleExecutor;
import com.github.wei86609.osmanthus.rule.executor.ParallelRuleExecutor;
import com.github.wei86609.osmanthus.rule.executor.SplitRuleExecutor;
import com.github.wei86609.osmanthus.rule.executor.StartNodeExecutor;
import com.github.wei86609.osmanthus.rule.executor.ruleset.GeneralRuleSetExecutor;
import com.github.wei86609.osmanthus.rule.handler.GeneralRuleSetHandler;
import com.github.wei86609.osmanthus.rule.intercepter.DefaultIntercepter;

public class FlowEngineTest extends TestCase {

   protected FlowEngine buildMultiThreadFlowEngine() {
        final FlowEngine engine = new FlowEngine();
        //RuleExceutors
        ParallelRuleExecutor parallelRuleExecutor=new ParallelRuleExecutor();
        MvelRuleExecutor ruleExecutor = new MvelRuleExecutor();
        engine.addRuleExecutor(new SplitRuleExecutor());
        engine.addRuleExecutor(new StartNodeExecutor());
        engine.addRuleExecutor(new MergeNodeExecutor());
        engine.addRuleExecutor(new EndNodeExecutor());
        engine.addRuleExecutor(parallelRuleExecutor);
        engine.addRuleExecutor(ruleExecutor);
        //RuleSetExcecutors
        GeneralRuleSetExecutor generalRuleSetExecutor = new GeneralRuleSetExecutor();
        generalRuleSetExecutor.setRuleExecutor(ruleExecutor);
        generalRuleSetExecutor.addRuleSetHandler(new GeneralRuleSetHandler());
        engine.addRuleExecutor(generalRuleSetExecutor);
        //add event listener & thread pool for executer of parallel rule
        parallelRuleExecutor.setParallelEventListener(new ParallelEventListener(){
            public void startNewEvent(Event event, String ruleId) throws Exception{
                event.setCurrentRuleId(ruleId);
                engine.execute(event);
            }
        });
        parallelRuleExecutor.setThreadPool(Executors.newCachedThreadPool());
        return engine;
    }
    protected FlowEngine buildSingleThreadFlowEngine() {
        FlowEngine engine = new FlowEngine();
        //add event listener and intercepter
        engine.addEventListener(new DefaultEventListener());
        engine.addRuleInterceptor(new DefaultIntercepter());
        // add RuleExceutors
        MvelRuleExecutor ruleExecutor = new MvelRuleExecutor();
        engine.addRuleExecutor(new SplitRuleExecutor());
        engine.addRuleExecutor(new StartNodeExecutor());
        engine.addRuleExecutor(new EndNodeExecutor());
        engine.addRuleExecutor(ruleExecutor);
        // add RuleSetExcecutors
        GeneralRuleSetExecutor generalRuleSetExecutor = new GeneralRuleSetExecutor();
        generalRuleSetExecutor.setRuleExecutor(ruleExecutor);
        generalRuleSetExecutor.addRuleSetHandler(new GeneralRuleSetHandler());
        engine.addRuleExecutor(generalRuleSetExecutor);
        return engine;
    }
    public void testSingleThreadExecute() {
        Event event=new Event();
        event.setFlowId("singleflow1");
        event.setEventId("eventid");
        event.addVar("salary", 5000);
        event.addVar("weight", 500);
        event.addVar("isBlackName", true);
        event.addVar("fee", 500);
        event.addVar("name", "test");
        event.addVar("reg", "12312");
        event.addVar("idNum", "350823198809122917");
        try {
            buildSingleThreadFlowEngine().execute(event);
            System.out.println(""+event);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    public void testMultiThreadExecute() {
        Event event=new Event();
        event.setFlowId("multiflow");
        event.setEventId("muleventid");
        event.addVar("salary", 5000);
        event.addVar("weight", 500);
        event.addVar("isBlackName", true);
        event.addVar("fee", 500);
        event.addVar("name", "test");
        event.addVar("reg", "12312");
        event.addVar("idNum", "350823198809122917");
        try {
            buildMultiThreadFlowEngine().execute(event);
            System.out.println(""+event);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

Example of “Guess Number”

The “Guess Number” is a classical example to help us to understand the rule engine, so here can also implement the example by Osmanthus.

“`xml

Code example

“`java

“`

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值