Skip to content

Commit

Permalink
issue #121: Add RulesEngineListener
Browse files Browse the repository at this point in the history
  • Loading branch information
fmbenhassine committed Dec 11, 2017
1 parent fdb0158 commit 4d2dad6
Show file tree
Hide file tree
Showing 7 changed files with 227 additions and 12 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,13 @@ public interface RulesEngine {
*/
List<RuleListener> getRuleListeners();

/**
* Return the list of registered rules engine listeners.
*
* @return the list of registered rules engine listeners
*/
List<RulesEngineListener> getRulesEngineListeners();

/**
* Fire all registered rules on given facts.
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/**
* The MIT License
*
* Copyright (c) 2017, Mahmoud Ben Hassine ([email protected])
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package org.jeasy.rules.api;

import org.jeasy.rules.core.InferenceRulesEngine;

/**
* A listener for rules engine execution events.
*
* @author Mahmoud Ben Hassine ([email protected])
*/
public interface RulesEngineListener {

/**
* Triggered before firing the rule set.
* <strong>When this listener is used with a {@link InferenceRulesEngine}, this method will be triggered before each candidate rule set in each iteration.</strong>
*
* @param rules to fire
* @param facts present before firing rules
*/
void beforeFiringRules(Rules rules, Facts facts);

/**
* Triggered after firing the rule set
* <strong>When this listener is used with a {@link InferenceRulesEngine}, this method will be triggered after each candidate rule set in each iteration.</strong>
*
* @param rules fired
* @param facts present after firing rules
*/
void afterFiringRules(Rules rules, Facts facts);
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import org.jeasy.rules.api.Facts;
import org.jeasy.rules.api.Rule;
import org.jeasy.rules.api.RuleListener;
import org.jeasy.rules.api.RulesEngineListener;
import org.jeasy.rules.api.Rules;
import org.jeasy.rules.api.RulesEngine;
import org.slf4j.Logger;
Expand All @@ -48,21 +49,15 @@ public final class DefaultRulesEngine implements RulesEngine {

private static final Logger LOGGER = LoggerFactory.getLogger(DefaultRuleListener.class);

/**
* The engine parameters
*/
private RulesEngineParameters parameters;

/**
* The registered rule listeners.
*/
private List<RuleListener> ruleListeners;
private List<RulesEngineListener> rulesEngineListeners;

/**
* Create a new {@link DefaultRulesEngine} with default parameters.
*/
public DefaultRulesEngine() {
this(new RulesEngineParameters(), new ArrayList<RuleListener>());
this(new RulesEngineParameters());
}

/**
Expand All @@ -78,13 +73,26 @@ public DefaultRulesEngine(final RulesEngineParameters parameters) {
* Create a new {@link DefaultRulesEngine}.
*
* @param parameters of the engine
* @param ruleListeners listener of rules
* @param ruleListeners to apply for each rule
*/
public DefaultRulesEngine(final RulesEngineParameters parameters, final List<RuleListener> ruleListeners) {
this(parameters, ruleListeners, new ArrayList<RulesEngineListener>());
}

/**
* Create a new {@link DefaultRulesEngine}.
*
* @param parameters of the engine
* @param ruleListeners to apply for each rule
* @param rulesEngineListeners to apply for each rule set
*/
public DefaultRulesEngine(final RulesEngineParameters parameters, final List<RuleListener> ruleListeners, final List<RulesEngineListener> rulesEngineListeners) {
this.parameters = parameters;
this.ruleListeners = new ArrayList<>();
this.ruleListeners.add(new DefaultRuleListener());
this.ruleListeners.addAll(ruleListeners);
this.rulesEngineListeners = new ArrayList<>();
this.rulesEngineListeners.addAll(rulesEngineListeners);
}

@Override
Expand All @@ -97,8 +105,14 @@ public List<RuleListener> getRuleListeners() {
return ruleListeners;
}

@Override
public List<RulesEngineListener> getRulesEngineListeners() {
return rulesEngineListeners;
}

@Override
public void fire(Rules rules, Facts facts) {
triggerListenersBeforeRules(rules, facts);
if (rules.isEmpty()) {
LOGGER.warn("No rules registered! Nothing to apply");
return;
Expand All @@ -107,6 +121,7 @@ public void fire(Rules rules, Facts facts) {
log(rules);
log(facts);
apply(rules, facts);
triggerListenersAfterRules(rules, facts);
}

@Override
Expand Down Expand Up @@ -196,6 +211,18 @@ private void triggerListenersAfterEvaluate(Rule rule, Facts facts, boolean evalu
}
}

private void triggerListenersBeforeRules(Rules rule, Facts facts) {
for (RulesEngineListener rulesEngineListener : rulesEngineListeners) {
rulesEngineListener.beforeFiringRules(rule, facts);
}
}

private void triggerListenersAfterRules(Rules rule, Facts facts) {
for (RulesEngineListener rulesEngineListener : rulesEngineListeners) {
rulesEngineListener.afterFiringRules(rule, facts);
}
}

private boolean shouldBeEvaluated(Rule rule, Facts facts) {
return triggerListenersBeforeEvaluate(rule, facts);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ public final class InferenceRulesEngine implements RulesEngine {

private RulesEngineParameters parameters;
private List<RuleListener> ruleListeners;
private List<RulesEngineListener> rulesEngineListeners;
private DefaultRulesEngine delegate;

/**
Expand All @@ -55,6 +56,7 @@ public InferenceRulesEngine() {

/**
* Create a new inference rules engine.
*
* @param parameters of the engine
*/
public InferenceRulesEngine(RulesEngineParameters parameters) {
Expand All @@ -63,13 +65,25 @@ public InferenceRulesEngine(RulesEngineParameters parameters) {

/**
* Create a new inference rules engine.
*
* @param parameters of the engine
* @param ruleListeners to apply for each rule
*/
public InferenceRulesEngine(RulesEngineParameters parameters, List<RuleListener> ruleListeners) {
this(parameters, ruleListeners, new ArrayList<RulesEngineListener>());
}

/**
* Create a new inference rules engine.
* @param parameters of the engine
* @param ruleListeners to apply for each rule
* @param rulesEngineListeners to apply for each rule set
*/
public InferenceRulesEngine(RulesEngineParameters parameters, List<RuleListener> ruleListeners, List<RulesEngineListener> rulesEngineListeners) {
this.parameters = parameters;
this.ruleListeners = ruleListeners;
delegate = new DefaultRulesEngine(parameters, ruleListeners);
this.rulesEngineListeners = rulesEngineListeners;
delegate = new DefaultRulesEngine(parameters, ruleListeners, rulesEngineListeners);
}

@Override
Expand All @@ -82,6 +96,11 @@ public List<RuleListener> getRuleListeners() {
return ruleListeners;
}

@Override
public List<RulesEngineListener> getRulesEngineListeners() {
return rulesEngineListeners;
}

@Override
public void fire(Rules rules, Facts facts) {
Set<Rule> selectedRules;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@

import org.jeasy.rules.api.RuleListener;
import org.jeasy.rules.api.RulesEngine;
import org.jeasy.rules.api.RulesEngineListener;

import java.util.ArrayList;
import java.util.List;
Expand All @@ -39,6 +40,7 @@ public class RulesEngineBuilder {
private final RulesEngineParameters parameters;

private final List<RuleListener> ruleListeners;
private final List<RulesEngineListener> rulesEngineListeners;

/**
* Create a new rules engine builder.
Expand All @@ -52,6 +54,7 @@ public static RulesEngineBuilder aNewRulesEngine() {
private RulesEngineBuilder() {
parameters = new RulesEngineParameters(false, false, false, RulesEngineParameters.DEFAULT_RULE_PRIORITY_THRESHOLD);
ruleListeners = new ArrayList<>();
rulesEngineListeners = new ArrayList<>();
}

/**
Expand Down Expand Up @@ -109,6 +112,17 @@ public RulesEngineBuilder withRuleListener(final RuleListener ruleListener) {
return this;
}

/**
* Register a rules engine listener.
*
* @param rulesEngineListener to register
* @return the rules engine builder
*/
public RulesEngineBuilder withRulesEngineListener(final RulesEngineListener rulesEngineListener) {
this.rulesEngineListeners.add(rulesEngineListener);
return this;
}

/**
* @deprecated Silent mode is now log implementation config. Now it uses slf4j facade
* <strong>This will be removed in v3.2</strong>
Expand All @@ -124,7 +138,7 @@ public RulesEngineBuilder withSilentMode(final boolean silentMode) {
* @return a rules engine instance
*/
public RulesEngine build() {
return new DefaultRulesEngine(parameters, ruleListeners);
return new DefaultRulesEngine(parameters, ruleListeners, rulesEngineListeners);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,14 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import java.util.List;
import java.util.Map;
import org.assertj.core.api.Assertions;
import org.jeasy.rules.annotation.Action;
import org.jeasy.rules.annotation.Condition;
import org.jeasy.rules.annotation.Priority;
import org.jeasy.rules.api.RuleListener;
import org.jeasy.rules.api.RulesEngineListener;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
Expand All @@ -52,6 +54,9 @@ public class DefaultRulesEngineTest extends AbstractTest {
@Mock
private RuleListener ruleListener;

@Mock
private RulesEngineListener rulesEngineListener;

private AnnotatedRule annotatedRule;

@Before
Expand Down Expand Up @@ -215,11 +220,30 @@ public void nullFactsShouldNotCrashTheEngine() {

@Test
public void testGetRuleListeners() throws Exception {
// Given
rulesEngine = RulesEngineBuilder.aNewRulesEngine()
.withRuleListener(ruleListener)
.build();

assertThat(rulesEngine.getRuleListeners()).contains(ruleListener);
// When
List<RuleListener> ruleListeners = rulesEngine.getRuleListeners();

// Then
assertThat(ruleListeners).contains(ruleListener);
}

@Test
public void testGetRulesEngineListeners() throws Exception {
// Given
rulesEngine = RulesEngineBuilder.aNewRulesEngine()
.withRulesEngineListener(rulesEngineListener)
.build();

// When
List<RulesEngineListener> rulesEngineListeners = rulesEngine.getRulesEngineListeners();

// Then
assertThat(rulesEngineListeners).contains(rulesEngineListener);
}

@After
Expand Down
Loading

2 comments on commit 4d2dad6

@cemo
Copy link

@cemo cemo commented on 4d2dad6 Dec 11, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, It may be a good idea to add support for checking rules as well. It will be complementary to fire

@fmbenhassine
Copy link
Member Author

@fmbenhassine fmbenhassine commented on 4d2dad6 Dec 11, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes I was thinking of that because currently we apply the rule listener in the check method (but not this new listener). In that case, we have to rename the method from beforeFiringRules to beforeRules (same for after) and apply it. I will do it and keep you informed. Thank you for the review!!

Please sign in to comment.