Skip to content

Commit

Permalink
add COLA rule engine
Browse files Browse the repository at this point in the history
  • Loading branch information
Frank authored and Frank committed Feb 25, 2024
1 parent ca12d5f commit bde4670
Show file tree
Hide file tree
Showing 31 changed files with 2,389 additions and 415 deletions.
20 changes: 20 additions & 0 deletions cola-components/cola-component-ruleengine/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
## 介绍
这是COLA规则引擎

## 使用
hello world 案例:
```java
RuleEngine ruleEngine = new DefaultRuleEngine();
Rule rule = new RuleBuilder()
.name("hello world rule")
.description("always say hello world")
.priority(1)
.when(facts -> true)
.then(facts -> System.out.println("hello world"))
.build();
Rules rules = new Rules();
rules.register(rule);

ruleEngine.fire(rules, null);
```

26 changes: 26 additions & 0 deletions cola-components/cola-component-ruleengine/gitignore.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
target/

### STS ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans

### IntelliJ IDEA ###
.idea
*.iws
*.iml
*.ipr
out/

### NetBeans ###
nbproject/private/
build/
nbbuild/
dist/
nbdist/
bin/
doc/
.DS_Store
85 changes: 85 additions & 0 deletions cola-components/cola-component-ruleengine/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
<?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>
<parent>
<groupId>com.alibaba.cola</groupId>
<artifactId>cola-components-parent</artifactId>
<version>4.4.0-SNAPSHOT</version>
</parent>

<artifactId>cola-component-ruleengine</artifactId>
<packaging>jar</packaging>
<name>${project.artifactId}:${project.version}</name>
<description>${project.artifactId}</description>
<url>https://github.com/alibaba/COLA</url>

<licenses>
<license>
<name>GNU Lesser General Public License v2.1</name>
<url>https://github.com/alibaba/COLA/blob/master/LICENSE</url>
<distribution>repo</distribution>
</license>
</licenses>
<scm>
<connection>scm:git:https://github.com/alibaba/COLA.git</connection>
<developerConnection>scm:git:https://github.com/alibaba/COLA.git</developerConnection>
<url>https://github.com/alibaba/COLA</url>
</scm>
<issueManagement>
<url>https://github.com/alibaba/COLA/issues</url>
<system>GitHub Issues</system>
</issueManagement>
<developers>
<developer>
<id>significantfrank</id>
<name>Frank Zhang</name>
<email>25216348(at)qq.com</email>
<roles>
<role>Developer</role>
<role>Architect</role>
</roles>
<timezone>+8</timezone>
<url>https://github.com/significantfrank</url>
</developer>
<developer>
<id>oldratlee</id>
<name>Jerry Lee</name>
<email>oldratlee(at)gmail.com</email>
<roles>
<role>Developer</role>
<role>CI/SCM Engineer</role>
</roles>
<timezone>+8</timezone>
<url>https://github.com/oldratlee</url>
</developer>
</developers>

<dependencies>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>

<!-- test dependencies -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.alibaba.cola.ruleengine.api;

@FunctionalInterface
public interface Action {
void execute(Facts facts);
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package com.alibaba.cola.ruleengine.api;

import java.util.Objects;

@FunctionalInterface
public interface Condition {

boolean evaluate(Facts facts);

//谓词and逻辑,参考Predicate
default Condition and(Condition other) {
Objects.requireNonNull(other);
return (facts) -> {
return this.evaluate(facts) && other.evaluate(facts);
};
}

//谓词or逻辑,参考Predicate
default Condition or(Condition other) {
Objects.requireNonNull(other);
return (facts) -> {
return this.evaluate(facts) || other.evaluate(facts);
};
}

/**
* A NoOp {@link Condition} that always returns false.
*/
Condition FALSE = facts -> false;

/**
* A NoOp {@link Condition} that always returns true.
*/
Condition TRUE = facts -> true;
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package com.alibaba.cola.ruleengine.api;

import java.util.Objects;

public class Fact<T> {

private final String name;
private final T value;

/**
* Create a new fact.
* @param name of the fact
* @param value of the fact
*/
public Fact(String name, T value) {
Objects.requireNonNull(name, "name must not be null");
Objects.requireNonNull(value, "value must not be null");
this.name = name;
this.value = value;
}

/**
* Get the fact name.
* @return fact name
*/
public String getName() {
return name;
}

/**
* Get the fact value.
* @return fact value
*/
public T getValue() {
return value;
}

@Override
public String toString() {
return "Fact{" +
"name='" + name + '\'' +
", value=" + value +
'}';
}

/*
* The Facts API represents a namespace for facts where each fact has a unique name.
* Hence, equals/hashcode are deliberately calculated only on the fact name.
*/

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Fact<?> fact = (Fact<?>) o;
return name.equals(fact.name);
}

@Override
public int hashCode() {
return Objects.hash(name);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
package com.alibaba.cola.ruleengine.api;

import java.util.*;

public class Facts implements Iterable<Fact<?>> {

private final Set<Fact<?>> facts = new HashSet<>();

/**
* Add a fact, replacing any fact with the same name.
*
* @param name of the fact to add, must not be null
* @param value of the fact to add, must not be null
*/
public <T> void put(String name, T value) {
Objects.requireNonNull(name, "fact name must not be null");
Objects.requireNonNull(value, "fact value must not be null");
Fact<?> retrievedFact = getFact(name);
if (retrievedFact != null) {
remove(retrievedFact);
}
add(new Fact<>(name, value));
}

/**
* Add a fact, replacing any fact with the same name.
*
* @param fact to add, must not be null
*/
public <T> void add(Fact<T> fact) {
Objects.requireNonNull(fact, "fact must not be null");
Fact<?> retrievedFact = getFact(fact.getName());
if (retrievedFact != null) {
remove(retrievedFact);
}
facts.add(fact);
}

/**
* Remove a fact by name.
*
* @param factName name of the fact to remove, must not be null
*/
public void remove(String factName) {
Objects.requireNonNull(factName, "fact name must not be null");
Fact<?> fact = getFact(factName);
if (fact != null) {
remove(fact);
}
}

/**
* Remove a fact.
*
* @param fact to remove, must not be null
*/
public <T> void remove(Fact<T> fact) {
Objects.requireNonNull(fact, "fact must not be null");
facts.remove(fact);
}

/**
* Get the value of a fact by its name. This is a convenience method provided
* as a short version of {@code getFact(factName).getValue()}.
*
* @param factName name of the fact, must not be null
* @param <T> type of the fact's value
* @return the value of the fact having the given name, or null if there is
* no fact with the given name
*/
@SuppressWarnings("unchecked")
public <T> T get(String factName) {
Objects.requireNonNull(factName, "fact name must not be null");
Fact<?> fact = getFact(factName);
if (fact != null) {
return (T) fact.getValue();
}
return null;
}

/**
* Get a fact by name.
*
* @param factName name of the fact, must not be null
* @return the fact having the given name, or null if there is no fact with the given name
*/
public Fact<?> getFact(String factName) {
Objects.requireNonNull(factName, "fact name must not be null");
return facts.stream()
.filter(fact -> fact.getName().equals(factName))
.findFirst()
.orElse(null);
}

/**
* Return a copy of the facts as a map. It is not intended to manipulate
* facts outside of the rules engine (aka other than manipulating them through rules).
*
* @return a copy of the current facts as a {@link HashMap}
*/
public Map<String, Object> asMap() {
Map<String, Object> map = new HashMap<>();
for (Fact<?> fact : facts) {
map.put(fact.getName(), fact.getValue());
}
return map;
}

/**
* Return an iterator on the set of facts. It is not intended to remove
* facts using this iterator outside of the rules engine (aka other than doing it through rules)
*
* @return an iterator on the set of facts
*/
@Override
public Iterator<Fact<?>> iterator() {
return facts.iterator();
}

/**
* Clear facts.
*/
public void clear() {
facts.clear();
}

@Override
public String toString() {
Iterator<Fact<?>> iterator = facts.iterator();
StringBuilder stringBuilder = new StringBuilder("[");
while (iterator.hasNext()) {
stringBuilder.append(iterator.next().toString());
if (iterator.hasNext()) {
stringBuilder.append(",");
}
}
stringBuilder.append("]");
return stringBuilder.toString();
}
}
Loading

0 comments on commit bde4670

Please sign in to comment.