Friendly Drools

Friendly wrapper that provides useful utilities over Drools engine

License

License

MIT
GroupId

GroupId

com.github.chen0040
ArtifactId

ArtifactId

friendly-drools
Last Version

Last Version

1.0.1
Release Date

Release Date

Type

Type

jar
Description

Description

Friendly Drools
Friendly wrapper that provides useful utilities over Drools engine
Project URL

Project URL

https://github.com/chen0040/friendly-drools
Source Code Management

Source Code Management

https://github.com/chen0040/friendly-drools

Download friendly-drools

How to add to project

<!-- https://jarcasting.com/artifacts/com.github.chen0040/friendly-drools/ -->
<dependency>
    <groupId>com.github.chen0040</groupId>
    <artifactId>friendly-drools</artifactId>
    <version>1.0.1</version>
</dependency>
// https://jarcasting.com/artifacts/com.github.chen0040/friendly-drools/
implementation 'com.github.chen0040:friendly-drools:1.0.1'
// https://jarcasting.com/artifacts/com.github.chen0040/friendly-drools/
implementation ("com.github.chen0040:friendly-drools:1.0.1")
'com.github.chen0040:friendly-drools:jar:1.0.1'
<dependency org="com.github.chen0040" name="friendly-drools" rev="1.0.1">
  <artifact name="friendly-drools" type="jar" />
</dependency>
@Grapes(
@Grab(group='com.github.chen0040', module='friendly-drools', version='1.0.1')
)
libraryDependencies += "com.github.chen0040" % "friendly-drools" % "1.0.1"
[com.github.chen0040/friendly-drools "1.0.1"]

Dependencies

compile (8)

Group / Artifact Type Version
org.slf4j : slf4j-api jar 1.7.20
org.slf4j : slf4j-log4j12 jar 1.7.20
org.apache.httpcomponents : httpclient jar 4.5.2
com.alibaba : fastjson jar 1.2.24
org.kie : kie-api jar 7.0.0.Beta2
org.drools : drools-core jar 7.0.0.Beta2
org.drools : drools-compiler jar 7.0.0.Beta2
org.drools : drools-decisiontables jar 7.0.0.Beta2

provided (1)

Group / Artifact Type Version
org.projectlombok : lombok jar 1.16.6

test (10)

Group / Artifact Type Version
org.testng : testng jar 6.9.10
org.hamcrest : hamcrest-core jar 1.3
org.hamcrest : hamcrest-library jar 1.3
org.assertj : assertj-core jar 3.5.2
org.powermock : powermock-core jar 1.6.5
org.powermock : powermock-api-mockito jar 1.6.5
org.powermock : powermock-module-junit4 jar 1.6.5
org.powermock : powermock-module-testng jar 1.6.5
org.mockito : mockito-core jar 2.0.2-beta
org.mockito : mockito-all jar 2.0.2-beta

Project Modules

There are no modules declared in this project.

friendly-drools

Drools engine friendly wrapper

Features

  • User can load an external drools file into the rule engine to run
  • User can load an external text string as drool rules into the rule engine to run
  • User can load external domain specific language (DSL) files or text string
  • user can load external decision table specified in external excel files

Install

Add the following dependency to your POM file:

<dependency>
    <groupId>com.github.chen0040</groupId>
    <artifactId>friendly-drools</artifactId>
    <version>1.0.1</version>
</dependency>

Usage

The sample codes can be found in the src/test/java folder.

Simple example of loading a an external text string as drool rules to run

StatefulRuleEngine ruleRunner = new StatefulKieRuleEngine();

String ruleContent = FileUtils.readToEnd("tutorials/customer-functions.drl");

ruleRunner.addRules("tutorials/customer-functions.drl", ruleContent);
ruleRunner.buildKnowledgeSession();

Customer customer = new Customer(1L, "Jack", Customer.Category.SILVER);

ruleRunner.insert(customer);
ruleRunner.setGlobal("globalList", new ArrayList<>());

ruleRunner.fireAllRules();
List<String> globalList = ruleRunner.getGlobal("globalList", List.class);
globalList.forEach(c -> logger.info("customer: {}", c));

Where tutorials/customer-functions.drl is a simple drools file containing the following content:

package tutorials;

import function com.github.chen0040.drools.tutorials.CustomerUtils.formatCustomer;
import com.github.chen0040.drools.tutorials.Customer;
import java.util.List;

global List globalList;

rule "Prepare customers list"
    when
        $c: Customer()
    then
        globalList.add(formatCustomer($c));
end

Load Drools File from resources folder

The following codes loads the anomaly detection rules for customer orders from drools files in the src/resources/tutorials folder:

StatefulRuleEngine ruleRunner = new StatefulKieRuleEngine();

ruleRunner.addClassPathRuleFile("tutorials", "tutorials/classify-item-rules.drl");
ruleRunner.addClassPathRuleFile("tutorials", "tutorials/classify-customer-rules.drl");
ruleRunner.addClassPathRuleFile("tutorials", "tutorials/coupon-creation-rules.drl");
ruleRunner.addClassPathRuleFile("tutorials", "tutorials/coupon-execution-rules.drl");
ruleRunner.addClassPathRuleFile("tutorials", "tutorials/order-discount-rules.drl");

ruleRunner.buildKnowledgeSession();

Order o = ModelFactory.getOrderWithFiveHighRangeItems();

ruleRunner.insert(o.getCustomer());

for(int i=0; i < o.getOrderLines().size(); ++i) {
 ruleRunner.insert(o.getOrderLines().get(i));
 ruleRunner.insert(o.getOrderLines().get(i).getItem());
}

ruleRunner.insert(o);


int fired = ruleRunner.fireAllRules();


// We have 5 Items that are categorized -> 5 rules were fired
// We have 1 Customer that needs to be categorized -> 1 rule fired
// We have just one order with all HIGH RAnge items -> 1 rule fired
// One Coupon is created for the SILVER Customer -> 1 rule fired
// One Coupon is executed after its creation -> 1 rule fired
logger.info("rules fired: {}", fired);
assertThat(fired).isEqualTo(9);
assertThat(o.getCustomer().getCategory()).isEqualTo(Customer.Category.SILVER);
assertThat(o.getDiscount()).isNotNull();

for(int i=0; i < o.getOrderLines().size(); ++i) {
 assertThat(o.getOrderLines().get(i).getItem().getCategory()).isEqualTo(Item.Category.HIGH_RANGE);
}

List<Coupon> coupons = ruleRunner.getFacts(Coupon.class);

logger.info("orders: {}", ruleRunner.getFacts(Order.class).size());
logger.info("customers: {}", ruleRunner.getFacts(Customer.class).size());

assertThat(coupons.size()).isEqualTo(1);

Load Drools File from file disk

The following codes loads the anomaly detection rules for customer orders which are stored in the folder /tmp/tutorials:

StatefulRuleEngine ruleRunner = new StatefulKieRuleEngine();

ruleRunner.addClassPathRuleFile("tutorials/classify-item-rules.drl", "/tmp/tutorials/classify-item-rules.drl");
ruleRunner.addClassPathRuleFile("tutorials/classify-customer-rules.drl", "/tmp/tutorials/classify-customer-rules.drl");
ruleRunner.addClassPathRuleFile("tutorials/coupon-creation-rules.drl", "/tmp/tutorials/coupon-creation-rules.drl");
ruleRunner.addClassPathRuleFile("tutorials/coupon-execution-rules.drl", "/tmp/tutorials/coupon-execution-rules.drl");
ruleRunner.addClassPathRuleFile("tutorials/order-discount-rules.drl", "/tmp/tutorials/order-discount-rules.drl");

ruleRunner.buildKnowledgeSession();

Order o = ModelFactory.getOrderWithFiveHighRangeItems();

ruleRunner.insert(o.getCustomer());

for(int i=0; i < o.getOrderLines().size(); ++i) {
 ruleRunner.insert(o.getOrderLines().get(i));
 ruleRunner.insert(o.getOrderLines().get(i).getItem());
}

ruleRunner.insert(o);


int fired = ruleRunner.fireAllRules();


// We have 5 Items that are categorized -> 5 rules were fired
// We have 1 Customer that needs to be categorized -> 1 rule fired
// We have just one order with all HIGH RAnge items -> 1 rule fired
// One Coupon is created for the SILVER Customer -> 1 rule fired
// One Coupon is executed after its creation -> 1 rule fired
logger.info("rules fired: {}", fired);
assertThat(fired).isEqualTo(9);
assertThat(o.getCustomer().getCategory()).isEqualTo(Customer.Category.SILVER);
assertThat(o.getDiscount()).isNotNull();

for(int i=0; i < o.getOrderLines().size(); ++i) {
 assertThat(o.getOrderLines().get(i).getItem().getCategory()).isEqualTo(Item.Category.HIGH_RANGE);
}

List<Coupon> coupons = ruleRunner.getFacts(Coupon.class);

logger.info("orders: {}", ruleRunner.getFacts(Order.class).size());
logger.info("customers: {}", ruleRunner.getFacts(Customer.class).size());

assertThat(coupons.size()).isEqualTo(1);

Load Excel Decision Table

The following codes how to load rules from external excel decision table.

StatefulRuleEngine ruleRunner = new StatefulKieRuleEngine();

InputStream is = new FileInputStream("tutorials/classify-customer.xls");

String rules = ruleRunner.expandDecisionTable(is, DecisionTableInputType.XLS, null);
logger.info("rules: \n{}", rules);

ruleRunner.addDecisionTable("classify-customer.xls", is, DecisionTableInputType.XLS, null);

ruleRunner.buildKnowledgeSession();

for(int age = 16; age < 45; ++age){
 ruleRunner.insert(ModelFactory.getCustomerWithAge(age));
}

ruleRunner.fireAllRules();

Load Dsl (Domain-Specific-Language) File

The following codes show how to load domain specific language (DSL) files as well as drools files written using DSL.

StatefulRuleEngine ruleRunner = new StatefulKieRuleEngine();

String dslContent = FileUtils.readToEnd("tutorials/classify-customer.dsl");
String dslrContent = FileUtils.readToEnd("tutorials/classify-customer-rules.dslr");

String drlContent = RuleEngine.expandDSLRules(dslContent, dslrContent);
logger.info("expanded drl: \n{}", drlContent);

ruleRunner.addDsl("tutorials/classify-customer.dsl", dslContent);
ruleRunner.addDslRules("tutorials/classify-customer-rules.dslr", dslrContent);

ruleRunner.buildKnowledgeSession();

for(int age = 21; age <= 31; ++age) {
 ruleRunner.insert(ModelFactory.getCustomerWithAge(age));
}

ruleRunner.fireAllRules();

CEP

The following codes show how to use the rule engine for complex event processing by loading the cep rule files from external text string.

StatefulRuleEngine ruleRunner = new StatefulKieRuleEngine();

ruleRunner.addRules("tutorials/transaction-cep-rules.drl", FileUtils.readToEnd("tutorials/transaction-cep-rules.drl"));

ruleRunner.enableEventStreaming();
ruleRunner.buildKnowledgeSession();

long now = new Date().getTime();

for(int i=0; i < 11; ++i) {
 ruleRunner.insert(new TransactionEvent(1L, 100.0, new Date(now - 10000 + 1000 * i), 100L));
}

ruleRunner.insert(new TransactionEvent(1L, 1010.0, new Date(now), 100L));

ruleRunner.fireAllRules();

CEP with window declaration

The following codes show how to perform CEP with window declaration:

StatefulRuleEngine ruleRunner = new StatefulKieRuleEngine();
ruleRunner.addRules("tutorials/heartbeat-cep-rules.drl", FileUtils.readToEnd("tutorials/heartbeat-cep-rules.drl"));

ruleRunner.enableEventStreaming();
ruleRunner.buildKnowledgeSession();

long now = new Date().getTime();

for(int i=0; i < 11; ++i) {
 ruleRunner.getSession().getEntryPoint("heart beat monitor").insert(new HeartBeat(new Date(now - 10000 + 5 * i), 100L));
}

ruleRunner.getSession().getEntryPoint("heart beat monitor").insert(new HeartBeat(new Date(now), 100L));

ruleRunner.fireAllRules();

Load drools that interact with java delclared functions

The following codes shows how to work with drool file that access the java declared functions:

StatefulRuleEngine ruleRunner = new StatefulKieRuleEngine();
ruleRunner.addRules("tutorials/order-total-rules.drl", FileUtils.readToEnd("tutorials/order-total-rules.drl"));
ruleRunner.addRules("tutorials/order-functions.drl", FileUtils.readToEnd("tutorials/order-functions.drl"));

ruleRunner.buildKnowledgeSession();

Customer customer1 = new Customer();
customer1.setCustomerId(1L);

Order customer1Order = ModelFactory.getPendingOrderWithTotalValueGreaterThan10000(customer1);

Customer customer2 = new Customer();
customer2.setCustomerId(2L);

Order customer2Order = ModelFactory.getPendingOrderWithTotalValueLessThan10000(customer1);


OrderService orderService = new OrderServiceImpl();
orderService.save(customer1Order);
orderService.save(customer2Order);

ruleRunner.insert(customer1);
ruleRunner.insert(customer2);

ruleRunner.setGlobal("biggestOrder", new BiggestOrder());
ruleRunner.setGlobal("orderService", orderService);

ruleRunner.fireAllRules();

BiggestOrder biggestOrder = ruleRunner.getGlobal("biggestOrder", BiggestOrder.class);
logger.info("biggest order: {}", biggestOrder.getObject());

The content of tutorials/order-functions.drl is shown below:

package tutorials;

import accumulate com.github.chen0040.drools.tutorials.BiggestOrderFunction biggestOrder
import com.github.chen0040.drools.tutorials.BiggestOrder;

global BiggestOrder biggestOrder;

rule "Find Biggest Order"
when
    $bigO: Order() from accumulate(
        $o: Order(),
        biggestOrder($o)
    )
then
    biggestOrder.setObject($bigO);
end

Where BiggestOrderFunction is defined in the java code instead:

 import org.kie.api.runtime.rule.AccumulateFunction;
 
 import java.io.*;

 public class BiggestOrderFunction implements AccumulateFunction {
 
    public static class Context implements Externalizable {
       public Order maxOrder = null;
       public double maxTotal = - Double.MAX_VALUE;
 
       @Override public void writeExternal(ObjectOutput out) throws IOException {
 
       }
 
 
       @Override public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
 
       }
    }
 
    @Override public Serializable createContext() {
       return new Context();
    }
 
 
    @Override public void init(Serializable serializable) throws Exception {
 
    }
 
 
    @Override public void accumulate(Serializable context, Object value) {
       Context c = (Context) context;
       Order order = (Order) value;
       double discount = order.getDiscount() == null ? 0 : order.getDiscount().getPercentage();
       double orderTotal = order.getTotal() - (order.getTotal() * discount);
       if(orderTotal > c.maxTotal) {
          c.maxTotal = orderTotal;
          c.maxOrder = order;
       }
    }
 
 
    @Override public void reverse(Serializable serializable, Object o) throws Exception {
 
    }
 
 
    @Override public Object getResult(Serializable context) throws Exception {
       return ((Context)context).maxOrder;
    }
 
 
    @Override public boolean supportsReverse() {
       return false;
    }
 
 
    @Override public Class<?> getResultType() {
       return Order.class;
    }
 
 
    @Override public void writeExternal(ObjectOutput out) throws IOException {
 
    }
 
 
    @Override public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
 
    }
 }

Event Listeners

The sample code below show how to use event listener:

StatefulRuleEngine ruleRunner = new StatefulKieRuleEngine();
ruleRunner.addRules("tutorials/order-total-rules.drl", FileUtils.readToEnd("tutorials/order-total-rules.drl"));
ruleRunner.addRules("tutorials/anomaly-detection-rules.drl", FileUtils.readToEnd("tutorials/anomaly-detection-rules.drl"));
ruleRunner.addRules("tutorials/anomaly-detection-result-queries.drl", FileUtils.readToEnd("tutorials/anomaly-detection-result-queries.drl"));


ruleRunner.buildKnowledgeSession();



Customer customer1 = new Customer();
customer1.setCustomerId(1L);

Order customer1Order = ModelFactory.getPendingOrderWithTotalValueGreaterThan10000(customer1);

Customer customer2 = new Customer();
customer2.setCustomerId(2L);

Order customer2Order = ModelFactory.getPendingOrderWithTotalValueLessThan10000(customer1);


OrderService orderService = new OrderServiceImpl();
orderService.save(customer1Order);
orderService.save(customer2Order);

AuditService auditService = new AuditServiceImpl();

ruleRunner.insert(customer1);
ruleRunner.insert(customer2);

ruleRunner.setGlobal("orderService", orderService);
ruleRunner.setGlobal("amountThreshold", 10000.00);
ruleRunner.setGlobal("results", new HashSet<>());
ruleRunner.setGlobal("auditService", auditService);

ruleRunner.registerChannel("audit-channel", o -> logger.info("receive from channel: {}", o));

ruleRunner.getSession().addEventListener(new RuleRuntimeEventListener() {
 @Override public void objectInserted(ObjectInsertedEvent objectInsertedEvent) {

 }


 @Override public void objectUpdated(ObjectUpdatedEvent objectUpdatedEvent) {

 }


 @Override public void objectDeleted(ObjectDeletedEvent objectDeletedEvent) {

 }
});

ruleRunner.getSession().addEventListener(new AgendaEventListener() {
 @Override public void matchCreated(MatchCreatedEvent matchCreatedEvent) {

 }


 @Override public void matchCancelled(MatchCancelledEvent matchCancelledEvent) {

 }


 @Override public void beforeMatchFired(BeforeMatchFiredEvent beforeMatchFiredEvent) {
    logger.info("before match fired: {}", beforeMatchFiredEvent.getMatch().getRule().getName());
 }


 @Override public void afterMatchFired(AfterMatchFiredEvent afterMatchFiredEvent) {
    logger.info("after match fired: {}", afterMatchFiredEvent.getMatch().getRule().getName());
 }


 @Override public void agendaGroupPopped(AgendaGroupPoppedEvent agendaGroupPoppedEvent) {

 }


 @Override public void agendaGroupPushed(AgendaGroupPushedEvent agendaGroupPushedEvent) {

 }


 @Override public void beforeRuleFlowGroupActivated(RuleFlowGroupActivatedEvent ruleFlowGroupActivatedEvent) {

 }


 @Override public void afterRuleFlowGroupActivated(RuleFlowGroupActivatedEvent ruleFlowGroupActivatedEvent) {

 }


 @Override public void beforeRuleFlowGroupDeactivated(RuleFlowGroupDeactivatedEvent ruleFlowGroupDeactivatedEvent) {

 }


 @Override public void afterRuleFlowGroupDeactivated(RuleFlowGroupDeactivatedEvent ruleFlowGroupDeactivatedEvent) {

 }
});

ruleRunner.fireAllRules();

Versions

Version
1.0.1