BAEL-5143 (#11273)
* BAEL-5143 * Modified as per PR comments * The List<> type for session data is changed to a more generic Collection<> * Version upgrade Co-authored-by: 0swald <andrey.bichkevskiy@gmail.com>
This commit is contained in:
parent
4dfe21a3dc
commit
025afb1f7d
35
rule-engines/evrete/pom.xml
Normal file
35
rule-engines/evrete/pom.xml
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<groupId>com.baeldung.evrete</groupId>
|
||||||
|
<artifactId>evrete</artifactId>
|
||||||
|
<version>1.0</version>
|
||||||
|
<name>evrete</name>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<evrete.version>2.1.04</evrete.version>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
<parent>
|
||||||
|
<groupId>com.baeldung</groupId>
|
||||||
|
<artifactId>rule-engines</artifactId>
|
||||||
|
<version>1.0.0-SNAPSHOT</version>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<!-- Core library -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.evrete</groupId>
|
||||||
|
<artifactId>evrete-core</artifactId>
|
||||||
|
<version>${evrete.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<!-- Annotated Java classes -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.evrete</groupId>
|
||||||
|
<artifactId>evrete-dsl-java</artifactId>
|
||||||
|
<version>${evrete.version}</version>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
</project>
|
@ -0,0 +1,47 @@
|
|||||||
|
package com.baeldung.evrete.introduction;
|
||||||
|
|
||||||
|
import com.baeldung.evrete.introduction.model.Customer;
|
||||||
|
import com.baeldung.evrete.introduction.model.Invoice;
|
||||||
|
import org.evrete.KnowledgeService;
|
||||||
|
import org.evrete.api.Knowledge;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
public class IntroductionAJR {
|
||||||
|
public static void main(String[] args) throws IOException {
|
||||||
|
ClassLoader classLoader = IntroductionAJR.class.getClassLoader();
|
||||||
|
KnowledgeService service = new KnowledgeService();
|
||||||
|
URL rulesetUrl = classLoader.getResource("rules/SalesRuleset.java");
|
||||||
|
Knowledge knowledge = service.newKnowledge(
|
||||||
|
"JAVA-SOURCE",
|
||||||
|
rulesetUrl
|
||||||
|
);
|
||||||
|
|
||||||
|
List<Customer> customers = Arrays.asList(
|
||||||
|
new Customer("Customer A"),
|
||||||
|
new Customer("Customer B"),
|
||||||
|
new Customer("Customer C")
|
||||||
|
);
|
||||||
|
|
||||||
|
Random random = new Random();
|
||||||
|
Collection<Object> sessionData = new LinkedList<>(customers);
|
||||||
|
for (int i = 0; i < 100_000; i++) {
|
||||||
|
Customer randomCustomer = customers.get(random.nextInt(customers.size()));
|
||||||
|
Invoice invoice = new Invoice(randomCustomer, 100 * random.nextDouble());
|
||||||
|
sessionData.add(invoice);
|
||||||
|
}
|
||||||
|
|
||||||
|
knowledge
|
||||||
|
.newStatelessSession()
|
||||||
|
.insert(sessionData)
|
||||||
|
.fire();
|
||||||
|
|
||||||
|
for (Customer c : customers) {
|
||||||
|
System.out.printf("%s:\t$%,.2f%n", c.getName(), c.getTotal());
|
||||||
|
}
|
||||||
|
|
||||||
|
service.shutdown();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,58 @@
|
|||||||
|
package com.baeldung.evrete.introduction;
|
||||||
|
|
||||||
|
import org.evrete.KnowledgeService;
|
||||||
|
import org.evrete.api.Knowledge;
|
||||||
|
import com.baeldung.evrete.introduction.model.*;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
public class IntroductionInline {
|
||||||
|
public static void main(String[] args) {
|
||||||
|
KnowledgeService service = new KnowledgeService();
|
||||||
|
Knowledge knowledge = service
|
||||||
|
.newKnowledge()
|
||||||
|
.newRule("Clear total sales")
|
||||||
|
.forEach("$c", Customer.class)
|
||||||
|
.execute(ctx -> {
|
||||||
|
Customer c = ctx.get("$c");
|
||||||
|
c.setTotal(0.0);
|
||||||
|
})
|
||||||
|
.newRule("Compute totals")
|
||||||
|
.forEach(
|
||||||
|
"$c", Customer.class,
|
||||||
|
"$i", Invoice.class
|
||||||
|
)
|
||||||
|
.where("$i.customer == $c")
|
||||||
|
.execute(ctx -> {
|
||||||
|
Customer c = ctx.get("$c");
|
||||||
|
Invoice i = ctx.get("$i");
|
||||||
|
c.addToTotal(i.getAmount());
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
List<Customer> customers = Arrays.asList(
|
||||||
|
new Customer("Customer A"),
|
||||||
|
new Customer("Customer B"),
|
||||||
|
new Customer("Customer C")
|
||||||
|
);
|
||||||
|
|
||||||
|
Random random = new Random();
|
||||||
|
Collection<Object> sessionData = new LinkedList<>(customers);
|
||||||
|
for (int i = 0; i < 100_000; i++) {
|
||||||
|
Customer randomCustomer = customers.get(random.nextInt(customers.size()));
|
||||||
|
Invoice invoice = new Invoice(randomCustomer, 100 * random.nextDouble());
|
||||||
|
sessionData.add(invoice);
|
||||||
|
}
|
||||||
|
|
||||||
|
knowledge
|
||||||
|
.newStatelessSession()
|
||||||
|
.insert(sessionData)
|
||||||
|
.fire();
|
||||||
|
|
||||||
|
for (Customer c : customers) {
|
||||||
|
System.out.printf("%s:\t$%,.2f%n", c.getName(), c.getTotal());
|
||||||
|
}
|
||||||
|
|
||||||
|
service.shutdown();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,26 @@
|
|||||||
|
package com.baeldung.evrete.introduction.model;
|
||||||
|
|
||||||
|
public class Customer {
|
||||||
|
private double total = 0.0;
|
||||||
|
private final String name;
|
||||||
|
|
||||||
|
public Customer(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addToTotal(double amount) {
|
||||||
|
this.total += amount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getTotal() {
|
||||||
|
return total;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTotal(double total) {
|
||||||
|
this.total = total;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,19 @@
|
|||||||
|
package com.baeldung.evrete.introduction.model;
|
||||||
|
|
||||||
|
public class Invoice {
|
||||||
|
private final Customer customer;
|
||||||
|
private final double amount;
|
||||||
|
|
||||||
|
public Invoice(Customer customer, double amount) {
|
||||||
|
this.customer = customer;
|
||||||
|
this.amount = amount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Customer getCustomer() {
|
||||||
|
return customer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getAmount() {
|
||||||
|
return amount;
|
||||||
|
}
|
||||||
|
}
|
13
rule-engines/evrete/src/main/resources/logback.xml
Normal file
13
rule-engines/evrete/src/main/resources/logback.xml
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<configuration>
|
||||||
|
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
|
||||||
|
<encoder>
|
||||||
|
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
|
||||||
|
</pattern>
|
||||||
|
</encoder>
|
||||||
|
</appender>
|
||||||
|
|
||||||
|
<root level="INFO">
|
||||||
|
<appender-ref ref="STDOUT" />
|
||||||
|
</root>
|
||||||
|
</configuration>
|
@ -0,0 +1,20 @@
|
|||||||
|
package org.abc.author1;
|
||||||
|
|
||||||
|
import com.baeldung.evrete.introduction.model.Customer;
|
||||||
|
import com.baeldung.evrete.introduction.model.Invoice;
|
||||||
|
import org.evrete.dsl.annotation.Rule;
|
||||||
|
import org.evrete.dsl.annotation.Where;
|
||||||
|
|
||||||
|
public class SalesRuleset {
|
||||||
|
|
||||||
|
@Rule
|
||||||
|
public void rule1(Customer $c) {
|
||||||
|
$c.setTotal(0.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Rule
|
||||||
|
@Where("$i.customer == $c")
|
||||||
|
public void rule2(Customer $c, Invoice $i) {
|
||||||
|
$c.addToTotal($i.getAmount());
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,80 @@
|
|||||||
|
package com.baeldung.evrete.introduction;
|
||||||
|
|
||||||
|
import com.baeldung.evrete.introduction.model.Customer;
|
||||||
|
import com.baeldung.evrete.introduction.model.Invoice;
|
||||||
|
import org.evrete.KnowledgeService;
|
||||||
|
import org.evrete.api.Knowledge;
|
||||||
|
import org.evrete.api.RuleSession;
|
||||||
|
import org.junit.jupiter.api.AfterAll;
|
||||||
|
import org.junit.jupiter.api.BeforeAll;
|
||||||
|
import org.junit.jupiter.params.ParameterizedTest;
|
||||||
|
import org.junit.jupiter.params.provider.ValueSource;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
class IntroductionAJRUnitTest {
|
||||||
|
private static KnowledgeService service;
|
||||||
|
|
||||||
|
@BeforeAll
|
||||||
|
static void setUpClass() {
|
||||||
|
service = new KnowledgeService();
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterAll
|
||||||
|
static void shutDownClass() {
|
||||||
|
service.shutdown();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This test makes sure that each customer's actual total sales is equal to the amount
|
||||||
|
* computed by the rule engine
|
||||||
|
*/
|
||||||
|
@ParameterizedTest
|
||||||
|
@ValueSource(strings = {"true", "false"})
|
||||||
|
void sessionTotalsTest(String type) throws IOException {
|
||||||
|
boolean stateful = Boolean.parseBoolean(type);
|
||||||
|
ClassLoader classLoader = IntroductionAJR.class.getClassLoader();
|
||||||
|
KnowledgeService service = new KnowledgeService();
|
||||||
|
Knowledge knowledge = service
|
||||||
|
.newKnowledge(
|
||||||
|
"JAVA-SOURCE",
|
||||||
|
classLoader.getResource("rules/SalesRuleset.java")
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
List<Customer> customers = Arrays.asList(
|
||||||
|
new Customer("Customer A"),
|
||||||
|
new Customer("Customer B"),
|
||||||
|
new Customer("Customer C")
|
||||||
|
);
|
||||||
|
Collection<Object> sessionData = new LinkedList<>(customers);
|
||||||
|
|
||||||
|
HashMap<Customer, Double> actualTotals = new HashMap<>();
|
||||||
|
Random random = new Random();
|
||||||
|
for (int i = 0; i < 1_000; i++) {
|
||||||
|
Customer randomCustomer = customers.get(random.nextInt(customers.size()));
|
||||||
|
Invoice invoice = new Invoice(randomCustomer, random.nextInt(100));
|
||||||
|
sessionData.add(invoice);
|
||||||
|
|
||||||
|
Double d = actualTotals.get(randomCustomer);
|
||||||
|
if(d == null) {
|
||||||
|
d = 0.0;
|
||||||
|
}
|
||||||
|
d = d + invoice.getAmount();
|
||||||
|
actualTotals.put(randomCustomer, d);
|
||||||
|
}
|
||||||
|
|
||||||
|
RuleSession<?> session = stateful ? knowledge.newStatefulSession() : knowledge.newStatelessSession();
|
||||||
|
session
|
||||||
|
.insert(sessionData)
|
||||||
|
.fire();
|
||||||
|
|
||||||
|
for(Customer c : customers) {
|
||||||
|
double d1 = c.getTotal();
|
||||||
|
double d2 = actualTotals.get(c);
|
||||||
|
assert d1 == d2;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,90 @@
|
|||||||
|
package com.baeldung.evrete.introduction;
|
||||||
|
|
||||||
|
import com.baeldung.evrete.introduction.model.Customer;
|
||||||
|
import com.baeldung.evrete.introduction.model.Invoice;
|
||||||
|
import org.evrete.KnowledgeService;
|
||||||
|
import org.evrete.api.Knowledge;
|
||||||
|
import org.evrete.api.RuleSession;
|
||||||
|
import org.junit.jupiter.api.AfterAll;
|
||||||
|
import org.junit.jupiter.api.BeforeAll;
|
||||||
|
import org.junit.jupiter.params.ParameterizedTest;
|
||||||
|
import org.junit.jupiter.params.provider.ValueSource;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
class IntroductionInlineUnitTest {
|
||||||
|
private static KnowledgeService service;
|
||||||
|
|
||||||
|
@BeforeAll
|
||||||
|
static void setUpClass() {
|
||||||
|
service = new KnowledgeService();
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterAll
|
||||||
|
static void shutDownClass() {
|
||||||
|
service.shutdown();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This test makes sure that each customer's actual total sales is equal to the amount
|
||||||
|
* computed by the rule engine
|
||||||
|
*/
|
||||||
|
@ParameterizedTest
|
||||||
|
@ValueSource(strings = {"true", "false"})
|
||||||
|
void sessionTotalsTest(String type) {
|
||||||
|
boolean stateful = Boolean.parseBoolean(type);
|
||||||
|
Knowledge knowledge = service
|
||||||
|
.newKnowledge()
|
||||||
|
.newRule("Clear customer's total sales")
|
||||||
|
.forEach("$c", Customer.class)
|
||||||
|
.execute(ctx -> {
|
||||||
|
Customer c = ctx.get("$c");
|
||||||
|
c.setTotal(0.0);
|
||||||
|
})
|
||||||
|
.newRule("Compute totals")
|
||||||
|
.forEach(
|
||||||
|
"$c", Customer.class,
|
||||||
|
"$i", Invoice.class
|
||||||
|
)
|
||||||
|
.where("$i.customer == $c")
|
||||||
|
.execute(ctx -> {
|
||||||
|
Customer c = ctx.get("$c");
|
||||||
|
Invoice i = ctx.get("$i");
|
||||||
|
c.addToTotal(i.getAmount());
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
List<Customer> customers = Arrays.asList(
|
||||||
|
new Customer("Customer A"),
|
||||||
|
new Customer("Customer B"),
|
||||||
|
new Customer("Customer C")
|
||||||
|
);
|
||||||
|
Collection<Object> sessionData = new LinkedList<>(customers);
|
||||||
|
|
||||||
|
HashMap<Customer, Double> actualTotals = new HashMap<>();
|
||||||
|
Random random = new Random();
|
||||||
|
for (int i = 0; i < 1_000; i++) {
|
||||||
|
Customer randomCustomer = customers.get(random.nextInt(customers.size()));
|
||||||
|
Invoice invoice = new Invoice(randomCustomer, random.nextInt(100));
|
||||||
|
sessionData.add(invoice);
|
||||||
|
|
||||||
|
Double d = actualTotals.get(randomCustomer);
|
||||||
|
if(d == null) {
|
||||||
|
d = 0.0;
|
||||||
|
}
|
||||||
|
d = d + invoice.getAmount();
|
||||||
|
actualTotals.put(randomCustomer, d);
|
||||||
|
}
|
||||||
|
|
||||||
|
RuleSession<?> session = stateful ? knowledge.newStatefulSession() : knowledge.newStatelessSession();
|
||||||
|
session
|
||||||
|
.insert(sessionData)
|
||||||
|
.fire();
|
||||||
|
|
||||||
|
for(Customer c : customers) {
|
||||||
|
double d1 = c.getTotal();
|
||||||
|
double d2 = actualTotals.get(c);
|
||||||
|
assert d1 == d2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -15,6 +15,7 @@
|
|||||||
|
|
||||||
<modules>
|
<modules>
|
||||||
<module>easy-rules</module>
|
<module>easy-rules</module>
|
||||||
|
<module>evrete</module>
|
||||||
<module>openl-tablets</module>
|
<module>openl-tablets</module>
|
||||||
<module>rulebook</module>
|
<module>rulebook</module>
|
||||||
<!-- <module>jess</module> --> <!-- requires dependencies which are not publicly available -->
|
<!-- <module>jess</module> --> <!-- requires dependencies which are not publicly available -->
|
||||||
|
Loading…
x
Reference in New Issue
Block a user