Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add working memory abstraction #37

Closed
fmbenhassine opened this issue May 4, 2016 · 2 comments
Closed

Add working memory abstraction #37

fmbenhassine opened this issue May 4, 2016 · 2 comments

Comments

@fmbenhassine
Copy link
Member

fmbenhassine commented May 4, 2016

In the real world :

  • A rule condition is evaluated against a set of known facts
  • A rule action acts on these facts and may add/remove/modify facts

The set of known facts is the working memory of a rules engine. It allows rules to add/remove/modify facts. This is a key concept to implement backward/forward chaining.

Moreover, having a separate working memory decouples a rule from data it operates on. This was the major drawback in Easy Rules v2 which caused a lot of issues regarding thread safety.

So having the following signature of evaluate and execute methods in the Rule API should make sense:

/**
 * Rule condition : return true if the rule should be applied given the known facts, false otherwise
 */
boolean evaluate(Facts facts);

/**
 * Rule action : may add/remove/modify facts
 */
void execute(Facts facts) throws Exception;

The goal is to add a Facts abstraction to represent a set of named facts and a @Fact annotation to mark a parameter as a fact when using a POJO as a rule. Any java object can be used as a fact (Facts is a Map<String, Object>).

Here is an example of a rule:

@Rule
class WeatherRule {
    @Condition
    public boolean itRains(@Fact("rain") boolean rain) {
        return rain;
    }

    @Action
    public void takeAnUmbrella(Facts facts) {
        System.out.println("It rains, take an umbrella!");
        // can add/remove/modify facts
    }
}

And how to use it:

@Test
public void test() throws Exception {
    // define facts
    Facts facts = new Facts();
    facts.add("rain", true);

    // define rules
    Rules rules = new Rules(new WeatherRule());

    // fire rules on known facts
    RulesEngine rulesEngine = new DefaultRulesEngine();
    rulesEngine.fire(rules, facts);
}

Easy Rules should make sure to inject the facts (by name) in the parameters of condition and action methods. If a parameter is of type Facts, the engine will inject all facts (by type).

This new API design should address the major thread safety issue of v2:

  • The rules are stateless and separated from the working memory
  • The rules engine is stateless and can be safely shared between threads
  • The working memory acts as a session for the fire method and should be created for each thread (each thread has its own set of data to operate on but the rules and the engine can be reused)
@fmbenhassine
Copy link
Member Author

Version 3.0.0-SNAPSHOT has been deployed (use this repo to import it).

Here is sample output for the previous example:

[INFO] Engine name: engine 
[INFO] Rule priority threshold: 2,147,483,647 
[INFO] Skip on first applied rule: false 
[INFO] Skip on first non triggered rule: false 
[INFO] Skip on first failed rule: false 
[INFO] Registered rules: 
[INFO] Rule { name = 'WeatherRule', description = 'when itRains then takeAnUmbrella', priority = '2147483646'} 
[INFO] Known facts: 
[INFO] Fact { rain : true } 
[INFO] Rules evaluation started 
[INFO] Rule 'WeatherRule' triggered
It rains, take an umbrella!
[INFO] Rule 'WeatherRule' performed successfully 

@sheldon-sminq
Copy link

Thanks for the update, will check the new version out.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Development

No branches or pull requests

2 participants