jboss drools
TRANSCRIPT
JBoss Drools
by Victor Polischuk
Business ProblemModel and Requirements
ParticipantPartyid
PartyDefpartyId
startTime
stopTime
publicCode
Roleid
publicCode
PartyRolepartyId
roleId
startTime
stopTime
OrderProductDefproductId
startTime
stopTime
publicCode
OrderItemorderId
quantity
productId
product
Orderid
time
inParty
inRole
outParty
outRole
inPartyId
inRoleId
outPartyId
outRoleId
Productid
Start-stop intervals must not overlap.
Entity exists only within start-stop interval.
Order must have valid and existent in party.
Order must have valid and existent out party.
Order must have valid and existent in role.
Order must have valid and existent out role.
Order items must have valid and existent product.
Order in and out parties must not be the same.
Order roles must be: (buyer, seller), (buyer, repairer), (repairer, seller), (seller, buyer).
Frontal AssaultConditions
public void placeOrder(Order order) {}
public void placeOrder(Order order) {PartyDef ip = dao.findPartyBy(...);}
public void placeOrder(Order order) {PartyDef ip = dao.findPartyBy(...);if (ip == null) {throw new Rule3Exception(...);}}
public void placeOrder(Order order) {PartyDef ip = dao.findPartyBy(...);if (ip == null) {throw new Rule3Exception(...);}PartyDef op = dao.findPartyBy(...);if (op == null) {throw new Rule4Exception(...);}}
public void placeOrder(Order order) {PartyDef ip = dao.findPartyBy(...);if (ip == null) {throw new Rule3Exception(...);}PartyDef op = dao.findPartyBy(...);if (op == null) {throw new Rule4Exception(...);}PartyRole ir = dao.findRoleBy(...);if (ir == null) {throw new Rule5Exception(...);}}
public void placeOrder(Order order) {PartyDef ip = dao.findPartyBy(...);
assertRule3(ip);
PartyDef op = dao.findPartyBy(...);
assertRule4(op);
PartyRole ir = dao.findRoleBy(...);
assertRule5(ir);}
public void placeOrder(Order order) {PartyDef ip = getValidatedByRule3(...);PartyDef op = getValidatedByRule4(...);PartyRole ir = getValidatedByRule5(...);}
public void placeOrder(Order order) {List problems = ;
PartyDef ip = getValidatedByRule3(...);PartyDef op = getValidatedByRule4(...);PartyRole ir = getValidatedByRule5(...); //???}
public void placeOrder(Order order) {List problems = ;
PartyDef ip = getValidatedByRule3(...);PartyDef op = getValidatedByRule4(...);if (ip != null) {PartyRole ir = getValidatedByRule5(...);}}
public void placeOrder(Order order) {List problems = ;
PartyDef ip = getValidatedByRule3(...);PartyDef op = getValidatedByRule4(...);if (ip != null) {PartyRole ir = getValidatedByRule5(...);}if (op != null) {PartyRole or = getValidatedByRule6(...);}}
public void placeOrder(Order order) {List problems = ;
PartyDef ip = getValidatedByRule3(...);PartyDef op = getValidatedByRule4(...);if (ip != null) {PartyRole ir = getValidatedByRule5(...);}if (op != null) {PartyRole or = getValidatedByRule6(...);}if (ip != null && op != null && ip == op) {problems.add(...); // Rule #8 violation}}
public void placeOrder(Order order) {List problems = ;
PartyDef ip = getValidatedByRule3(...);PartyDef op = getValidatedByRule4(...);PartyRole ir = getSafeValidatedByRule5(...);PartyRole or = getSafeValidatedByRule6(...);validateRule8(...);}
public void placeOrder(Order order) {List problems = ;
PartyDef ip = getValidatedByRule3(...);PartyDef op = getValidatedByRule4(...);PartyRole ir = getSafeValidatedByRule5(...);PartyRole or = getSafeValidatedByRule6(...);validateRule8(...);if (ir != null && or != null && (...)) {problems.add(...); // Rule #9 violation}}
public void placeOrder(Order order) {List problems = ;
PartyDef ip = getValidatedByRule3(...);PartyDef op = getValidatedByRule4(...);PartyRole ir = getSafeValidatedByRule5(...);PartyRole or = getSafeValidatedByRule6(...);validateRule8(...);validateRule9(...);if (problems.isEmpty()) {order.inPartyId = ip.partyId;...} }
public void placeOrder(Order order) {List problems = ;
PartyDef ip = getValidatedByRule3(...);PartyDef op = getValidatedByRule4(...);PartyRole ir = getSafeValidatedByRule5(...);PartyRole or = getSafeValidatedByRule6(...);validateRule8(...);validateRule9(...);fillOrderIdentifications(...);// TODO: Rule #7 }
Clean the MessIndependent Rules
ContextRuleRunnerRuleConfigDataRule
class RuleRunner...public List run( data) {List problems =
;Context context = ;RuleConfig config = ;while
(config.hasNext(context)) {Rule rule = config.next(context);try
{rule.execute(context, data);} catch () {problems.add(...);}
}}
class RuleConfig...private final List rules = ;public Rule next(Context context) {Rule rule = null;int i = context.getLatestRuleIndex();while (i < rules.size() && rule == null) {If (rules.get(i).accepts(context)) {rule = rules.get(i);}i++;}context.setLatestRuleIndex(i);return rules.get(i);}
interface Rule {
boolean accepts(Context context);
void execute(Context context, T data);
}
class RuleN3 implements Rule
public boolean accepts(Context ctx) {return true;}public void execute(Context ctx,Order data) {PartyDef ip = dao.findPartyBy(...);if (ip == null) {throw new Rule3Exception(...);}ctx.inPartyDef = ip;data.inPartyId = ip.partyId;}
class RuleN8 implements Rule
public boolean accepts(Context ctx) {return ctx.inPartyDef != null &&ctx.outPartyDef != null;}public void execute(Context ctx,Order data) {PartyDef ip = dao.findPartyBy(...);if (ctx.inPartyDef == ctx.outPartyDef) {throw new Rule8Exception(...);}}
One More StepExpert System
ContextRuleRunnerRuleConfigDataRule
WorkingMemoryRuleEngineKnowledgeBaseFactsRule
Simple Rule EngineRuleRuleRuleRule14563Decision Block
Decision BlockDecision BlockDecision Block
Result
Expert SystemRuleRuleRuleRule14563Decision Block
Decision BlockDecision BlockDecision Block
Result
Error?Fix RoutingProcess OrderEnd Processing
Fixed?
Rule 1Rule 2Rule 3Rule 4Rule N...
Fact 1Fact 2Fact 3Fact 4Fact M...
*
* Repeats = ?
Rule 1Rule 2Rule 3Rule 4Rule N...
Fact 1Fact 2Fact 3Fact 4Fact M...
*
* Repeats = ?
Inefficient
To the RescueRete
Rete = Net
Charles L. Forgy
1979
1974
1982
Wikipedia.org
Root Node
Start
All Facts Pass Through It
1-input Nodes
Alpha Network
IntraElement Conditions
2-input Nodes
Beta Network
Two Element Comparison
Terminal Nodes
Stop
Rules Have a Full Match
DroolsFinally
rule "Rule example" // Rule unique name... // Rule attributes: grouping, priority, etc.when... // Left hand side. Condition.then... // Right hand side. Consequence.end
Drools Rule Pattern
IF
THEN
rule "Rule #3 violation"when $o : Order($id:id, $t:time, $pty:inParty) not( Problem(orderId==$id, ruleId==3) ) eval( dao.findPartyBy($t, $pty)==null )then log.info("Rule #3 violated for the order: " + $id); insert(new Problem($id, 3));end
rule "Rule #3 fulfillment"when $o : Order(inPartyId==null, $id:id, $t:time, $pty:inParty) not( Problem(orderId==$id, ruleId==3) ) eval( dao.findPartyBy($t, $pty)!=null )then log.info("Rule #3 fulfilled for the order: " + $id); modify($o) { setInPartyId( dao.findPartyBy($t, $pty).getPartyId() ) };end
class RuleN3 implements Rule
public boolean accepts(Context ctx) {return true;}public void execute(Context ctx,Order data) {PartyDef ip = dao.findPartyBy(...);if (ip == null) {throw new Rule3Exception(...);}ctx.inPartyDef = ip;data.inPartyId = ip.partyId;}
If Java Is SimplerWhy Use Drools?
rule "Rule #7 violation"when $o : Order(items!=null, $id:id, $t:time) $item : OrderItem($prd:product) from $o.items not( OIProblem(orderId==$id, ruleId==7, product==$prd) ) eval(dao.findProductBy($t, $prd)==null)then log.info("Rule #7 violated for order: " + $id + " and product: " + $prd); insert(new OIProblem($id, 7, $prd));end
class RuleN7 implements Rule
public boolean accepts(Context ctx) {return true;}public void execute(Context ctx,Order data) {for (OrderItem item : data.items) {ProductDef pd = dao.findProductBy(...);
if (pd == null) {throw new Rule7Exception(...); //???} }}
Java Solution #1Complex Rule
class RuleN7_1 implements Rule
public boolean accepts(Context ctx) {return true;}public void execute(Context ctx,Order data) {for (OrderItem item : data.items) {ProductDef pd = dao.findProductBy(...);
if (pd == null) {ctx.addRule7Error(...); //OK} }}
Java Solution #2Complex Runner and Configuration
class RuleN7_2 implements Rule
public boolean accepts(Context ctx) {return true;}public void execute(Context ctx,OrderItem data) {ProductDef pd = dao.findProductBy(...);
if (pd == null) {throw new Rule7Exception(...); //OK}}
DroolsAgain?
rule "Rule #3 violation"when $o : Order($id:id, $t:time, $pty:inParty) not( Problem(orderId==$id, ruleId==3) ) eval( dao.findPartyBy($t, $pty)==null )then log.info("Rule #3 violated for the order: " + $id); insert(new Problem($id, 3));end
rule "Rule #7 violation"when $o : Order(items!=null, $id:id, $t:time) $item : OrderItem($prd:product) from $o.items not( OIProblem(orderId==$id, ruleId==7, product==$prd) ) eval(dao.findProductBy($t, $prd)==null)then log.info("Rule #7 violated for order: " + $id + " and product: " + $prd); insert(new OIProblem($id, 7, $prd));end
Yet Another Validation ToolOr Not?
package com.victor.droolsdialect "mvel"import com.victor.drools.*; // optional
global GeneralDao dao;global org.slf4j.Logger log;
declare Problem orderId : long @key ruleId : int @keyenddeclare OIProblem extends Problem product : String @keyend
declare ExampleClass // Class name[extends ParentExampleClass] // Inheritance@stringMetadata("I am String") // Metadata@dateMetadata(01-Jan-1970)
propertyName1 : ValidType, // PropertiespropertyName2 : AnotherValidTypeend
Drools Class Declaration
rule "Rule #7 violation"when $o : Order(items!=null, $id:id, $t:time) $item : OrderItem($prd:product) from $o.items not( OIProblem(orderId==$id, ruleId==7, product==$prd) ) eval(dao.findProductBy($t, $prd)==null)then log.info("Rule #7 violated for order: " + $id + " and product: " + $prd); insert(new OIProblem($id, 7, $prd));end
function String hello(String name) { return "Hello "+name+"!";}
Drools Function Declaration
rule "Function usage in LHS"when eval( hello("Victor")!="Hello Victor!" )thenint faults = ++alexeyFaults.count; log.fatal("Alexey, fix hello(..) function"); log.anger("You'd better be off"); log.info("You've broken it " +faults+ " time(s)");end
rule "Function usage in RHS"when eval(true)then log.happiness(hello("Victor")); log.worship(hello("Master"));end
query "Orders with inParty" $o : Order(inParty != null)end
query "Orders with inRole equals to" (String role) $o : Order(inRole == role)end
Drools Query Declaration
template headerfield1... // Template fieldstemplate TemplateName rule "ExampleRule_@{row.rowNumber}"... // Usage of declared fieldsendend template
Drools Templates
Promotional discount rulesAge BracketNumber of prior claimsPolicy type applying forDiscount %
Rewards for safe drivers18,240COMPREHENSIVE1
18,240FIRE_THEFT2
25,301COMPREHENSIVE5
25,302COMPREHENSIVE1
25,300COMPREHENSIVE20
jboss.org
Pice de RsistanceDSL
rule "Rule #3 violation"when $o : Order($id:id, $t:time, $pty:inParty) not( Problem(orderId==$id, ruleId==3) ) eval( dao.findPartyBy($t, $pty)==null )then log.info("Rule #3 violated for the order: " + $id); insert(new Problem($id, 3));end
rule "Rule #3 violation"whenEvery OrderWithout violations of rule 3Which violates inParty constraintsthenAdd the violation of the rule 3end
[when] Every Order = $o : Order($id:id, $t:time, $inPty:inParty)
[when] Without violations of rule {ruleNumber} = not(Problem(orderId==$id, ruleId=={ruleNumber}))[when] Which violates inParty constraints = eval( dao.findPartyBy($t, $inPty) == null )
[then] Add the violation of the rule {ruleNumber} = log.info("Rule #" + {ruleNumber} + " violated for order: " + $id); insert(new Problem($id, {ruleNumber}));
rule "Rule #4 violation"whenEvery OrderWithout violations of rule 4Which violates outParty constraintsthenAdd the violation of the rule 4end
[when] Every Order = $o : Order($id:id, $t:time, $inPty:inParty)
[when] Without violations of rule {ruleNumber} = not(Problem(orderId==$id, ruleId=={ruleNumber}))[when] Which violates inParty constraints = eval( dao.findPartyBy($t, $inPty) == null )
[then] Add the violation of the rule {ruleNumber} = log.info("Rule #" + {ruleNumber} + " violated for order: " + $id); insert(new Problem($id, {ruleNumber}));
[when] Every Order = $o : Order($id:id, $t:time, $inPty:inParty,$outPty:outParty)
[when] Without violations of rule {ruleNumber} = not(Problem(orderId==$id, ruleId=={ruleNumber}))[when] Which violates inParty constraints = eval( dao.findPartyBy($t, $inPty) == null )[when] Which violates outParty constraints = eval( dao.findPartyBy($t, $outPty) == null )[then] Add the violation of the rule {ruleNumber} = log.info("Rule #" + {ruleNumber} + " violated for order: " + $id); insert(new Problem($id, {ruleNumber}));
Seven?Let's have fun
rule "Rule #7 violation"when $o : Order(items!=null, $id:id, $t:time) $item : OrderItem($prd:product) from $o.items not( OIProblem(orderId==$id, ruleId==7, product==$prd) ) eval(dao.findProductBy($t, $prd)==null)then log.info("Rule #7 violated for order: " + $id + " and product: " + $prd); insert(new OIProblem($id, 7, $prd));end
rule "Rule #7 violation"when $o : Order(items!=null, $id:id, $t:time) $item : OrderItem($prd:product) from $o.items not( OIProblem(orderId==$id, ruleId==7, product==$prd) ) eval(dao.findProductBy($t, $prd)==null)then log.info("Rule #7 violated for order: " + $id + " and product: " + $prd); insert(new OIProblem($id, 7, $prd));end
rule "Rule #7 violation"when $o : Order(items!=null, $id:id, $t:time) $item : OrderItem($prd:product) from $o.items not( OIProblem(orderId==$id, ruleId==7, product==$prd) ) eval(dao.findProductBy($t, $prd)==null)then log.info("Rule #7 violated for order: " + $id + " and product: " + $prd); insert(new OIProblem($id, 7, $prd));end
rule "Rule #7 violation"when $o : Order(items!=null, $id:id, $t:time) $item : OrderItem($prd:product) from $o.items not( OIProblem(orderId==$id, ruleId==7, product==$prd) ) eval(dao.findProductBy($t, $prd)==null)then log.info("Rule #7 violated for order: " + $id + " and product: " + $prd); insert(new OIProblem($id, 7, $prd));end
rule "Rule #7 violation"when $o : Order(items!=null, $id:id, $t:time) $item : OrderItem($prd:product) from $o.items not( OIProblem(orderId==$id, ruleId==7, product==$prd) ) eval(dao.findProductBy($t, $prd)==null)then log.info("Rule #7 violated for order: " + $id + " and product: " + $prd); insert(new OIProblem($id, 7, $prd));end
rule "Rule #7 violation"when Every Order - which has items And every OrderItemWithout violations of order item rule 7 Which violates product constraintsthenAdd the violation of the rule 7 for the order itemend
[when] Every Order =
[when] Without violations of rule {N} =[when] Which violates inParty constraints =[when] Which violates outParty constraints =
[then] Add the violation of the rule {N} =
[when] Every Order = [when] - which has items = items != null
[when] And every OrderItem =[when] Without violations of rule {N} =[when] Which violates inParty constraints =[when] Which violates outParty constraints =[when] Without violations of order item rule {N} = [when] Which violates product constraints =
[then] Add the violation of the rule {N} for order item = [then] Add the violation of the rule {N} =
I Do BelieveAm I Alone?
Meanwhile in the Real World...Requirements Change
Start-stop intervals must not overlap.
Entity exists only within start-stop interval.
Order must have valid and existent in party.
Order must have valid and existent out party.
Order must have valid and existent in role.
Order must have valid and existent out role.
Order items must have valid and existent product.
Order in and out parties CAN be the same. ONLY IF they have roles (seller, repairer).
Order roles must be: (buyer, seller), (buyer, repairer), (repairer, seller), (seller, buyer). OR (seller, repairer) if they are the same.
rule "Rule #8 violation"when $o : Order(inPartyId != null, outPartyId != null,outPartyId == inPartyId,$id : id) not( Problem(orderId == $id, ruleId in (3, 4, 8)) )then log.info("Rule #8 violated for order: " + $id); insert(new Problem($id, 8));end
rule "Rule #8 violation"when $o : Order(!(inRole == "S" && outRole == "R"),inPartyId != null, outPartyId != null,outPartyId == inPartyId,$id : id) not( Problem(orderId == $id, ruleId in (3, 4, 8)) )then log.info("Rule #8 violated for order: " + $id); insert(new Problem($id, 8));end
rule "Rule #8 violation"when Every Order- with inPartyId- with outPartyId- where inPartyId equals outPartyId - and inRole not "S" and outRole not "R" Without violations of rules [3,4,8]thenAdd the violation of the rule 8end
rule "Rule #8 exceptional case"// salience 100when $o : Order(inRole == "S", outRole == "R",inPartyId != null, outPartyId != null,outPartyId == inPartyId,$id : id)then log.info("Rule #8 special case for order: " + $id);insertLogical(new SkipRule($id, 8));end
rule "Rule #8 violation"when $o : Order(inPartyId != null, outPartyId != null,outPartyId == inPartyId,$id : id)not( SkipRule(orderId == $id, ruleId == 8) ) not(Problem(orderId == $id, ruleId in (3, 4, 8)) )then log.info("Rule #8 violated for order: " + $id); insert(new Problem($id, 8));end
rule "Cleanup skipped rules"when$o : Order($id : id)SkipRule(orderId == $id, $rId : ruleId)$p : Problem(orderId == $id, ruleId == $rId)then log.info("Retract #" + $rId + " for order: " + $id);retract($p);end
Houston, we've got a problemPerformance & Resources
Rete
Entry Point
Object Type
Alpha
Adapters/Eval
Join
Not
Terminal
Rete
Entry PointEntry Point Node
Scope 1EntryPointNode#1
Scope 2EntryPointNode#2
Scope 3EntryPointNode#3
Entry Point Node
Object TypeObject Type Node
OrderObjectTypeNode#1
PartyDefObjectTypeNode#2
ProblemObjectTypeNode#3
OIProblemObjectTypeNode#4
Object Type Node (Order)
Fact Set
Order#112
Order#113
Order#114
Order#115
...
Object Type Node (Order)
LiteralAlpha Node
inParty == nullAlphaNode#1
inParty != nullAlphaNode#2
outParty == nullAlphaNode#3
outParty != nullAlphaNode#4
Alpha Node
Fact Set
Order#1
Order#5
Order#6
...
Join Node
Left
Order#1, SkipRule#5
Order#5, SkipRule#15
Order#6, SkipRule#11
...
Right
Problem#1
Problem#5
Problem#6
...
Problem#11
Problem#12
Not Node
Left
Order#1, OrderItem#5
Order#1, OrderItem#1
Order#1, OrderItem#3
...
Right
Problem#1
Problem#5
Problem#6
...
Problem#11
Problem#12
Eval
No Cache
No Memory
rule "Rule #3 violation"when $o : Order($id:id, $t:time, $pty:inParty) not( Problem(orderId==$id, ruleId==3) ) eval( dao.findPartyBy($t, $pty)==null )then log.info("Rule #3 violated for the order: " + $id); insert(new Problem($id, 3));end
rule "Rule #3 fulfillment"when $o : Order(inPartyId==null, $id:id, $t:time, $pty:inParty) not( Problem(orderId==$id, ruleId==3) ) eval( dao.findPartyBy($t, $pty)!=null )then log.info("Rule #3 fulfilled for the order: " + $id); modify($o) { setInPartyId( dao.findPartyBy($t, $pty).getPartyId() ) };end
Hard to TestInefficientHigh Cost of RefactoringImpossible Exception HandlingCan Change State Between InvocationsUsually Less Readable
Replace EVALPrefetch Data
rule "Rule #3 violation"when $o : Order($id:id, $t:time, $pty:inParty) not( Problem(orderId==$id, ruleId==3) ) not( PartyDef(publicCode == $pty,startTime $t) )then log.info("Rule #3 violated for the order: " + $id); insert(new Problem($id, 3));end
rule "Rule #3 fulfillment"when $o : Order(inPartyId==null, $id:id, $t:time, $pty:inParty) not( Problem(orderId==$id, ruleId==3) ) $p : PartyDef(publicCode == $pty,startTime $t) then log.info("Rule #3 fulfilled for the order: " + $id); modify($o) { inPartyId = $p.getPartyId(); };end
rule "Prefetch inParty PartyDef"salience 100whenOrder(inParty!=null, $t:time, $pCode:inParty)not( PartyDef(publicCode == $pCode,startTime $t) )thenPartyDef pd = dao.findPartyBy($t, $pCode);
if (pd != null) {log.info("insert party: " + pd.getPartyId());insert(pd);}end
rule "Prefetch inParty PartyDef"salience 100whenOrder(inParty!=null, $t:time, $pCode:inParty)not( PartyDef(publicCode == $pCode,startTime $t) )thenPartyDef pd = dao.findPartyBy($t, $pCode);log.info("insert party (if exists): " + pd);insert(pd); // null won't be insertedend
Hard to TestInefficientHigh Cost of RefactoringImpossible Exception HandlingCan Change State Between InvocationsUsually Less ReadableSeveral More Rules to Support
???
http://akinator.com/
http://docs.jboss.org/