rename base package to org.jeasy.rules after moving to jeasy organisation
parent
4a4a4469c5
commit
c18f7eb1bb
@ -1,5 +1,5 @@
|
||||
<archetype>
|
||||
<id>easyrules-archetype</id>
|
||||
<id>easy-rules-archetype</id>
|
||||
<sources>
|
||||
<source>src/main/java/HelloWorldRule.java</source>
|
||||
<source>src/main/java/Launcher.java</source>
|
@ -0,0 +1,47 @@
|
||||
/**
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2017, Mahmoud Ben Hassine (mahmoud.benhassine@icloud.com)
|
||||
*
|
||||
* 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.annotation;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* Annotation to mark a method as a rule action.
|
||||
* Must annotate any public method with no arguments.
|
||||
* The method return value will be ignored by the engine.
|
||||
*
|
||||
* @author Mahmoud Ben Hassine (mahmoud.benhassine@icloud.com)
|
||||
*/
|
||||
|
||||
@Inherited
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.METHOD)
|
||||
public @interface Action {
|
||||
|
||||
/**
|
||||
* The order in which the action should be executed.
|
||||
* @return the order in which the action should be executed
|
||||
*/
|
||||
int order() default 0;
|
||||
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
/**
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2017, Mahmoud Ben Hassine (mahmoud.benhassine@icloud.com)
|
||||
*
|
||||
* 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.annotation;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* Annotation to mark a method as a rule condition.
|
||||
* Must annotate any public method with no arguments and that returns a boolean value.
|
||||
*
|
||||
* @author Mahmoud Ben Hassine (mahmoud.benhassine@icloud.com)
|
||||
*/
|
||||
|
||||
@Inherited
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.METHOD)
|
||||
public @interface Condition {
|
||||
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
/**
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2017, Mahmoud Ben Hassine (mahmoud.benhassine@icloud.com)
|
||||
*
|
||||
* 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.annotation;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* Annotation to mark a parameter as a fact.
|
||||
*
|
||||
* @author Mahmoud Ben Hassine (mahmoud.benhassine@icloud.com)
|
||||
*/
|
||||
|
||||
@Inherited
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.PARAMETER})
|
||||
public @interface Fact {
|
||||
String value();
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
/**
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2017, Mahmoud Ben Hassine (mahmoud.benhassine@icloud.com)
|
||||
*
|
||||
* 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.annotation;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* Annotation to mark the method to execute to get rule priority.
|
||||
* Must annotate any public method with no arguments and that returns an integer value.
|
||||
*
|
||||
* @author Mahmoud Ben Hassine (mahmoud.benhassine@icloud.com)
|
||||
*/
|
||||
|
||||
@Inherited
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.METHOD)
|
||||
public @interface Priority {
|
||||
|
||||
}
|
@ -0,0 +1,51 @@
|
||||
/**
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2017, Mahmoud Ben Hassine (mahmoud.benhassine@icloud.com)
|
||||
*
|
||||
* 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.annotation;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* Annotation to mark a class as a rule.
|
||||
*
|
||||
* @author Mahmoud Ben Hassine (mahmoud.benhassine@icloud.com)
|
||||
*/
|
||||
|
||||
@Inherited
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.TYPE)
|
||||
public @interface Rule {
|
||||
|
||||
/**
|
||||
* The rule name which must be unique within an rules registry.
|
||||
* @return The rule name
|
||||
*/
|
||||
String name() default org.jeasy.rules.api.Rule.DEFAULT_NAME;
|
||||
|
||||
/**
|
||||
* The rule description.
|
||||
* @return The rule description
|
||||
*/
|
||||
String description() default org.jeasy.rules.api.Rule.DEFAULT_DESCRIPTION;
|
||||
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
/**
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2017, Mahmoud Ben Hassine (mahmoud.benhassine@icloud.com)
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
/**
|
||||
* This package contains Easy Rules annotations.
|
||||
*/
|
||||
package org.jeasy.rules.annotation;
|
@ -0,0 +1,61 @@
|
||||
/**
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2017, Mahmoud Ben Hassine (mahmoud.benhassine@icloud.com)
|
||||
*
|
||||
* 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 java.util.*;
|
||||
|
||||
import static java.lang.String.format;
|
||||
|
||||
public class Facts implements Iterable<Map.Entry<String, Object>> {
|
||||
|
||||
private Map<String, Object> facts = new HashMap<>();
|
||||
|
||||
public void add(String name, Object fact) {
|
||||
facts.put(name, fact);
|
||||
}
|
||||
|
||||
public void remove(String name) {
|
||||
facts.remove(name);
|
||||
}
|
||||
|
||||
public Object get(String name) {
|
||||
return facts.get(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<Map.Entry<String, Object>> iterator() {
|
||||
return facts.entrySet().iterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder stringBuilder = new StringBuilder("Facts {").append("\n");
|
||||
for (Map.Entry<String, Object> fact : facts.entrySet()) {
|
||||
stringBuilder.append(format(" Fact { %s : %s }", fact.getKey(), fact.getValue().toString()));
|
||||
stringBuilder.append("\n");
|
||||
}
|
||||
stringBuilder.append("}");
|
||||
return stringBuilder.toString();
|
||||
}
|
||||
}
|
@ -0,0 +1,80 @@
|
||||
/**
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2017, Mahmoud Ben Hassine (mahmoud.benhassine@icloud.com)
|
||||
*
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* Abstraction for a rule that can be fired by the rules engine.
|
||||
*
|
||||
* Rules are registered in the rules engine registry and must have a <strong>unique</strong> name.
|
||||
*
|
||||
* @author Mahmoud Ben Hassine (mahmoud.benhassine@icloud.com)
|
||||
*/
|
||||
public interface Rule extends Comparable<Rule> {
|
||||
|
||||
/**
|
||||
* Default rule name.
|
||||
*/
|
||||
String DEFAULT_NAME = "rule";
|
||||
|
||||
/**
|
||||
* Default rule description.
|
||||
*/
|
||||
String DEFAULT_DESCRIPTION = "description";
|
||||
|
||||
/**
|
||||
* Default rule priority.
|
||||
*/
|
||||
int DEFAULT_PRIORITY = Integer.MAX_VALUE - 1;
|
||||
|
||||
/**
|
||||
* Getter for rule name.
|
||||
* @return the rule name
|
||||
*/
|
||||
String getName();
|
||||
|
||||
/**
|
||||
* Getter for rule description.
|
||||
* @return rule description
|
||||
*/
|
||||
String getDescription();
|
||||
|
||||
/**
|
||||
* Getter for rule priority.
|
||||
* @return rule priority
|
||||
*/
|
||||
int getPriority();
|
||||
|
||||
/**
|
||||
* Rule conditions abstraction : this method encapsulates the rule's conditions.
|
||||
* @return true if the rule should be applied given the provided facts, false else
|
||||
*/
|
||||
boolean evaluate(Facts facts);
|
||||
|
||||
/**
|
||||
* Rule actions abstraction : this method encapsulates the rule's actions.
|
||||
* @throws Exception thrown if an exception occurs during actions performing
|
||||
*/
|
||||
void execute(Facts facts) throws Exception;
|
||||
|
||||
}
|
@ -0,0 +1,75 @@
|
||||
/**
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2017, Mahmoud Ben Hassine (mahmoud.benhassine@icloud.com)
|
||||
*
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* A listener for rules execution events.
|
||||
*
|
||||
* @author Mahmoud Ben Hassine (mahmoud.benhassine@icloud.com)
|
||||
*/
|
||||
public interface RuleListener {
|
||||
|
||||
/**
|
||||
* Triggered before the evaluation of a rule.
|
||||
*
|
||||
* @param rule being evaluated
|
||||
* @param facts known before evaluating the rule
|
||||
* @return true if the rule should be evaluated, false otherwise
|
||||
*/
|
||||
boolean beforeEvaluate(Rule rule, Facts facts);
|
||||
|
||||
/**
|
||||
* Triggered after the evaluation of a rule.
|
||||
*
|
||||
* @param rule that has been evaluated
|
||||
* @param evaluationResult true if the rule evaluated to true, false otherwise
|
||||
*/
|
||||
void afterEvaluate(Rule rule, boolean evaluationResult);
|
||||
|
||||
/**
|
||||
* Triggered before the execution of a rule.
|
||||
*
|
||||
* @param rule the current rule
|
||||
* @param rule known before executing the rule
|
||||
*/
|
||||
void beforeExecute(Rule rule, Facts facts);
|
||||
|
||||
/**
|
||||
* Triggered after a rule has been executed successfully.
|
||||
*
|
||||
* @param rule the current rule
|
||||
* @param facts known after executing the rule
|
||||
*/
|
||||
void onSuccess(Rule rule, Facts facts);
|
||||
|
||||
/**
|
||||
* Triggered after a rule has failed.
|
||||
*
|
||||
* @param rule the current rule
|
||||
* @param exception the exception thrown when attempting to execute the rule
|
||||
* @param facts known after executing the rule
|
||||
*/
|
||||
void onFailure(Rule rule, Exception exception, Facts facts);
|
||||
|
||||
}
|
@ -0,0 +1,75 @@
|
||||
/**
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2017, Mahmoud Ben Hassine (mahmoud.benhassine@icloud.com)
|
||||
*
|
||||
* 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.RuleProxy;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
|
||||
public class Rules implements Iterable<Rule> {
|
||||
|
||||
private Set<Rule> rules = new TreeSet<>();
|
||||
|
||||
public Rules(Set<Rule> rules) {
|
||||
this.rules = rules;
|
||||
}
|
||||
|
||||
public Rules(Rule... rules ) {
|
||||
Collections.addAll(this.rules, rules);
|
||||
}
|
||||
|
||||
public Rules(Object... rules ) {
|
||||
for (Object rule : rules) {
|
||||
this.register(RuleProxy.asRule(rule));
|
||||
}
|
||||
}
|
||||
|
||||
public void register(Object rule) {
|
||||
rules.add(RuleProxy.asRule(rule));
|
||||
}
|
||||
|
||||
public void unregister(Object rule) {
|
||||
rules.remove(RuleProxy.asRule(rule));
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return rules.isEmpty();
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
rules.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<Rule> iterator() {
|
||||
return rules.iterator();
|
||||
}
|
||||
|
||||
public void sort() {
|
||||
rules = new TreeSet<>(rules);
|
||||
}
|
||||
}
|
@ -0,0 +1,72 @@
|
||||
/**
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2017, Mahmoud Ben Hassine (mahmoud.benhassine@icloud.com)
|
||||
*
|
||||
* 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 java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.jeasy.rules.core.RulesEngineParameters;
|
||||
|
||||
/**
|
||||
* Rules engine interface.
|
||||
*
|
||||
* @author Mahmoud Ben Hassine (mahmoud.benhassine@icloud.com)
|
||||
*/
|
||||
public interface RulesEngine {
|
||||
|
||||
/**
|
||||
* Default engine name.
|
||||
*/
|
||||
String DEFAULT_NAME = "engine";
|
||||
|
||||
/**
|
||||
* Default rule priority threshold.
|
||||
*/
|
||||
int DEFAULT_RULE_PRIORITY_THRESHOLD = Integer.MAX_VALUE;
|
||||
|
||||
/**
|
||||
* Return the rules engine parameters.
|
||||
*
|
||||
* @return The rules engine parameters
|
||||
*/
|
||||
RulesEngineParameters getParameters();
|
||||
|
||||
/**
|
||||
* Return the list of registered rule listeners.
|
||||
*
|
||||
* @return the list of registered rule listeners
|
||||
*/
|
||||
List<RuleListener> getRuleListeners();
|
||||
|
||||
/**
|
||||
* Fire all registered rules on given facts.
|
||||
*/
|
||||
void fire(Rules rules, Facts facts);
|
||||
|
||||
/**
|
||||
* Check rules without firing them.
|
||||
* @return a map with the result of evaluation of each rule
|
||||
*/
|
||||
Map<Rule, Boolean> check(Rules rules, Facts facts);
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
/**
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2017, Mahmoud Ben Hassine (mahmoud.benhassine@icloud.com)
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
/**
|
||||
* This package contains Easy Rules public API.
|
||||
*/
|
||||
package org.jeasy.rules.api;
|
@ -0,0 +1,83 @@
|
||||
/**
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2017, Mahmoud Ben Hassine (mahmoud.benhassine@icloud.com)
|
||||
*
|
||||
* 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.core;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
/**
|
||||
* Utility class that associates an action method and its execution order.
|
||||
*
|
||||
* @author Mahmoud Ben Hassine (mahmoud.benhassine@icloud.com)
|
||||
*/
|
||||
class ActionMethodOrderBean implements Comparable<ActionMethodOrderBean> {
|
||||
|
||||
private Method method;
|
||||
|
||||
private int order;
|
||||
|
||||
ActionMethodOrderBean(final Method method, final int order) {
|
||||
this.method = method;
|
||||
this.order = order;
|
||||
}
|
||||
|
||||
public int getOrder() {
|
||||
return order;
|
||||
}
|
||||
|
||||
public Method getMethod() {
|
||||
return method;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(final ActionMethodOrderBean actionMethodOrderBean) {
|
||||
if (order < actionMethodOrderBean.getOrder()) {
|
||||
return -1;
|
||||
} else if (order > actionMethodOrderBean.getOrder()) {
|
||||
return 1;
|
||||
} else {
|
||||
return method.equals(actionMethodOrderBean.getMethod()) ? 0 : 1;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object o) {
|
||||
if (this == o) return true;
|
||||
if (!(o instanceof ActionMethodOrderBean)) return false;
|
||||
|
||||
ActionMethodOrderBean that = (ActionMethodOrderBean) o;
|
||||
|
||||
if (order != that.order) return false;
|
||||
if (!method.equals(that.method)) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = method.hashCode();
|
||||
result = 31 * result + order;
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,172 @@
|
||||
/**
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2017, Mahmoud Ben Hassine (mahmoud.benhassine@icloud.com)
|
||||
*
|
||||
* 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.core;
|
||||
|
||||
import org.jeasy.rules.api.Facts;
|
||||
import org.jeasy.rules.api.Rule;
|
||||
|
||||
/**
|
||||
* Basic rule implementation class that provides common methods.
|
||||
*
|
||||
* You can extend this class and override {@link BasicRule#evaluate(Facts)} and {@link BasicRule#execute(Facts)} to provide rule
|
||||
* conditions and actions logic.
|
||||
*
|
||||
* @author Mahmoud Ben Hassine (mahmoud.benhassine@icloud.com)
|
||||
*/
|
||||
public class BasicRule implements Rule, Comparable<Rule> {
|
||||
|
||||
/**
|
||||
* Rule name.
|
||||
*/
|
||||
protected String name;
|
||||
|
||||
/**
|
||||
* Rule description.
|
||||
*/
|
||||
protected String description;
|
||||
|
||||
/**
|
||||
* Rule priority.
|
||||
*/
|
||||
protected int priority;
|
||||
|
||||
/**
|
||||
* Create a new {@link BasicRule}.
|
||||
*/
|
||||
public BasicRule() {
|
||||
this(Rule.DEFAULT_NAME, Rule.DEFAULT_DESCRIPTION, Rule.DEFAULT_PRIORITY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link BasicRule}.
|
||||
*
|
||||
* @param name rule name
|
||||
*/
|
||||
public BasicRule(final String name) {
|
||||
this(name, Rule.DEFAULT_DESCRIPTION, Rule.DEFAULT_PRIORITY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link BasicRule}.
|
||||
*
|
||||
* @param name rule name
|
||||
* @param description rule description
|
||||
*/
|
||||
public BasicRule(final String name, final String description) {
|
||||
this(name, description, Rule.DEFAULT_PRIORITY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link BasicRule}.
|
||||
*
|
||||
* @param name rule name
|
||||
* @param description rule description
|
||||
* @param priority rule priority
|
||||
*/
|
||||
public BasicRule(final String name, final String description, final int priority) {
|
||||
this.name = name;
|
||||
this.description = description;
|
||||
this.priority = priority;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public boolean evaluate(Facts facts) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public void execute(Facts facts) throws Exception {
|
||||
// no op
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public void setDescription(final String description) {
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public int getPriority() {
|
||||
return priority;
|
||||
}
|
||||
|
||||
public void setPriority(final int priority) {
|
||||
this.priority = priority;
|
||||
}
|
||||
|
||||
/*
|
||||
* Rules are unique according to their names within a rules engine registry.
|
||||
*/
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object o) {
|
||||
if (this == o)
|
||||
return true;
|
||||
if (o == null || getClass() != o.getClass())
|
||||
return false;
|
||||
|
||||
BasicRule basicRule = (BasicRule) o;
|
||||
|
||||
if (priority != basicRule.priority)
|
||||
return false;
|
||||
if (!name.equals(basicRule.name))
|
||||
return false;
|
||||
return !(description != null ? !description.equals(basicRule.description) : basicRule.description != null);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = name.hashCode();
|
||||
result = 31 * result + (description != null ? description.hashCode() : 0);
|
||||
result = 31 * result + priority;
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(final Rule rule) {
|
||||
if (getPriority() < rule.getPriority()) {
|
||||
return -1;
|
||||
} else if (getPriority() > rule.getPriority()) {
|
||||
return 1;
|
||||
} else {
|
||||
return getName().compareTo(rule.getName());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,141 @@
|
||||
/**
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2017, Mahmoud Ben Hassine (mahmoud.benhassine@icloud.com)
|
||||
*
|
||||
* 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.core;
|
||||
|
||||
import org.jeasy.rules.api.Facts;
|
||||
import org.jeasy.rules.api.Rule;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
|
||||
/**
|
||||
* Class representing a composite rule composed of a set of rules.
|
||||
*
|
||||
* A composite rule is triggered if <strong>ALL</strong> conditions of its composing rules are satisfied.
|
||||
* When a composite rule is applied, actions of <strong>ALL</strong> composing rules are performed.
|
||||
*
|
||||
* @author Mahmoud Ben Hassine (mahmoud.benhassine@icloud.com)
|
||||
*/
|
||||
public class CompositeRule extends BasicRule {
|
||||
|
||||
/**
|
||||
* The set of composing rules.
|
||||
*/
|
||||
protected Set<Rule> rules;
|
||||
|
||||
protected Map<Object, Rule> proxyRules;
|
||||
|
||||
/**
|
||||
* Create a new {@link CompositeRule}.
|
||||
*/
|
||||
public CompositeRule() {
|
||||
this(Rule.DEFAULT_NAME, Rule.DEFAULT_DESCRIPTION, Rule.DEFAULT_PRIORITY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link CompositeRule}.
|
||||
*
|
||||
* @param name rule name
|
||||
*/
|
||||
public CompositeRule(final String name) {
|
||||
this(name, Rule.DEFAULT_DESCRIPTION, Rule.DEFAULT_PRIORITY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link CompositeRule}.
|
||||
*
|
||||
* @param name rule name
|
||||
* @param description rule description
|
||||
*/
|
||||
public CompositeRule(final String name, final String description) {
|
||||
this(name, description, Rule.DEFAULT_PRIORITY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link CompositeRule}.
|
||||
*
|
||||
* @param name rule name
|
||||
* @param description rule description
|
||||
* @param priority rule priority
|
||||
*/
|
||||
public CompositeRule(final String name, final String description, final int priority) {
|
||||
super(name, description, priority);
|
||||
rules = new TreeSet<>();
|
||||
proxyRules = new HashMap<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* A composite rule is triggered if <strong>ALL</strong> conditions of all composing rules are evaluated to true.
|
||||
* @return true if <strong>ALL</strong> conditions of composing rules are evaluated to true
|
||||
*/
|
||||
@Override
|
||||
public boolean evaluate(Facts facts) {
|
||||
if (!rules.isEmpty()) {
|
||||
for (Rule rule : rules) {
|
||||
if (!rule.evaluate(facts)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* When a composite rule is applied, <strong>ALL</strong> actions of composing rules are performed
|
||||
* in their natural order.
|
||||
*
|
||||
* @throws Exception thrown if an exception occurs during actions performing
|
||||
*/
|
||||
@Override
|
||||
public void execute(Facts facts) throws Exception {
|
||||
for (Rule rule : rules) {
|
||||
rule.execute(facts);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a rule to the composite rule.
|
||||
* @param rule the rule to add
|
||||
*/
|
||||
public void addRule(final Object rule) {
|
||||
Rule proxy = RuleProxy.asRule(rule);
|
||||
rules.add(proxy);
|
||||
proxyRules.put(rule, proxy);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a rule from the composite rule.
|
||||
* @param rule the rule to remove
|
||||
*/
|
||||
public void removeRule(final Object rule) {
|
||||
Rule proxy = proxyRules.get(rule);
|
||||
if (proxy != null) {
|
||||
rules.remove(proxy);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,220 @@
|
||||
/**
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2017, Mahmoud Ben Hassine (mahmoud.benhassine@icloud.com)
|
||||
*
|
||||
* 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.core;
|
||||
|
||||
import org.jeasy.rules.api.*;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import static java.lang.String.format;
|
||||
|
||||
/**
|
||||
* Default {@link RulesEngine} implementation.
|
||||
*
|
||||
* This implementation handles a set of rules with unique name.
|
||||
*
|
||||
* Rules are fired according to their natural order which is priority by default.
|
||||
*
|
||||
* @author Mahmoud Ben Hassine (mahmoud.benhassine@icloud.com)
|
||||
*/
|
||||
public final class DefaultRulesEngine implements RulesEngine {
|
||||
|
||||
private static final Logger LOGGER = Logger.getLogger(RulesEngine.class.getName());
|
||||
|
||||
/**
|
||||
* The engine parameters
|
||||
*/
|
||||
private RulesEngineParameters parameters;
|
||||
|
||||
/**
|
||||
* The registered rule listeners.
|
||||
*/
|
||||
private List<RuleListener> ruleListeners;
|
||||
|
||||
public DefaultRulesEngine() {
|
||||
this.parameters = new RulesEngineParameters();
|
||||
this.ruleListeners = new ArrayList<>();
|
||||
}
|
||||
|
||||
DefaultRulesEngine(final RulesEngineParameters parameters, final List<RuleListener> ruleListeners) {
|
||||
this.parameters = parameters;
|
||||
this.ruleListeners = ruleListeners;
|
||||
if (parameters.isSilentMode()) {
|
||||
Utils.muteLoggers();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public RulesEngineParameters getParameters() {
|
||||
return parameters;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<RuleListener> getRuleListeners() {
|
||||
return ruleListeners;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fire(Rules rules, Facts facts) {
|
||||
if (rules.isEmpty()) {
|
||||
LOGGER.warning("No rules registered! Nothing to apply");
|
||||
return;
|
||||
}
|
||||
sort(rules);
|
||||
logEngineParameters();
|
||||
log(rules, facts);
|
||||
apply(rules, facts);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<Rule, Boolean> check(Rules rules, Facts facts) {
|
||||
LOGGER.info("Checking rules");
|
||||
sort(rules);
|
||||
Map<Rule, Boolean> result = new HashMap<>();
|
||||
for (Rule rule : rules) {
|
||||
if (shouldBeEvaluated(rule, facts)) {
|
||||
result.put(rule, rule.evaluate(facts));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private void sort(Rules rules) {
|
||||
rules.sort();
|
||||
}
|
||||
|
||||
private void apply(Rules rules, Facts facts) {
|
||||
|
||||
LOGGER.info("Rules evaluation started");
|
||||
for (Rule rule : rules) {
|
||||
|
||||
final String name = rule.getName();
|
||||
final int priority = rule.getPriority();
|
||||
|
||||
if (priority > parameters.getPriorityThreshold()) {
|
||||
LOGGER.log(Level.INFO,
|
||||
"Rule priority threshold ({0}) exceeded at rule ''{1}'' with priority={2}, next rules will be skipped",
|
||||
new Object[]{parameters.getPriorityThreshold(), name, priority});
|
||||
break;
|
||||
}
|
||||
|
||||
if (!shouldBeEvaluated(rule, facts)) {
|
||||
LOGGER.log(Level.INFO, "Rule ''{0}'' has been skipped before being evaluated", name);
|
||||
continue;
|
||||
}
|
||||
if (rule.evaluate(facts)) {
|
||||
LOGGER.log(Level.INFO, "Rule ''{0}'' triggered", name);
|
||||
triggerListenersAfterEvaluate(rule, true);
|
||||
try {
|
||||
triggerListenersBeforeExecute(rule, facts);
|
||||
rule.execute(facts);
|
||||
LOGGER.log(Level.INFO, "Rule ''{0}'' performed successfully", name);
|
||||
triggerListenersOnSuccess(rule, facts);
|
||||
|
||||
if (parameters.isSkipOnFirstAppliedRule()) {
|
||||
LOGGER.info("Next rules will be skipped since parameter skipOnFirstAppliedRule is set");
|
||||
break;
|
||||
}
|
||||
} catch (Exception exception) {
|
||||
LOGGER.log(Level.SEVERE, String.format("Rule '%s' performed with error", name), exception);
|
||||
triggerListenersOnFailure(rule, exception, facts);
|
||||
if (parameters.isSkipOnFirstFailedRule()) {
|
||||
LOGGER.info("Next rules will be skipped since parameter skipOnFirstFailedRule is set");
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
LOGGER.log(Level.INFO, "Rule ''{0}'' has been evaluated to false, it has not been executed", name);
|
||||
triggerListenersAfterEvaluate(rule, false);
|
||||
if (parameters.isSkipOnFirstNonTriggeredRule()) {
|
||||
LOGGER.info("Next rules will be skipped since parameter skipOnFirstNonTriggeredRule is set");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void triggerListenersOnFailure(final Rule rule, final Exception exception, Facts facts) {
|
||||
for (RuleListener ruleListener : ruleListeners) {
|
||||
ruleListener.onFailure(rule, exception, facts);
|
||||
}
|
||||
}
|
||||
|
||||
private void triggerListenersOnSuccess(final Rule rule, Facts facts) {
|
||||
for (RuleListener ruleListener : ruleListeners) {
|
||||
ruleListener.onSuccess(rule, facts);
|
||||
}
|
||||
}
|
||||
|
||||
private void triggerListenersBeforeExecute(final Rule rule, Facts facts) {
|
||||
for (RuleListener ruleListener : ruleListeners) {
|
||||
ruleListener.beforeExecute(rule, facts);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean triggerListenersBeforeEvaluate(Rule rule, Facts facts) {
|
||||
for (RuleListener ruleListener : ruleListeners) {
|
||||
if (!ruleListener.beforeEvaluate(rule, facts)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private void triggerListenersAfterEvaluate(Rule rule, boolean evaluationResult) {
|
||||
for (RuleListener ruleListener : ruleListeners) {
|
||||
ruleListener.afterEvaluate(rule, evaluationResult);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean shouldBeEvaluated(Rule rule, Facts facts) {
|
||||
return triggerListenersBeforeEvaluate(rule, facts);
|
||||
}
|
||||
|
||||
private void logEngineParameters() {
|
||||
LOGGER.log(Level.INFO, "Engine name: {0}", parameters.getName());
|
||||
LOGGER.log(Level.INFO, "Rule priority threshold: {0}", parameters.getPriorityThreshold());
|
||||
LOGGER.log(Level.INFO, "Skip on first applied rule: {0}", parameters.isSkipOnFirstAppliedRule());
|
||||
LOGGER.log(Level.INFO, "Skip on first non triggered rule: {0}", parameters.isSkipOnFirstNonTriggeredRule());
|
||||
LOGGER.log(Level.INFO, "Skip on first failed rule: {0}", parameters.isSkipOnFirstFailedRule());
|
||||
}
|
||||
|
||||
private void log(Rules rules, Facts facts) {
|
||||
LOGGER.log(Level.INFO, "Registered rules:");
|
||||
for (Rule rule : rules) {
|
||||
LOGGER.log(Level.INFO, format("Rule { name = '%s', description = '%s', priority = '%s'}",
|
||||
rule.getName(), rule.getDescription(), rule.getPriority()));
|
||||
}
|
||||
|
||||
LOGGER.log(Level.INFO, "Known facts:");
|
||||
for (Map.Entry<String, Object> fact : facts) {
|
||||
LOGGER.log(Level.INFO, format("Fact { %s : %s }", fact.getKey(), fact.getValue().toString()));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,161 @@
|
||||
/**
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2017, Mahmoud Ben Hassine (mahmoud.benhassine@icloud.com)
|
||||
*
|
||||
* 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.core;
|
||||
|
||||
import org.jeasy.rules.annotation.*;
|
||||
import org.jeasy.rules.api.Facts;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.lang.reflect.Parameter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static java.lang.String.format;
|
||||
|
||||
/**
|
||||
* Validate that an annotated rule object is well defined.
|
||||
*
|
||||
* @author Mahmoud Ben Hassine (mahmoud.benhassine@icloud.com)
|
||||
*/
|
||||
class RuleDefinitionValidator {
|
||||
|
||||
void validateRuleDefinition(final Object rule) {
|
||||
checkRuleClass(rule);
|
||||
checkConditionMethod(rule);
|
||||
checkActionMethods(rule);
|
||||
checkPriorityMethod(rule);
|
||||
}
|
||||
|
||||
private void checkRuleClass(final Object rule) {
|
||||
if (!isRuleClassWellDefined(rule)) {
|
||||
throw new IllegalArgumentException(format("Rule '%s' is not annotated with '%s'", rule.getClass().getName(), Rule.class.getName()));
|
||||
}
|
||||
}
|
||||
|
||||
private void checkConditionMethod(final Object rule) {
|
||||
List<Method> conditionMethods = getMethodsAnnotatedWith(Condition.class, rule);
|
||||
if (conditionMethods.isEmpty()) {
|
||||
throw new IllegalArgumentException(format("Rule '%s' must have a public method annotated with '%s'", rule.getClass().getName(), Condition.class.getName()));
|
||||
}
|
||||
|
||||
if (conditionMethods.size() > 1) {
|
||||
throw new IllegalArgumentException(format("Rule '%s' must have exactly one method annotated with '%s'", rule.getClass().getName(), Condition.class.getName()));
|
||||
}
|
||||
|
||||
Method conditionMethod = conditionMethods.get(0);
|
||||
|
||||
if (!isConditionMethodWellDefined(conditionMethod)) {
|
||||
throw new IllegalArgumentException(format("Condition method '%s' defined in rule '%s' must be public, may have parameters annotated with @Fact (and/or a parameter of type Facts) and return boolean type.", conditionMethod, rule.getClass().getName()));
|
||||
}
|
||||
}
|
||||
|
||||
private void checkActionMethods(final Object rule) {
|
||||
List<Method> actionMethods = getMethodsAnnotatedWith(Action.class, rule);
|
||||
if (actionMethods.isEmpty()) {
|
||||
throw new IllegalArgumentException(format("Rule '%s' must have at least one public method annotated with '%s'", rule.getClass().getName(), Action.class.getName()));
|
||||
}
|
||||
|
||||
for (Method actionMethod : actionMethods) {
|
||||
if (!isActionMethodWellDefined(actionMethod)) {
|
||||
throw new IllegalArgumentException(format("Action method '%s' defined in rule '%s' must be public and may have parameters annotated with @Fact (and/or a parameter of type Facts).", actionMethod, rule.getClass().getName()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void checkPriorityMethod(final Object rule) {
|
||||
|
||||
List<Method> priorityMethods = getMethodsAnnotatedWith(Priority.class, rule);
|
||||
|
||||
if (priorityMethods.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (priorityMethods.size() > 1) {
|
||||
throw new IllegalArgumentException(format("Rule '%s' must have exactly one method annotated with '%s'", rule.getClass().getName(), Priority.class.getName()));
|
||||
}
|
||||
|
||||
Method priorityMethod = priorityMethods.get(0);
|
||||
|
||||
if (!isPriorityMethodWellDefined(priorityMethod)) {
|
||||
throw new IllegalArgumentException(format("Priority method '%s' defined in rule '%s' must be public, have no parameters and return integer type.", priorityMethod, rule.getClass().getName()));
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isRuleClassWellDefined(final Object rule) {
|
||||
return Utils.isAnnotationPresent(Rule.class, rule.getClass());
|
||||
}
|
||||
|
||||
private boolean isConditionMethodWellDefined(final Method method) {
|
||||
Parameter[] parameters = method.getParameters();
|
||||
return Modifier.isPublic(method.getModifiers())
|
||||
&& method.getReturnType().equals(Boolean.TYPE)
|
||||
&& validParameters(parameters);
|
||||
}
|
||||
|
||||
private boolean validParameters(Parameter[] parameters) {
|
||||
List<Parameter> notAnnotatedParams = new ArrayList<>();
|
||||
for (Parameter parameter : parameters) {
|
||||
if (parameter.getAnnotation(Fact.class) == null) {
|
||||
notAnnotatedParams.add(parameter);
|
||||
}
|
||||
}
|
||||
if (notAnnotatedParams.size() > 1) {
|
||||
return false;
|
||||
} else if (notAnnotatedParams.size() == 1) {
|
||||
Parameter parameter = notAnnotatedParams.get(0);
|
||||
return parameter.getType().isAssignableFrom(Facts.class);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean isActionMethodWellDefined(final Method method) {
|
||||
Parameter[] parameters = method.getParameters();
|
||||
return Modifier.isPublic(method.getModifiers())
|
||||
&& validParameters(parameters);
|
||||
}
|
||||
|
||||
private boolean isPriorityMethodWellDefined(final Method method) {
|
||||
return Modifier.isPublic(method.getModifiers())
|
||||
&& method.getReturnType().equals(Integer.TYPE)
|
||||
&& method.getParameterTypes().length == 0;
|
||||
}
|
||||
|
||||
private List<Method> getMethodsAnnotatedWith(final Class<? extends Annotation> annotation, final Object rule) {
|
||||
Method[] methods = getMethods(rule);
|
||||
List<Method> annotatedMethods = new ArrayList<>();
|
||||
for (Method method : methods) {
|
||||
if (method.isAnnotationPresent(annotation)) {
|
||||
annotatedMethods.add(method);
|
||||
}
|
||||
}
|
||||
return annotatedMethods;
|
||||
}
|
||||
|
||||
private Method[] getMethods(final Object rule) {
|
||||
return rule.getClass().getMethods();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,245 @@
|
||||
/**
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2017, Mahmoud Ben Hassine (mahmoud.benhassine@icloud.com)
|
||||
*
|
||||
* 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.core;
|
||||
|
||||
import org.jeasy.rules.annotation.Fact;
|
||||
import org.jeasy.rules.api.Facts;
|
||||
import org.jeasy.rules.annotation.Action;
|
||||
import org.jeasy.rules.annotation.Condition;
|
||||
import org.jeasy.rules.annotation.Priority;
|
||||
import org.jeasy.rules.api.Rule;
|
||||
|
||||
import java.lang.reflect.InvocationHandler;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Parameter;
|
||||
import java.lang.reflect.Proxy;
|
||||
import java.util.*;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
public class RuleProxy implements InvocationHandler {
|
||||
|
||||
private static final Logger LOGGER = Logger.getLogger(RuleProxy.class.getName());
|
||||
|
||||
private Object target;
|
||||
|
||||
private static RuleDefinitionValidator ruleDefinitionValidator = new RuleDefinitionValidator();
|
||||
|
||||
private RuleProxy(final Object target) {
|
||||
this.target = target;
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes the rule object implement the {@link Rule} interface.
|
||||
*
|
||||
* @param rule the annotated rule object.
|
||||
* @return a proxy that implements the {@link Rule} interface.
|
||||
*/
|
||||
public static Rule asRule(final Object rule) {
|
||||
Rule result;
|
||||
if (Utils.getInterfaces(rule).contains(Rule.class)) {
|
||||
result = (Rule) rule;
|
||||
} else {
|
||||
ruleDefinitionValidator.validateRuleDefinition(rule);
|
||||
result = (Rule) Proxy.newProxyInstance(
|
||||
Rule.class.getClassLoader(),
|
||||
new Class[]{Rule.class, Comparable.class},
|
||||
new RuleProxy(rule));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable {
|
||||
String methodName = method.getName();
|
||||
if (methodName.equals("getName")) {
|
||||
return getRuleName();
|
||||
}
|
||||
if (methodName.equals("getDescription")) {
|
||||
return getRuleDescription();
|
||||
}
|
||||
if (methodName.equals("getPriority")) {
|
||||
return getRulePriority();
|
||||
}
|
||||
if (methodName.equals("evaluate")) {
|
||||
Facts facts = (Facts) args[0];
|
||||
Method conditionMethod = getConditionMethod();
|
||||
List<Object> actualParameters = getActualParameters(conditionMethod, facts);
|
||||
return conditionMethod.invoke(target, actualParameters.toArray()); // validated upfront
|
||||
}
|
||||
if (methodName.equals("execute")) {
|
||||
for (ActionMethodOrderBean actionMethodBean : getActionMethodBeans()) {
|
||||
Facts facts = (Facts) args[0];
|
||||
Method actiomMethod = actionMethodBean.getMethod();
|
||||
List<Object> actualParameters = getActualParameters(actiomMethod, facts);
|
||||
actiomMethod.invoke(target, actualParameters.toArray());
|
||||
}
|
||||
}
|
||||
if (methodName.equals("equals")) {
|
||||
return target.equals(args[0]);
|
||||
}
|
||||
if (methodName.equals("hashCode")) {
|
||||
return target.hashCode();
|
||||
}
|
||||
if (methodName.equals("toString")) {
|
||||
return target.toString();
|
||||
}
|
||||
if (methodName.equals("compareTo")) {
|
||||
Method compareToMethod = getCompareToMethod();
|
||||
if (compareToMethod != null) {
|
||||
return compareToMethod.invoke(target, args);
|
||||
} else {
|
||||
Rule otherRule = (Rule) args[0];
|
||||
return compareTo(otherRule);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private List<Object> getActualParameters(Method method, Facts facts) {
|
||||
Parameter[] parameters = method.getParameters(); // validated upfront
|
||||
List<Object> actualParameters = new ArrayList<>();
|
||||
for (Parameter parameter : parameters) {
|
||||
Fact annotation = parameter.getAnnotation(Fact.class); // validated upfront
|
||||
if (annotation == null) { // validated upfront, there may be only one parameter not annotated and which is of type Facts.class
|
||||
actualParameters.add(facts);
|
||||
} else {
|
||||
String factName = annotation.value();
|
||||
Object fact = facts.get(factName);
|
||||
if (fact == null) {
|
||||
throw new RuntimeException(String.format("No fact named %s found in known facts", factName));
|
||||
}
|
||||
actualParameters.add(fact);
|
||||
}
|
||||
}
|
||||
return actualParameters;
|
||||
}
|
||||
|
||||
private int compareTo(final Rule otherRule) throws Exception {
|
||||
String otherName = otherRule.getName();
|
||||
int otherPriority = otherRule.getPriority();
|
||||
String name = getRuleName();
|
||||
int priority = getRulePriority();
|
||||
|
||||
if (priority < otherPriority) {
|
||||
return -1;
|
||||
} else if (priority > otherPriority) {
|
||||
return 1;
|
||||
} else {
|
||||
return name.compareTo(otherName);
|
||||
}
|
||||
}
|
||||
|
||||
private int getRulePriority() throws Exception {
|
||||
int priority = Rule.DEFAULT_PRIORITY;
|
||||
|
||||
Method[] methods = getMethods();
|
||||
for (Method method : methods) {
|
||||
if (method.isAnnotationPresent(Priority.class)) {
|
||||
priority = (Integer) method.invoke(target);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return priority;
|
||||
}
|
||||
|
||||
private Method getConditionMethod() {
|
||||
Method[] methods = getMethods();
|
||||
for (Method method : methods) {
|
||||
if (method.isAnnotationPresent(Condition.class)) {
|
||||
return method;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private Set<ActionMethodOrderBean> getActionMethodBeans() {
|
||||
Method[] methods = getMethods();
|
||||
Set<ActionMethodOrderBean> actionMethodBeans = new TreeSet<>();
|
||||
for (Method method : methods) {
|
||||
if (method.isAnnotationPresent(Action.class)) {
|
||||
Action actionAnnotation = method.getAnnotation(Action.class);
|
||||
int order = actionAnnotation.order();
|
||||
actionMethodBeans.add(new ActionMethodOrderBean(method, order));
|
||||
}
|
||||
}
|
||||
return actionMethodBeans;
|
||||
}
|
||||
|
||||
private Method getCompareToMethod() {
|
||||
Method[] methods = getMethods();
|
||||
for (Method method : methods) {
|
||||
if (method.getName().equals("compareTo")) {
|
||||
return method;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private Method[] getMethods() {
|
||||
return getTargetClass().getMethods();
|
||||
}
|
||||
|
||||
private org.jeasy.rules.annotation.Rule getRuleAnnotation() {
|
||||
return Utils.findAnnotation(org.jeasy.rules.annotation.Rule.class, getTargetClass());
|
||||
}
|
||||
|
||||
private String getRuleName() {
|
||||
org.jeasy.rules.annotation.Rule rule = getRuleAnnotation();
|
||||
return rule.name().equals(Rule.DEFAULT_NAME) ? getTargetClass().getSimpleName() : rule.name();
|
||||
}
|
||||
|
||||
private String getRuleDescription() {
|
||||
// Default description = "when " + conditionMethodName + " then " + comma separated actionMethodsNames
|
||||
StringBuilder description = new StringBuilder();
|
||||
appendConditionMethodName(description);
|
||||
appendActionMethodsNames(description);
|
||||
|
||||
org.jeasy.rules.annotation.Rule rule = getRuleAnnotation();
|
||||
return rule.description().equals(Rule.DEFAULT_DESCRIPTION) ? description.toString() : rule.description();
|
||||
}
|
||||
|
||||
private void appendConditionMethodName(StringBuilder description) {
|
||||
Method method = getConditionMethod();
|
||||
if (method != null) {
|
||||
description.append("when ");
|
||||
description.append(method.getName());
|
||||
description.append(" then ");
|
||||
}
|
||||
}
|
||||
|
||||
private void appendActionMethodsNames(StringBuilder description) {
|
||||
Iterator<ActionMethodOrderBean> iterator = getActionMethodBeans().iterator();
|
||||
while (iterator.hasNext()) {
|
||||
description.append(iterator.next().getMethod().getName());
|
||||
if (iterator.hasNext()) {
|
||||
description.append(",");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Class<?> getTargetClass() {
|
||||
return target.getClass();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,91 @@
|
||||
/**
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2017, Mahmoud Ben Hassine (mahmoud.benhassine@icloud.com)
|
||||
*
|
||||
* 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.core;
|
||||
|
||||
import org.jeasy.rules.api.RuleListener;
|
||||
import org.jeasy.rules.api.RulesEngine;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Builder for rules engine instances.
|
||||
*
|
||||
* @author Mahmoud Ben Hassine (mahmoud.benhassine@icloud.com)
|
||||
*/
|
||||
public class RulesEngineBuilder {
|
||||
|
||||
private RulesEngineParameters parameters;
|
||||
|
||||
private List<RuleListener> ruleListeners;
|
||||
|
||||
public static RulesEngineBuilder aNewRulesEngine() {
|
||||
return new RulesEngineBuilder();
|
||||
}
|
||||
|
||||
private RulesEngineBuilder() {
|
||||
parameters = new RulesEngineParameters(RulesEngine.DEFAULT_NAME, false, false, RulesEngine.DEFAULT_RULE_PRIORITY_THRESHOLD, false);
|
||||
ruleListeners = new ArrayList<>();
|
||||
}
|
||||
|
||||
public RulesEngineBuilder named(final String name) {
|
||||
parameters.setName(name);
|
||||
return this;
|
||||
}
|
||||
|
||||
public RulesEngineBuilder withSkipOnFirstAppliedRule(final boolean skipOnFirstAppliedRule) {
|
||||
parameters.setSkipOnFirstAppliedRule(skipOnFirstAppliedRule);
|
||||
return this;
|
||||
}
|
||||
|
||||
public RulesEngineBuilder withSkipOnFirstNonTriggeredRule(final boolean skipOnFirstNonTriggeredRule) {
|
||||
parameters.setSkipOnFirstNonTriggeredRule(skipOnFirstNonTriggeredRule);
|
||||
return this;
|
||||
}
|
||||
|
||||
public RulesEngineBuilder withSkipOnFirstFailedRule(final boolean skipOnFirstFailedRule) {
|
||||
parameters.setSkipOnFirstFailedRule(skipOnFirstFailedRule);
|
||||
return this;
|
||||
}
|
||||
|
||||
public RulesEngineBuilder withRulePriorityThreshold(final int priorityThreshold) {
|
||||
parameters.setPriorityThreshold(priorityThreshold);
|
||||
return this;
|
||||
}
|
||||
|
||||
public RulesEngineBuilder withRuleListener(final RuleListener ruleListener) {
|
||||
this.ruleListeners.add(ruleListener);
|
||||
return this;
|
||||
}
|
||||
|
||||
public RulesEngineBuilder withSilentMode(final boolean silentMode) {
|
||||
parameters.setSilentMode(silentMode);
|
||||
return this;
|
||||
}
|
||||
|
||||
public RulesEngine build() {
|
||||
return new DefaultRulesEngine(parameters, ruleListeners);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,125 @@
|
||||
/**
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2017, Mahmoud Ben Hassine (mahmoud.benhassine@icloud.com)
|
||||
*
|
||||
* 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.core;
|
||||
|
||||
import org.jeasy.rules.api.RulesEngine;
|
||||
|
||||
/**
|
||||
* Parameters of the rules engine.
|
||||
*
|
||||
* @author Mahmoud Ben Hassine (mahmoud.benhassine@icloud.com)
|
||||
*/
|
||||
public class RulesEngineParameters {
|
||||
|
||||
/**
|
||||
* The engine name.
|
||||
*/
|
||||
protected String name;
|
||||
|
||||
/**
|
||||
* Parameter to skip next applicable rules when a rule is applied.
|
||||
*/
|
||||
private boolean skipOnFirstAppliedRule;
|
||||
|
||||
/**
|
||||
* Parameter to skip next applicable rules when a rule is non triggered
|
||||
*/
|
||||
private boolean skipOnFirstNonTriggeredRule;
|
||||
|
||||
/**
|
||||
* Parameter to skip next applicable rules when a rule has failed.
|
||||
*/
|
||||
private boolean skipOnFirstFailedRule;
|
||||
|
||||
/**
|
||||
* Parameter to skip next rules if priority exceeds a user defined threshold.
|
||||
*/
|
||||
private int priorityThreshold;
|
||||
|
||||
/**
|
||||
* Parameter to mute loggers.
|
||||
*/
|
||||
private boolean silentMode;
|
||||
|
||||
public RulesEngineParameters() {
|
||||
this.name = RulesEngine.DEFAULT_NAME;
|
||||
this.priorityThreshold = RulesEngine.DEFAULT_RULE_PRIORITY_THRESHOLD;
|
||||
}
|
||||
|
||||
public RulesEngineParameters(String name, boolean skipOnFirstAppliedRule, boolean skipOnFirstFailedRule, int priorityThreshold, boolean silentMode) {
|
||||
this.name = name;
|
||||
this.skipOnFirstAppliedRule = skipOnFirstAppliedRule;
|
||||
this.skipOnFirstFailedRule = skipOnFirstFailedRule;
|
||||
this.priorityThreshold = priorityThreshold;
|
||||
this.silentMode = silentMode;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public int getPriorityThreshold() {
|
||||
return priorityThreshold;
|
||||
}
|
||||
|
||||
public void setPriorityThreshold(int priorityThreshold) {
|
||||
this.priorityThreshold = priorityThreshold;
|
||||
}
|
||||
|
||||
public boolean isSilentMode() {
|
||||
return silentMode;
|
||||
}
|
||||
|
||||
public void setSilentMode(boolean silentMode) {
|
||||
this.silentMode = silentMode;
|
||||
}
|
||||
|
||||
public boolean isSkipOnFirstAppliedRule() {
|
||||
return skipOnFirstAppliedRule;
|
||||
}
|
||||
|
||||
public void setSkipOnFirstAppliedRule(boolean skipOnFirstAppliedRule) {
|
||||
this.skipOnFirstAppliedRule = skipOnFirstAppliedRule;
|
||||
}
|
||||
|
||||
public boolean isSkipOnFirstNonTriggeredRule() {
|
||||
return skipOnFirstNonTriggeredRule;
|
||||
}
|
||||
|
||||
public void setSkipOnFirstNonTriggeredRule(boolean skipOnFirstNonTriggeredRule) {
|
||||
this.skipOnFirstNonTriggeredRule = skipOnFirstNonTriggeredRule;
|
||||
}
|
||||
|
||||
public boolean isSkipOnFirstFailedRule() {
|
||||
return skipOnFirstFailedRule;
|
||||
}
|
||||
|
||||
public void setSkipOnFirstFailedRule(boolean skipOnFirstFailedRule) {
|
||||
this.skipOnFirstFailedRule = skipOnFirstFailedRule;
|
||||
}
|
||||
}
|
@ -0,0 +1,109 @@
|
||||
/**
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2017, Mahmoud Ben Hassine (mahmoud.benhassine@icloud.com)
|
||||
*
|
||||
* 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.core;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Enumeration;
|
||||
import java.util.List;
|
||||
import java.util.logging.Handler;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.LogManager;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import static java.util.Arrays.asList;
|
||||
|
||||
/**
|
||||
* Utilities class.
|
||||
*
|
||||
* @author Mahmoud Ben Hassine (mahmoud.benhassine@icloud.com)
|
||||
*/
|
||||
final class Utils {
|
||||
|
||||
private static final Logger LOGGER = Logger.getLogger(Utils.class.getName());
|
||||
|
||||
static {
|
||||
try {
|
||||
if (System.getProperty("java.util.logging.config.file") == null &&
|
||||
System.getProperty("java.util.logging.config.class") == null) {
|
||||
LogManager.getLogManager().readConfiguration(Utils.class.getResourceAsStream("/logging.properties"));
|
||||
}
|
||||
} catch (IOException e) {
|
||||
LOGGER.log(Level.WARNING, "Unable to load logging configuration file", e);
|
||||
}
|
||||
}
|
||||
|
||||
private Utils() {
|
||||
|
||||
}
|
||||
|
||||
static void muteLoggers() {
|
||||
Enumeration<String> loggerNames = LogManager.getLogManager().getLoggerNames();
|
||||
while (loggerNames.hasMoreElements()) {
|
||||
String loggerName = loggerNames.nextElement();
|
||||
if (loggerName.startsWith("org.easyrules")) {
|
||||
muteLogger(loggerName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void muteLogger(final String logger) {
|
||||
Logger.getLogger(logger).setUseParentHandlers(false);
|
||||
Handler[] handlers = Logger.getLogger(logger).getHandlers();
|
||||
for (Handler handler : handlers) {
|
||||
Logger.getLogger(logger).removeHandler(handler);
|
||||
}
|
||||
}
|
||||
|
||||
static List<Class<?>> getInterfaces(final Object rule) {
|
||||
List<Class<?>> interfaces = new ArrayList<>();
|
||||
Class<?> clazz = rule.getClass();
|
||||
while (clazz.getSuperclass() != null) {
|
||||
interfaces.addAll(asList(clazz.getInterfaces()));
|
||||
clazz = clazz.getSuperclass();
|
||||
}
|
||||
return interfaces;
|
||||
}
|
||||
|
||||
static <A extends Annotation> A findAnnotation(final Class<A> targetAnnotation, final Class<?> annotatedType) {
|
||||
|
||||
A foundAnnotation = annotatedType.getAnnotation(targetAnnotation);
|
||||
if (foundAnnotation == null) {
|
||||
for (Annotation annotation : annotatedType.getAnnotations()) {
|
||||
Class<? extends Annotation> annotationType = annotation.annotationType();
|
||||
if (annotationType.isAnnotationPresent(targetAnnotation)) {
|
||||
foundAnnotation = annotationType.getAnnotation(targetAnnotation);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return foundAnnotation;
|
||||
}
|
||||
|
||||
static boolean isAnnotationPresent(final Class<? extends Annotation> targetAnnotation, final Class<?> annotatedType) {
|
||||
return findAnnotation(targetAnnotation, annotatedType) != null;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
/**
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2017, Mahmoud Ben Hassine (mahmoud.benhassine@icloud.com)
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
/**
|
||||
* This package contains Easy Rules core implementation.
|
||||
*/
|
||||
package org.jeasy.rules.core;
|
@ -0,0 +1,54 @@
|
||||
/**
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2017, Mahmoud Ben Hassine (mahmoud.benhassine@icloud.com)
|
||||
*
|
||||
* 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;
|
||||
|
||||
import junit.framework.TestSuite;
|
||||
import org.jeasy.rules.core.*;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.Suite;
|
||||
|
||||
/**
|
||||
* Test suite for Easy Rules core module.
|
||||
*
|
||||
* @author Mahmoud Ben Hassine (mahmoud.benhassine@icloud.com)
|
||||
*/
|
||||
@RunWith(Suite.class)
|
||||
@Suite.SuiteClasses({
|
||||
AnnotationInheritanceTest.class,
|
||||
BasicRuleTest.class,
|
||||
RulePriorityThresholdTest.class,
|
||||
SkipOnFirstAppliedRuleTest.class,
|
||||
SkipOnFirstFailedRuleTest.class,
|
||||
SkipOnFirstNonTriggeredRuleTest.class,
|
||||
RuleListenerTest.class,
|
||||
CustomRuleOrderingTest.class,
|
||||
RuleProxyTest.class,
|
||||
RulesEngineBuilderTest.class,
|
||||
CompositeRuleTest.class,
|
||||
RuleDefinitionValidatorTest.class,
|
||||
DefaultRulesEngineTest.class,
|
||||
UtilsTest.class})
|
||||
public class EasyRulesTestSuite extends TestSuite {
|
||||
|
||||
}
|
@ -0,0 +1,47 @@
|
||||
/**
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2017, Mahmoud Ben Hassine (mahmoud.benhassine@icloud.com)
|
||||
*
|
||||
* 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.annotation;
|
||||
|
||||
@Rule
|
||||
public class AnnotatedRuleWithActionMethodHavingOneArgumentNotOfTypeFacts {
|
||||
|
||||
private boolean executed;
|
||||
|
||||
@Condition
|
||||
public boolean when() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Action
|
||||
public void then(int i) throws Exception {
|
||||
if (i == 1) {
|
||||
executed = true;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isExecuted() {
|
||||
return executed;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,45 @@
|
||||
/**
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2017, Mahmoud Ben Hassine (mahmoud.benhassine@icloud.com)
|
||||
*
|
||||
* 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.annotation;
|
||||
|
||||
@Rule
|
||||
public class AnnotatedRuleWithConditionMethodHavingNonBooleanReturnType {
|
||||
|
||||
private boolean executed;
|
||||
|
||||
@Condition
|
||||
public int when() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Action
|
||||
public void then() throws Exception {
|
||||
executed = true;
|
||||
}
|
||||
|
||||
public boolean isExecuted() {
|
||||
return executed;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,45 @@
|
||||
/**
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2017, Mahmoud Ben Hassine (mahmoud.benhassine@icloud.com)
|
||||
*
|
||||
* 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.annotation;
|
||||
|
||||
@Rule
|
||||
public class AnnotatedRuleWithConditionMethodHavingOneArgumentNotOfTypeFacts {
|
||||
|
||||
private boolean executed;
|
||||
|
||||
@Condition
|
||||
public boolean when(int i) {
|
||||
return i == 0;
|
||||
}
|
||||
|
||||
@Action
|
||||
public void then() throws Exception {
|
||||
executed = true;
|
||||
}
|
||||
|
||||
public boolean isExecuted() {
|
||||
return executed;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
/**
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2017, Mahmoud Ben Hassine (mahmoud.benhassine@icloud.com)
|
||||
*
|
||||
* 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.annotation;
|
||||
|
||||
@MetaRule
|
||||
public class AnnotatedRuleWithMetaRuleAnnotation {
|
||||
|
||||
@Condition
|
||||
public boolean when() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Action
|
||||
public void then() throws Exception {
|
||||
}
|
||||
}
|
@ -0,0 +1,55 @@
|
||||
/**
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2017, Mahmoud Ben Hassine (mahmoud.benhassine@icloud.com)
|
||||
*
|
||||
* 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.annotation;
|
||||
|
||||
@Rule
|
||||
public class AnnotatedRuleWithMoreThanOnePriorityMethod {
|
||||
|
||||
private boolean executed;
|
||||
|
||||
@Condition
|
||||
public boolean when() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Action
|
||||
public void then() throws Exception {
|
||||
executed = true;
|
||||
}
|
||||
|
||||
@Priority
|
||||
public int getPriority() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Priority
|
||||
public int getRulePriority() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
public boolean isExecuted() {
|
||||
return executed;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,45 @@
|
||||
/**
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2017, Mahmoud Ben Hassine (mahmoud.benhassine@icloud.com)
|
||||
*
|
||||
* 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.annotation;
|
||||
|
||||
@Rule
|
||||
public class AnnotatedRuleWithNotPublicActionMethod {
|
||||
|
||||
private boolean executed;
|
||||
|
||||
@Condition
|
||||
private boolean when() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Action
|
||||
private void then() throws Exception {
|
||||
executed = true;
|
||||
}
|
||||
|
||||
public boolean isExecuted() {
|
||||
return executed;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,45 @@
|
||||
/**
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2017, Mahmoud Ben Hassine (mahmoud.benhassine@icloud.com)
|
||||
*
|
||||
* 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.annotation;
|
||||
|
||||
@Rule
|
||||
public class AnnotatedRuleWithNotPublicConditionMethod {
|
||||
|
||||
private boolean executed;
|
||||
|
||||
@Condition
|
||||
private boolean when() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Action
|
||||
public void then() throws Exception {
|
||||
executed = true;
|
||||
}
|
||||
|
||||
public boolean isExecuted() {
|
||||
return executed;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
/**
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2017, Mahmoud Ben Hassine (mahmoud.benhassine@icloud.com)
|
||||
*
|
||||
* 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.annotation;
|
||||
|
||||
@Rule
|
||||
public class AnnotatedRuleWithNotPublicPriorityMethod {
|
||||
|
||||
private boolean executed;
|
||||
|
||||
@Condition
|
||||
private boolean when() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Action
|
||||
private void then() throws Exception {
|
||||
executed = true;
|
||||
}
|
||||
|
||||
public boolean isExecuted() {
|
||||
return executed;
|
||||
}
|
||||
|
||||
@Priority
|
||||
private int getPriority() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
/**
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2017, Mahmoud Ben Hassine (mahmoud.benhassine@icloud.com)
|
||||
*
|
||||
* 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.annotation;
|
||||
|
||||
@Rule
|
||||
public class AnnotatedRuleWithPriorityMethodHavingArguments {
|
||||
|
||||
private boolean executed;
|
||||
|
||||
@Condition
|
||||
private boolean when() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Action
|
||||
private void then() throws Exception {
|
||||
executed = true;
|
||||
}
|
||||
|
||||
public boolean isExecuted() {
|
||||
return executed;
|
||||
}
|
||||
|
||||
@Priority
|
||||
public int getPriority(int i) {
|
||||
return i;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
/**
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2017, Mahmoud Ben Hassine (mahmoud.benhassine@icloud.com)
|
||||
*
|
||||
* 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.annotation;
|
||||
|
||||
@Rule
|
||||
public class AnnotatedRuleWithPriorityMethodHavingNonIntegerReturnType {
|
||||
|
||||
private boolean executed;
|
||||
|
||||
@Condition
|
||||
private boolean when() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Action
|
||||
private void then() throws Exception {
|
||||
executed = true;
|
||||
}
|
||||
|
||||
public boolean isExecuted() {
|
||||
return executed;
|
||||
}
|
||||
|
||||
@Priority
|
||||
public String getPriority() {
|
||||
return "1";
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,44 @@
|
||||
/**
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2017, Mahmoud Ben Hassine (mahmoud.benhassine@icloud.com)
|
||||
*
|
||||
* 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.annotation;
|
||||
|
||||
@Rule
|
||||
public class AnnotatedRuleWithoutActionMethod {
|
||||
|
||||
private boolean executed;
|
||||
|
||||
@Condition
|
||||
public boolean when() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public void then() throws Exception {
|
||||
executed = true;
|
||||
}
|
||||
|
||||
public boolean isExecuted() {
|
||||
return executed;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,44 @@
|
||||
/**
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2017, Mahmoud Ben Hassine (mahmoud.benhassine@icloud.com)
|
||||
*
|
||||
* 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.annotation;
|
||||
|
||||
@Rule
|
||||
public class AnnotatedRuleWithoutCondition {
|
||||
|
||||
private boolean executed;
|
||||
|
||||
public boolean when() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Action
|
||||
public void then() throws Exception {
|
||||
executed = true;
|
||||
}
|
||||
|
||||
public boolean isExecuted() {
|
||||
return executed;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
/**
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2017, Mahmoud Ben Hassine (mahmoud.benhassine@icloud.com)
|
||||
*
|
||||
* 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.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.TYPE)
|
||||
@Rule
|
||||
public @interface MetaRule {
|
||||
}
|
@ -0,0 +1,58 @@
|
||||
/**
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2017, Mahmoud Ben Hassine (mahmoud.benhassine@icloud.com)
|
||||
*
|
||||
* 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.core;
|
||||
|
||||
import org.jeasy.rules.api.Facts;
|
||||
import org.jeasy.rules.api.Rule;
|
||||
import org.jeasy.rules.api.Rules;
|
||||
import org.jeasy.rules.api.RulesEngine;
|
||||
import org.junit.Before;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public abstract class AbstractTest {
|
||||
|
||||
@Mock
|
||||
protected Rule rule1, rule2;
|
||||
@Mock
|
||||
protected Object fact1, fact2;
|
||||
|
||||
protected Facts facts;
|
||||
protected Rules rules;
|
||||
|
||||
protected RulesEngine rulesEngine;
|
||||
|
||||
@Before
|
||||
public void setup() throws Exception {
|
||||
facts = new Facts();
|
||||
facts.add("fact1", fact1);
|
||||
facts.add("fact2", fact2);
|
||||
rules = new Rules(rule1, rule2);
|
||||
|
||||
rulesEngine = RulesEngineBuilder.aNewRulesEngine().build();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,83 @@
|
||||
/**
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2017, Mahmoud Ben Hassine (mahmoud.benhassine@icloud.com)
|
||||
*
|
||||
* 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.core;
|
||||
|
||||
import org.jeasy.rules.annotation.Action;
|
||||
import org.jeasy.rules.annotation.Condition;
|
||||
import org.jeasy.rules.annotation.Fact;
|
||||
import org.jeasy.rules.annotation.Rule;
|
||||
import org.jeasy.rules.api.Facts;
|
||||
import org.jeasy.rules.api.Rules;
|
||||
import org.jeasy.rules.api.RulesEngine;
|
||||
import org.junit.Test;
|
||||
|
||||
public class AnnotatedRulesTest {
|
||||
|
||||
@Test
|
||||
public void test() throws Exception {
|
||||
Facts facts = new Facts();
|
||||
facts.add("rain", true);
|
||||
facts.add("age", 18);
|
||||
|
||||
Rules rules = new Rules(
|
||||
new WeatherRule(),
|
||||
new AgeRule()
|
||||
);
|
||||
|
||||
RulesEngine rulesEngine = new DefaultRulesEngine();
|
||||
|
||||
rulesEngine.fire(rules, facts);
|
||||
}
|
||||
|
||||
@Rule
|
||||
class AgeRule {
|
||||
|
||||
@Condition
|
||||
public boolean isAdult(@Fact("age") int age) {
|
||||
return age >= 18;
|
||||
}
|
||||
|
||||
@Action
|
||||
public void then() {
|
||||
System.out.println("You are an adult");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@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!");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,68 @@
|
||||
/**
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2017, Mahmoud Ben Hassine (mahmoud.benhassine@icloud.com)
|
||||
*
|
||||
* 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.core;
|
||||
|
||||
import org.jeasy.rules.annotation.Action;
|
||||
import org.jeasy.rules.annotation.Condition;
|
||||
import org.jeasy.rules.api.Facts;
|
||||
import org.jeasy.rules.api.Rules;
|
||||
import org.jeasy.rules.api.RulesEngine;
|
||||
import org.jeasy.rules.annotation.Rule;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
public class AnnotationInheritanceTest {
|
||||
|
||||
@Test
|
||||
public void annotationsShouldBeInherited() throws Exception {
|
||||
MyChildRule myChildRule = new MyChildRule();
|
||||
RulesEngine rulesEngine = RulesEngineBuilder.aNewRulesEngine().build();
|
||||
Rules rules = new Rules();
|
||||
rules.register(myChildRule);
|
||||
rulesEngine.fire(rules, new Facts());
|
||||
|
||||
assertThat(myChildRule.isExecuted()).isTrue();
|
||||
}
|
||||
|
||||
@Rule
|
||||
class MyBaseRule {
|
||||
protected boolean executed;
|
||||
@Condition
|
||||
public boolean when() {
|
||||
return true;
|
||||
}
|
||||
@Action
|
||||
public void then() {
|
||||
executed = true;
|
||||
}
|
||||
public boolean isExecuted() {
|
||||
return executed;
|
||||
}
|
||||
}
|
||||
|
||||
class MyChildRule extends MyBaseRule {
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,112 @@
|
||||
/**
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2017, Mahmoud Ben Hassine (mahmoud.benhassine@icloud.com)
|
||||
*
|
||||
* 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.core;
|
||||
|
||||
import org.jeasy.rules.api.Facts;
|
||||
import org.jeasy.rules.api.Rules;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
public class BasicRuleTest extends AbstractTest {
|
||||
|
||||
@Test
|
||||
public void basicRuleEvaluateShouldReturnFalse() throws Exception {
|
||||
BasicRule basicRule = new BasicRule();
|
||||
assertThat(basicRule.evaluate(facts)).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCompareTo() {
|
||||
FirstRule rule1 = new FirstRule();
|
||||
FirstRule rule2 = new FirstRule();
|
||||
|
||||
assertThat(rule1.compareTo(rule2)).isEqualTo(0);
|
||||
assertThat(rule2.compareTo(rule1)).isEqualTo(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSortSequence() {
|
||||
FirstRule rule1 = new FirstRule();
|
||||
SecondRule rule2 = new SecondRule();
|
||||
ThirdRule rule3 = new ThirdRule();
|
||||
|
||||
rules = new Rules(rule1, rule2, rule3);
|
||||
|
||||
rulesEngine.check(rules, facts);
|
||||
assertThat(rules).containsSequence(rule1, rule3, rule2);
|
||||
}
|
||||
|
||||
class FirstRule extends BasicRule {
|
||||
@Override
|
||||
public int getPriority() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean evaluate(Facts facts) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "rule1";
|
||||
}
|
||||
}
|
||||
|
||||
class SecondRule extends BasicRule {
|
||||
@Override
|
||||
public int getPriority() {
|
||||
return 3;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean evaluate(Facts facts) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "rule2";
|
||||
}
|
||||
}
|
||||
|
||||
class ThirdRule extends BasicRule {
|
||||
@Override
|
||||
public int getPriority() {
|
||||
return 2;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean evaluate(Facts facts) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "rule3";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,171 @@
|
||||
/**
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2017, Mahmoud Ben Hassine (mahmoud.benhassine@icloud.com)
|
||||
*
|
||||
* 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.core;
|
||||
|
||||
import org.jeasy.rules.annotation.Action;
|
||||
import org.jeasy.rules.annotation.Condition;
|
||||
import org.jeasy.rules.annotation.Rule;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
/**
|
||||
* Test class for composite rule execution.
|
||||
*
|
||||
* @author Mahmoud Ben Hassine (mahmoud.benhassine@icloud.com)
|
||||
*/
|
||||
public class CompositeRuleTest extends AbstractTest {
|
||||
|
||||
private CompositeRule compositeRule;
|
||||
|
||||
@Before
|
||||
public void setup() throws Exception {
|
||||
super.setup();
|
||||
when(rule1.evaluate(facts)).thenReturn(true);
|
||||
when(rule2.evaluate(facts)).thenReturn(true);
|
||||
when(rule2.compareTo(rule1)).thenReturn(1);
|
||||
compositeRule = new CompositeRule();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void compositeRuleAndComposingRulesMustBeExecuted() throws Exception {
|
||||
compositeRule.addRule(rule1);
|
||||
compositeRule.addRule(rule2);
|
||||
rules.clear(); // FIXME
|
||||
rules.register(compositeRule);
|
||||
rulesEngine.fire(rules, facts);
|
||||
verify(rule1).execute(facts);
|
||||
verify(rule2).execute(facts);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void compositeRuleMustNotBeExecutedIfAComposingRuleEvaluatesToFalse() throws Exception {
|
||||
when(rule2.evaluate(facts)).thenReturn(false);
|
||||
compositeRule.addRule(rule1);
|
||||
compositeRule.addRule(rule2);
|
||||
rules.clear(); // FIXME
|
||||
rules.register(compositeRule);
|
||||
rulesEngine.fire(rules, facts);
|
||||
|
||||
/*
|
||||
* The composing rules should not be executed
|
||||
* since not all rules conditions evaluate to TRUE
|
||||
*/
|
||||
|
||||
//Rule 1 should not be executed
|
||||
verify(rule1, never()).execute(facts);
|
||||
|
||||
//Rule 2 should not be executed
|
||||
verify(rule2, never()).execute(facts);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenARuleIsRemoved_thenItShouldNotBeEvaluated() throws Exception {
|
||||
|
||||
compositeRule.addRule(rule1);
|
||||
compositeRule.addRule(rule2);
|
||||
compositeRule.removeRule(rule2);
|
||||
|
||||
rules.clear(); // FIXME
|
||||
rules.register(compositeRule);
|
||||
|
||||
rulesEngine.fire(rules, facts);
|
||||
|
||||
//Rule 1 should be executed
|
||||
verify(rule1).execute(facts);
|
||||
|
||||
//Rule 2 should not be evaluated nor executed
|
||||
verify(rule2, never()).evaluate(facts);
|
||||
verify(rule2, never()).execute(facts);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenNoComposingRulesAreRegistered_thenCompositeRuleShouldEvaluateToFalse() {
|
||||
assertThat(compositeRule.evaluate(facts)).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCompositeRuleWithAnnotatedComposingRules() throws Exception {
|
||||
MyRule rule = new MyRule();
|
||||
compositeRule = new CompositeRule("myCompositeRule");
|
||||
compositeRule.addRule(rule);
|
||||
|
||||
rules.register(compositeRule);;
|
||||
rulesEngine.fire(rules, facts);
|
||||
|
||||
assertThat(rule.isExecuted()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenAnnotatedRuleIsRemoved_thenItsProxyShouldBeRetrieved() throws Exception {
|
||||
MyRule rule = new MyRule();
|
||||
MyAnnotatedRule annotatedRule = new MyAnnotatedRule();
|
||||
compositeRule = new CompositeRule("myCompositeRule", "composite rule with mixed types of rules");
|
||||
compositeRule.addRule(rule);
|
||||
compositeRule.addRule(annotatedRule);
|
||||
compositeRule.removeRule(annotatedRule);
|
||||
|
||||
rules.register(compositeRule);
|
||||
rulesEngine.fire(rules, facts);
|
||||
|
||||
assertThat(rule.isExecuted()).isTrue();
|
||||
assertThat(annotatedRule.isExecuted()).isFalse();
|
||||
}
|
||||
|
||||
@Rule
|
||||
class MyRule {
|
||||
boolean executed;
|
||||
@Condition
|
||||
public boolean when() {
|
||||
return true;
|
||||
}
|
||||
@Action
|
||||
public void then() {
|
||||
executed = true;
|
||||
}
|
||||
public boolean isExecuted() {
|
||||
return executed;
|
||||
}
|
||||
}
|
||||
|
||||
@Rule
|
||||
static class MyAnnotatedRule {
|
||||
private boolean executed;
|
||||
@Condition
|
||||
public boolean evaluate() {
|
||||
return true;
|
||||
}
|
||||
@Action
|
||||
public void execute() {
|
||||
executed = true;
|
||||
}
|
||||
public boolean isExecuted() {
|
||||
return executed;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,84 @@
|
||||
/**
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2017, Mahmoud Ben Hassine (mahmoud.benhassine@icloud.com)
|
||||
*
|
||||
* 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.core;
|
||||
|
||||
import org.jeasy.rules.api.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.InOrder;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
|
||||
import static org.mockito.Mockito.inOrder;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
/**
|
||||
* Test class for custom rule ordering.
|
||||
*
|
||||
* @author Mahmoud Ben Hassine (mahmoud.benhassine@icloud.com)
|
||||
*/
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class CustomRuleOrderingTest extends AbstractTest {
|
||||
|
||||
@Mock
|
||||
private MyRule rule1, rule2;
|
||||
|
||||
@Test
|
||||
public void whenCompareToIsOverridden_thenShouldExecuteRulesInTheCustomOrder() throws Exception {
|
||||
|
||||
when(rule1.getName()).thenReturn("a");
|
||||
when(rule1.getPriority()).thenReturn(1);
|
||||
when(rule1.evaluate(facts)).thenReturn(true);
|
||||
|
||||
when(rule2.getName()).thenReturn("b");
|
||||
when(rule2.getPriority()).thenReturn(0);
|
||||
when(rule2.evaluate(facts)).thenReturn(true);
|
||||
|
||||
when(rule2.compareTo(rule1)).thenCallRealMethod();
|
||||
|
||||
rules.clear();
|
||||
rules.register(rule1);
|
||||
rules.register(rule2);
|
||||
|
||||
rulesEngine.fire(rules, facts);
|
||||
|
||||
/*
|
||||
* By default, if compareTo is not overridden, then rule2 should be executed first (priority 0 < 1).
|
||||
* But in this case, the compareTo method order rules by their name, so rule1 should be executed first ("a" < "b")
|
||||
*/
|
||||
InOrder inOrder = inOrder(rule1, rule2);
|
||||
inOrder.verify(rule1).execute(facts);
|
||||
inOrder.verify(rule2).execute(facts);
|
||||
|
||||
}
|
||||
|
||||
class MyRule extends BasicRule {
|
||||
|
||||
@Override
|
||||
public int compareTo(Rule rule) {
|
||||
return getName().compareTo(rule.getName());
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,266 @@
|
||||
/**
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2017, Mahmoud Ben Hassine (mahmoud.benhassine@icloud.com)
|
||||
*
|
||||
* 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.core;
|
||||
|
||||
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.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.mockito.*;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
/**
|
||||
* Test class for {@link DefaultRulesEngine}.
|
||||
*
|
||||
* @author Mahmoud Ben Hassine (mahmoud.benhassine@icloud.com)
|
||||
*/
|
||||
public class DefaultRulesEngineTest extends AbstractTest {
|
||||
|
||||
@Mock
|
||||
private BasicRule rule, anotherRule;
|
||||
|
||||
@Mock
|
||||
private RuleListener ruleListener;
|
||||
|
||||
private AnnotatedRule annotatedRule;
|
||||
|
||||
@Before
|
||||
public void setup() throws Exception {
|
||||
super.setup();
|
||||
when(rule.getName()).thenReturn("r");
|
||||
when(rule.getDescription()).thenReturn("d");
|
||||
when(rule.getPriority()).thenReturn(1);
|
||||
annotatedRule = new AnnotatedRule();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenConditionIsTrue_thenActionShouldBeExecuted() throws Exception {
|
||||
when(rule.evaluate(facts)).thenReturn(true);
|
||||
|
||||
rules.clear();// FIXME
|
||||
rules.register(rule);
|
||||
|
||||
rulesEngine.fire(rules, facts);
|
||||
|
||||
verify(rule).execute(facts);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenConditionIsFalse_thenActionShouldNotBeExecuted() throws Exception {
|
||||
when(rule.evaluate(facts)).thenReturn(false);
|
||||
rules.clear();// FIXME
|
||||
rules.register(rule);
|
||||
|
||||
rulesEngine.fire(rules, facts);
|
||||
|
||||
verify(rule, never()).execute(facts);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void rulesMustBeTriggeredInTheirNaturalOrder() throws Exception {
|
||||
when(rule.evaluate(facts)).thenReturn(true);
|
||||
when(anotherRule.evaluate(facts)).thenReturn(true);
|
||||
when(anotherRule.compareTo(rule)).thenReturn(1);
|
||||
rules.clear();// FIXME
|
||||
rules.register(rule);
|
||||
rules.register(anotherRule);
|
||||
|
||||
rulesEngine.fire(rules, facts);
|
||||
|
||||
InOrder inOrder = inOrder(rule, anotherRule);
|
||||
inOrder.verify(rule).execute(facts);
|
||||
inOrder.verify(anotherRule).execute(facts);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void rulesMustBeCheckedInTheirNaturalOrder() throws Exception {
|
||||
when(rule.evaluate(facts)).thenReturn(true);
|
||||
when(anotherRule.evaluate(facts)).thenReturn(true);
|
||||
when(anotherRule.compareTo(rule)).thenReturn(1);
|
||||
rules.clear();// FIXME
|
||||
rules.register(rule);
|
||||
rules.register(anotherRule);
|
||||
|
||||
rulesEngine.check(rules, facts);
|
||||
|
||||
InOrder inOrder = inOrder(rule, anotherRule);
|
||||
inOrder.verify(rule).evaluate(facts);
|
||||
inOrder.verify(anotherRule).evaluate(facts);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void actionsMustBeExecutedInTheDefinedOrder() {
|
||||
rules.clear(); // FIXME
|
||||
rules.register(annotatedRule);
|
||||
rulesEngine.fire(rules, facts);
|
||||
assertEquals("012", annotatedRule.getActionSequence());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void annotatedRulesAndNonAnnotatedRulesShouldBeUsableTogether() throws Exception {
|
||||
when(rule.evaluate(facts)).thenReturn(true);
|
||||
rules.clear(); // FIXME
|
||||
rules.register(rule);
|
||||
rules.register(annotatedRule);
|
||||
|
||||
rulesEngine.fire(rules, facts);
|
||||
|
||||
verify(rule).execute(facts);
|
||||
assertThat(annotatedRule.isExecuted()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenRuleNameIsNotSpecified_thenItShouldBeEqualToClassNameByDefault() throws Exception {
|
||||
org.jeasy.rules.api.Rule rule = RuleProxy.asRule(new DummyRule());
|
||||
assertThat(rule.getName()).isEqualTo("DummyRule");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenRuleDescriptionIsNotSpecified_thenItShouldBeEqualToConditionNameFollowedByActionsNames() throws Exception {
|
||||
org.jeasy.rules.api.Rule rule = RuleProxy.asRule(new DummyRule());
|
||||
assertThat(rule.getDescription()).isEqualTo("when condition then action1,action2");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCheckRules() throws Exception {
|
||||
// Given
|
||||
when(rule.evaluate(facts)).thenReturn(true);
|
||||
rules.clear(); // FIXME
|
||||
rules.register(rule);
|
||||
rules.register(annotatedRule);
|
||||
|
||||
// When
|
||||
Map<org.jeasy.rules.api.Rule, Boolean> result = rulesEngine.check(rules, facts);
|
||||
|
||||
// Then
|
||||
assertThat(result).hasSize(2);
|
||||
for (org.jeasy.rules.api.Rule r : rules) {
|
||||
assertThat(result.get(r)).isTrue();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void listenerShouldBeInvokedBeforeCheckingRules() throws Exception {
|
||||
// Given
|
||||
when(rule.evaluate(facts)).thenReturn(true);
|
||||
when(ruleListener.beforeEvaluate(rule, facts)).thenReturn(true);
|
||||
rulesEngine = RulesEngineBuilder.aNewRulesEngine()
|
||||
.withRuleListener(ruleListener)
|
||||
.build();
|
||||
rules.clear(); // FIXME
|
||||
rules.register(rule);
|
||||
|
||||
// When
|
||||
rulesEngine.check(rules, facts);
|
||||
|
||||
// Then
|
||||
verify(ruleListener).beforeEvaluate(rule, facts);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetRuleListeners() throws Exception {
|
||||
rulesEngine = RulesEngineBuilder.aNewRulesEngine()
|
||||
.withRuleListener(ruleListener)
|
||||
.build();
|
||||
|
||||
assertThat(rulesEngine.getRuleListeners())
|
||||
.containsExactly(ruleListener);
|
||||
}
|
||||
|
||||
@After
|
||||
public void clearRules() {
|
||||
rules.clear();
|
||||
}
|
||||
|
||||
@org.jeasy.rules.annotation.Rule(name = "myRule", description = "my rule description")
|
||||
public class AnnotatedRule {
|
||||
|
||||
private boolean executed;
|
||||
|
||||
private String actionSequence = "";
|
||||
|
||||
@Condition
|
||||
public boolean when() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Action
|
||||
public void then0() throws Exception {
|
||||
actionSequence += "0";
|
||||
}
|
||||
|
||||
@Action(order = 1)
|
||||
public void then1() throws Exception {
|
||||
actionSequence += "1";
|
||||
}
|
||||
|
||||
@Action(order = 2)
|
||||
public void then2() throws Exception {
|
||||
actionSequence += "2";
|
||||
executed = true;
|
||||
}
|
||||
|
||||
@Priority
|
||||
public int getPriority() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public boolean isExecuted() {
|
||||
return executed;
|
||||
}
|
||||
|
||||
public String getActionSequence() {
|
||||
return actionSequence;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@org.jeasy.rules.annotation.Rule
|
||||
public class DummyRule {
|
||||
|
||||
@Condition
|
||||
public boolean condition() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Action(order = 1)
|
||||
public void action1() throws Exception {
|
||||
// no op
|
||||
}
|
||||
|
||||
@Action(order = 2)
|
||||
public void action2() throws Exception {
|
||||
// no op
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,117 @@
|
||||
/**
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2017, Mahmoud Ben Hassine (mahmoud.benhassine@icloud.com)
|
||||
*
|
||||
* 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.core;
|
||||
|
||||
|
||||
import org.jeasy.rules.annotation.*;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
public class RuleDefinitionValidatorTest {
|
||||
|
||||
private RuleDefinitionValidator ruleDefinitionValidator;
|
||||
|
||||
@Before
|
||||
public void setup(){
|
||||
ruleDefinitionValidator = new RuleDefinitionValidator();
|
||||
}
|
||||
|
||||
/*
|
||||
* Rule annotation test
|
||||
*/
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void notAnnotatedRuleMustNotBeAccepted() {
|
||||
ruleDefinitionValidator.validateRuleDefinition(new Object());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void withCustomAnnotationThatIsItselfAnnotatedWithTheRuleAnnotation() throws Throwable {
|
||||
ruleDefinitionValidator.validateRuleDefinition(new AnnotatedRuleWithMetaRuleAnnotation());
|
||||
}
|
||||
|
||||
/*
|
||||
* Conditions methods tests
|
||||
*/
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void conditionMethodMustBeDefined() {
|
||||
ruleDefinitionValidator.validateRuleDefinition(new AnnotatedRuleWithoutCondition());
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void conditionMethodMustBePublic() {
|
||||
ruleDefinitionValidator.validateRuleDefinition(new AnnotatedRuleWithNotPublicConditionMethod());
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void whenConditionMethodHasOneNonAnnotatedParameter_thenThisParameterMustBeOfTypeFacts() {
|
||||
ruleDefinitionValidator.validateRuleDefinition(new AnnotatedRuleWithConditionMethodHavingOneArgumentNotOfTypeFacts());
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void conditionMethodMustReturnBooleanType() {
|
||||
ruleDefinitionValidator.validateRuleDefinition(new AnnotatedRuleWithConditionMethodHavingNonBooleanReturnType());
|
||||
}
|
||||
|
||||
/*
|
||||
* Action method tests
|
||||
*/
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void actionMethodMustBeDefined() {
|
||||
ruleDefinitionValidator.validateRuleDefinition(new AnnotatedRuleWithoutActionMethod());
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void actionMethodMustBePublic() {
|
||||
ruleDefinitionValidator.validateRuleDefinition(new AnnotatedRuleWithNotPublicActionMethod());
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void actionMethodMustHaveNoArguments() {
|
||||
ruleDefinitionValidator.validateRuleDefinition(new AnnotatedRuleWithActionMethodHavingOneArgumentNotOfTypeFacts());
|
||||
}
|
||||
|
||||
/*
|
||||
* Priority method tests
|
||||
*/
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void priorityMethodMustBeUnique() {
|
||||
ruleDefinitionValidator.validateRuleDefinition(new AnnotatedRuleWithMoreThanOnePriorityMethod());
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void priorityMethodMustBePublic() {
|
||||
ruleDefinitionValidator.validateRuleDefinition(new AnnotatedRuleWithNotPublicPriorityMethod());
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void priorityMethodMustHaveNoArguments() {
|
||||
ruleDefinitionValidator.validateRuleDefinition(new AnnotatedRuleWithPriorityMethodHavingArguments());
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void priorityMethodReturnTypeMustBeInteger() {
|
||||
ruleDefinitionValidator.validateRuleDefinition(new AnnotatedRuleWithPriorityMethodHavingNonIntegerReturnType());
|
||||
}
|
||||
}
|
@ -0,0 +1,158 @@
|
||||
/**
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2017, Mahmoud Ben Hassine (mahmoud.benhassine@icloud.com)
|
||||
*
|
||||
* 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.core;
|
||||
|
||||
import org.jeasy.rules.api.RuleListener;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.mockito.InOrder;
|
||||
import org.mockito.Mock;
|
||||
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
/**
|
||||
* Test class of the execution of rule listeners.
|
||||
*
|
||||
* @author Mahmoud Ben Hassine (mahmoud.benhassine@icloud.com)
|
||||
*/
|
||||
public class RuleListenerTest extends AbstractTest {
|
||||
|
||||
@Mock
|
||||
private RuleListener ruleListener1, ruleListener2;
|
||||
|
||||
@Before
|
||||
public void setup() throws Exception {
|
||||
super.setup();
|
||||
when(ruleListener1.beforeEvaluate(rule1, facts)).thenReturn(true);
|
||||
when(ruleListener2.beforeEvaluate(rule1, facts)).thenReturn(true);
|
||||
rulesEngine = RulesEngineBuilder.aNewRulesEngine()
|
||||
.withRuleListener(ruleListener1)
|
||||
.withRuleListener(ruleListener2)
|
||||
.build();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenTheRuleExecutesSuccessfully_thenOnSuccessShouldBeExecuted() throws Exception {
|
||||
when(rule1.evaluate(facts)).thenReturn(true);
|
||||
rules.clear(); // FIXME
|
||||
rules.register(rule1);
|
||||
rulesEngine.fire(rules, facts);
|
||||
|
||||
InOrder inOrder = inOrder(rule1, fact1, fact2, ruleListener1, ruleListener2);
|
||||
inOrder.verify(ruleListener1).beforeExecute(rule1, facts);
|
||||
inOrder.verify(ruleListener2).beforeExecute(rule1, facts);
|
||||
inOrder.verify(ruleListener1).onSuccess(rule1, facts);
|
||||
inOrder.verify(ruleListener2).onSuccess(rule1, facts);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenTheRuleFails_thenOnFailureShouldBeExecuted() throws Exception {
|
||||
|
||||
final Exception exception = new Exception("fatal error!");
|
||||
doThrow(exception).when(rule1).execute(facts);
|
||||
when(rule1.evaluate(facts)).thenReturn(true);
|
||||
|
||||
rules.clear(); // FIXME
|
||||
rules.register(rule1);
|
||||
|
||||
rulesEngine.fire(rules, facts);
|
||||
|
||||
InOrder inOrder = inOrder(rule1, fact1, fact2, ruleListener1, ruleListener2);
|
||||
inOrder.verify(ruleListener1).beforeExecute(rule1, facts);
|
||||
inOrder.verify(ruleListener2).beforeExecute(rule1, facts);
|
||||
inOrder.verify(ruleListener1).onFailure(rule1, exception, facts);
|
||||
inOrder.verify(ruleListener2).onFailure(rule1, exception, facts);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenListenerReturnsFalse_thenTheRuleShouldBeSkippedBeforeBeingEvaluated() throws Exception {
|
||||
|
||||
// Given
|
||||
when(ruleListener1.beforeEvaluate(rule1, facts)).thenReturn(false);
|
||||
rulesEngine = RulesEngineBuilder.aNewRulesEngine()
|
||||
.withRuleListener(ruleListener1)
|
||||
.build();
|
||||
|
||||
// When
|
||||
rules.register(rule1);
|
||||
rulesEngine.fire(rules, facts);
|
||||
|
||||
// Then
|
||||
verify(rule1, never()).evaluate(facts);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenListenerReturnsTrue_thenTheRuleShouldBeEvaluated() throws Exception {
|
||||
|
||||
// Given
|
||||
when(ruleListener1.beforeEvaluate(rule1, facts)).thenReturn(true);
|
||||
rulesEngine = RulesEngineBuilder.aNewRulesEngine()
|
||||
.withRuleListener(ruleListener1)
|
||||
.build();
|
||||
|
||||
// When
|
||||
rules.register(rule1);
|
||||
rulesEngine.fire(rules, facts);
|
||||
|
||||
// Then
|
||||
verify(rule1).evaluate(facts);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenTheRuleEvaluatesToTrue_thenTheListenerShouldBeInvoked() throws Exception {
|
||||
// Given
|
||||
when(rule1.evaluate(facts)).thenReturn(true);
|
||||
rulesEngine = RulesEngineBuilder.aNewRulesEngine()
|
||||
.withRuleListener(ruleListener1)
|
||||
.build();
|
||||
|
||||
// When
|
||||
rules.clear();
|
||||
rules.register(rule1);
|
||||
rulesEngine.fire(rules, facts);
|
||||
|
||||
// Then
|
||||
verify(ruleListener1).afterEvaluate(rule1, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenTheRuleEvaluatesToFalse_thenTheListenerShouldBeInvoked() throws Exception {
|
||||
// Given
|
||||
when(rule1.evaluate(facts)).thenReturn(false);
|
||||
rulesEngine = RulesEngineBuilder.aNewRulesEngine()
|
||||
.withRuleListener(ruleListener1)
|
||||
.build();
|
||||
|
||||
// When
|
||||
rules.clear();
|
||||
rules.register(rule1);
|
||||
rulesEngine.fire(rules, facts);
|
||||
|
||||
// Then
|
||||
verify(ruleListener1).afterEvaluate(rule1, false);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,68 @@
|
||||
/**
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2017, Mahmoud Ben Hassine (mahmoud.benhassine@icloud.com)
|
||||
*
|
||||
* 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.core;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
/**
|
||||
* Test class of rules priority comparison.
|
||||
*
|
||||
* @author Mahmoud Ben Hassine (mahmoud.benhassine@icloud.com)
|
||||
*/
|
||||
public class RulePriorityThresholdTest extends AbstractTest {
|
||||
|
||||
@Before
|
||||
public void setup() throws Exception {
|
||||
super.setup();
|
||||
|
||||
when(rule1.getPriority()).thenReturn(1);
|
||||
when(rule1.evaluate(facts)).thenReturn(true);
|
||||
|
||||
rulesEngine = RulesEngineBuilder.aNewRulesEngine()
|
||||
.withRulePriorityThreshold(1)
|
||||
.build();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void rulesThatExceedPriorityThresholdMustNotBeExecuted() throws Exception {
|
||||
|
||||
rules.register(rule1);
|
||||
rules.register(rule2);
|
||||
|
||||
rulesEngine.fire(rules, facts);
|
||||
|
||||
//Rule 1 should be executed
|
||||
verify(rule1).execute(facts);
|
||||
|
||||
//Rule 2 should be skipped since its priority (2) exceeds priority threshold (1)
|
||||
verify(rule2, never()).execute(facts);
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
/**
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2017, Mahmoud Ben Hassine (mahmoud.benhassine@icloud.com)
|
||||
*
|
||||
* 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.core;
|
||||
|
||||
import org.jeasy.rules.annotation.AnnotatedRuleWithMetaRuleAnnotation;
|
||||
import org.jeasy.rules.api.Rule;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
|
||||
public class RuleProxyTest {
|
||||
|
||||
@Test
|
||||
public void proxyingHappensEvenWhenRuleIsAnnotatedWithMetaRuleAnnotation() {
|
||||
AnnotatedRuleWithMetaRuleAnnotation rule1 = new AnnotatedRuleWithMetaRuleAnnotation();
|
||||
|
||||
Rule rule = RuleProxy.asRule(rule1);
|
||||
|
||||
assertNotNull(rule.getDescription());
|
||||
assertNotNull(rule.getName());
|
||||
}
|
||||
}
|
@ -0,0 +1,81 @@
|
||||
/**
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2017, Mahmoud Ben Hassine (mahmoud.benhassine@icloud.com)
|
||||
*
|
||||
* 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.core;
|
||||
|
||||
import org.jeasy.rules.api.RuleListener;
|
||||
import org.jeasy.rules.api.RulesEngine;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class RulesEngineBuilderTest {
|
||||
|
||||
@Mock
|
||||
private RuleListener ruleListener;
|
||||
|
||||
@Test
|
||||
public void testCreationWithDefaultParameters() {
|
||||
RulesEngine rulesEngine = RulesEngineBuilder.aNewRulesEngine().build();
|
||||
|
||||
assertThat(rulesEngine).isNotNull();
|
||||
RulesEngineParameters parameters = rulesEngine.getParameters();
|
||||
|
||||
assertThat(parameters.getName()).isEqualTo(RulesEngine.DEFAULT_NAME);
|
||||
assertThat(parameters.getPriorityThreshold()).isEqualTo(RulesEngine.DEFAULT_RULE_PRIORITY_THRESHOLD);
|
||||
|
||||
assertThat(parameters.isSkipOnFirstAppliedRule()).isFalse();
|
||||
assertThat(parameters.isSkipOnFirstFailedRule()).isFalse();
|
||||
assertThat(parameters.isSkipOnFirstNonTriggeredRule()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreationWithCustomParameters() {
|
||||
String name = "myRulesEngine";
|
||||
int expectedThreshold = 10;
|
||||
|
||||
RulesEngine rulesEngine = RulesEngineBuilder.aNewRulesEngine()
|
||||
.named(name)
|
||||
.withRuleListener(ruleListener)
|
||||
.withRulePriorityThreshold(expectedThreshold)
|
||||
.withSilentMode(true)
|
||||
.withSkipOnFirstNonTriggeredRule(true)
|
||||
.withSkipOnFirstAppliedRule(true)
|
||||
.withSkipOnFirstFailedRule(true)
|
||||
.build();
|
||||
|
||||
assertThat(rulesEngine).isNotNull();
|
||||
RulesEngineParameters parameters = rulesEngine.getParameters();
|
||||
|
||||
assertThat(parameters.getName()).isEqualTo(name);
|
||||
assertThat(parameters.getPriorityThreshold()).isEqualTo(expectedThreshold);
|
||||
assertThat(parameters.isSilentMode()).isTrue();
|
||||
assertThat(parameters.isSkipOnFirstAppliedRule()).isTrue();
|
||||
assertThat(parameters.isSkipOnFirstFailedRule()).isTrue();
|
||||
assertThat(parameters.isSkipOnFirstNonTriggeredRule()).isTrue();
|
||||
}
|
||||
}
|
@ -0,0 +1,80 @@
|
||||
/**
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2017, Mahmoud Ben Hassine (mahmoud.benhassine@icloud.com)
|
||||
*
|
||||
* 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.core;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.jeasy.rules.core.RulesEngineBuilder.aNewRulesEngine;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
/**
|
||||
* Test class of "skip on first applied rule" parameter of Easy Rules default engine.
|
||||
*
|
||||
* @author Mahmoud Ben Hassine (mahmoud.benhassine@icloud.com)
|
||||
*/
|
||||
public class SkipOnFirstAppliedRuleTest extends AbstractTest {
|
||||
|
||||
@Before
|
||||
public void setup() throws Exception {
|
||||
super.setup();
|
||||
|
||||
setUpRule1();
|
||||
|
||||
rulesEngine = aNewRulesEngine()
|
||||
.withSkipOnFirstAppliedRule(true)
|
||||
.build();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSkipOnFirstAppliedRule() throws Exception {
|
||||
rulesEngine.fire(rules, facts);
|
||||
|
||||
//Rule 1 should be executed
|
||||
verify(rule1).execute(facts);
|
||||
|
||||
//Rule 2 should be skipped since Rule 1 has been executed
|
||||
verify(rule2, never()).execute(facts);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSkipOnFirstAppliedRuleWithException() throws Exception {
|
||||
rulesEngine.fire(rules, facts);
|
||||
|
||||
//If an exception occurs when executing Rule 0, Rule 1 should still be applied
|
||||
verify(rule1).execute(facts);
|
||||
|
||||
//Rule 2 should be skipped since Rule 1 has been executed
|
||||
verify(rule2, never()).execute(facts);
|
||||
|
||||
}
|
||||
|
||||
private void setUpRule1() throws Exception {
|
||||
when(rule1.evaluate(facts)).thenReturn(true);
|
||||
final Exception exception = new Exception("fatal error!");
|
||||
doThrow(exception).when(rule1).execute(facts);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,68 @@
|
||||
/**
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2017, Mahmoud Ben Hassine (mahmoud.benhassine@icloud.com)
|
||||
*
|
||||
* 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.core;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
/**
|
||||
* Test class of "skip on first failed rule" parameter of Easy Rules default engine.
|
||||
*
|
||||
* @author Mahmoud Ben Hassine (mahmoud.benhassine@icloud.com)
|
||||
*/
|
||||
public class SkipOnFirstFailedRuleTest extends AbstractTest {
|
||||
|
||||
@Before
|
||||
public void setup() throws Exception {
|
||||
super.setup();
|
||||
setUpRule1();
|
||||
rulesEngine = RulesEngineBuilder.aNewRulesEngine()
|
||||
.withSkipOnFirstFailedRule(true)
|
||||
.build();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSkipOnFirstFailedRule() throws Exception {
|
||||
|
||||
rulesEngine.fire(rules, facts);
|
||||
|
||||
//Rule 1 should be executed
|
||||
verify(rule1).execute(facts);
|
||||
|
||||
//Rule 2 should be skipped since Rule 1 has failed
|
||||
verify(rule2, never()).execute(facts);
|
||||
|
||||
}
|
||||
|
||||
private void setUpRule1() throws Exception {
|
||||
when(rule1.getName()).thenReturn("r1");
|
||||
when(rule1.getPriority()).thenReturn(1);
|
||||
when(rule1.evaluate(facts)).thenReturn(true);
|
||||
final Exception exception = new Exception("fatal error!");
|
||||
doThrow(exception).when(rule1).execute(facts);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,66 @@
|
||||
/**
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2017, Mahmoud Ben Hassine (mahmoud.benhassine@icloud.com)
|
||||
*
|
||||
* 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.core;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
/**
|
||||
* Test class of "skip on first non triggered rule" parameter of Easy Rules default engine.
|
||||
*
|
||||
* @author Krzysztof Kozlowski (krzysztof.kozlowski@coderion.pl)
|
||||
*/
|
||||
public class SkipOnFirstNonTriggeredRuleTest extends AbstractTest {
|
||||
|
||||
@Before
|
||||
public void setup() throws Exception {
|
||||
super.setup();
|
||||
setUpRule1();
|
||||
rulesEngine = RulesEngineBuilder.aNewRulesEngine()
|
||||
.withSkipOnFirstNonTriggeredRule(true)
|
||||
.build();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSkipOnFirstNonTriggeredRule() throws Exception {
|
||||
|
||||
rulesEngine.fire(rules, facts);
|
||||
|
||||
//Rule1 is non triggered
|
||||
verify(rule1, never()).execute(facts);
|
||||
|
||||
//Rule 2 should be skipped since Rule 1 has not been executed
|
||||
verify(rule2, never()).execute(facts);
|
||||
|
||||
}
|
||||
|
||||
private void setUpRule1() throws Exception {
|
||||
when(rule1.getName()).thenReturn("r1");
|
||||
when(rule1.getPriority()).thenReturn(1);
|
||||
when(rule1.evaluate(facts)).thenReturn(false);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,95 @@
|
||||
/**
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2017, Mahmoud Ben Hassine (mahmoud.benhassine@icloud.com)
|
||||
*
|
||||
* 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.core;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
public class UtilsTest {
|
||||
|
||||
@Test
|
||||
public void findAnnotationWithClassWhereAnnotationIsPresent() {
|
||||
Annotation foo = Utils.findAnnotation(Foo.class, AnnotationIsPresent.class);
|
||||
|
||||
assertCorrectAnnotationIsFound(Foo.class, foo);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findAnnotationWithClassWhereAnnotationIsPresentViaMetaAnnotation() {
|
||||
Annotation foo = Utils.findAnnotation(Foo.class, AnnotationIsPresentViaMetaAnnotation.class);
|
||||
|
||||
assertCorrectAnnotationIsFound(Foo.class, foo);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findAnnotationWithClassWhereAnnotationIsNotPresent() {
|
||||
Annotation foo = Utils.findAnnotation(Foo.class, Object.class);
|
||||
|
||||
assertNull(foo);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isAnnotationPresentWithClassWhereAnnotationIsPresent() {
|
||||
assertTrue(Utils.isAnnotationPresent(Foo.class, AnnotationIsPresent.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isAnnotationPresentWithClassWhereAnnotationIsPresentViaMetaAnnotation() {
|
||||
assertTrue(Utils.isAnnotationPresent(Foo.class, AnnotationIsPresentViaMetaAnnotation.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isAnnotationPresentWithClassWhereAnnotationIsNotPresent() {
|
||||
assertFalse(Utils.isAnnotationPresent(Foo.class, Object.class));
|
||||
}
|
||||
|
||||
private static void assertCorrectAnnotationIsFound(
|
||||
Class<?> expectedAnnotationType, Annotation actualAnnotation) {
|
||||
|
||||
assertNotNull(actualAnnotation);
|
||||
assertEquals(expectedAnnotationType, actualAnnotation.annotationType());
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.TYPE)
|
||||
private @interface Foo {
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.TYPE)
|
||||
@Foo
|
||||
private @interface MetaFoo {
|
||||
}
|
||||
|
||||
@Foo
|
||||
private static final class AnnotationIsPresent {
|
||||
}
|
||||
|
||||
@MetaFoo
|
||||
private static final class AnnotationIsPresentViaMetaAnnotation {
|
||||
}
|
||||
}
|
@ -1,84 +1,84 @@
|
||||
@if "%DEBUG%" == "" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@rem Gradle startup script for Windows
|
||||
@rem
|
||||
@rem ##########################################################################
|
||||
|
||||
@rem Set local scope for the variables with windows NT shell
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%" == "" set DIRNAME=.
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS=
|
||||
|
||||
@rem Find java.exe
|
||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if "%ERRORLEVEL%" == "0" goto init
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:findJavaFromJavaHome
|
||||
set JAVA_HOME=%JAVA_HOME:"=%
|
||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto init
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:init
|
||||
@rem Get command-line arguments, handling Windows variants
|
||||
|
||||
if not "%OS%" == "Windows_NT" goto win9xME_args
|
||||
|
||||
:win9xME_args
|
||||
@rem Slurp the command line arguments.
|
||||
set CMD_LINE_ARGS=
|
||||
set _SKIP=2
|
||||
|
||||
:win9xME_args_slurp
|
||||
if "x%~1" == "x" goto execute
|
||||
|
||||
set CMD_LINE_ARGS=%*
|
||||
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||
|
||||
@rem Execute Gradle
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||
|
||||
:fail
|
||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||
rem the _cmd.exe /c_ return code!
|
||||
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||
exit /b 1
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
||||
:omega
|
||||
@if "%DEBUG%" == "" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@rem Gradle startup script for Windows
|
||||
@rem
|
||||
@rem ##########################################################################
|
||||
|
||||
@rem Set local scope for the variables with windows NT shell
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%" == "" set DIRNAME=.
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS=
|
||||
|
||||
@rem Find java.exe
|
||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if "%ERRORLEVEL%" == "0" goto init
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:findJavaFromJavaHome
|
||||
set JAVA_HOME=%JAVA_HOME:"=%
|
||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto init
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:init
|
||||
@rem Get command-line arguments, handling Windows variants
|
||||
|
||||
if not "%OS%" == "Windows_NT" goto win9xME_args
|
||||
|
||||
:win9xME_args
|
||||
@rem Slurp the command line arguments.
|
||||
set CMD_LINE_ARGS=
|
||||
set _SKIP=2
|
||||
|
||||
:win9xME_args_slurp
|
||||
if "x%~1" == "x" goto execute
|
||||
|
||||
set CMD_LINE_ARGS=%*
|
||||
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||
|
||||
@rem Execute Gradle
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||
|
||||
:fail
|
||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||
rem the _cmd.exe /c_ return code!
|
||||
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||
exit /b 1
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
||||
:omega
|
@ -1,9 +1,9 @@
|
||||
package org.easyrules.samples.fire.rules
|
||||
|
||||
import org.easyrules.annotation.Action
|
||||
import org.easyrules.annotation.Condition
|
||||
import org.easyrules.annotation.Rule
|
||||
import org.easyrules.annotation.Priority
|
||||
import Action
|
||||
import Condition
|
||||
import Rule
|
||||
import Priority
|
||||
|
||||
@Rule(description='All the fires are out, cancel the alarm')
|
||||
class CancelAlarmRule {
|
@ -1,9 +1,9 @@
|
||||
package org.easyrules.samples.fire.rules
|
||||
|
||||
import org.easyrules.annotation.Action
|
||||
import org.easyrules.annotation.Condition
|
||||
import org.easyrules.annotation.Rule
|
||||
import org.easyrules.annotation.Priority
|
||||
import Action
|
||||
import Condition
|
||||
import Rule
|
||||
import Priority
|
||||
|
||||
@Rule(description='No alarm, nothing to see here; This need to be last rule considered')
|
||||
class EverythingOKRule {
|
@ -1,9 +1,9 @@
|
||||
package org.easyrules.samples.fire.rules
|
||||
|
||||
import org.easyrules.annotation.Action
|
||||
import org.easyrules.annotation.Condition
|
||||
import org.easyrules.annotation.Rule
|
||||
import org.easyrules.annotation.Priority
|
||||
import Action
|
||||
import Condition
|
||||
import Rule
|
||||
import Priority
|
||||
|
||||
@Rule(description='The alarm is detected at the fire station')
|
||||
class ThereIsAnAlarmRule {
|
@ -1,9 +1,9 @@
|
||||
package org.easyrules.samples.fire.rules
|
||||
|
||||
import org.easyrules.annotation.Action
|
||||
import org.easyrules.annotation.Condition
|
||||
import org.easyrules.annotation.Rule
|
||||
import org.easyrules.annotation.Priority
|
||||
import Action
|
||||
import Condition
|
||||
import Rule
|
||||
import Priority
|
||||
|
||||
@Rule(description='The fires are out, turn off all of the sprinklers')
|
||||
class TurnSprinklerOffRule {
|
@ -1,9 +1,9 @@
|
||||
package org.easyrules.samples.fire.rules
|
||||
|
||||
import org.easyrules.annotation.Action
|
||||
import org.easyrules.annotation.Condition
|
||||
import org.easyrules.annotation.Rule
|
||||
import org.easyrules.annotation.Priority
|
||||
import Action
|
||||
import Condition
|
||||
import Rule
|
||||
import Priority
|
||||
|
||||
@Rule(description='A fire has been detected in a room, turn on the sprinkler in the room; Highest priority rule')
|
||||
class TurnSprinklerOnRule {
|
@ -1,9 +1,9 @@
|
||||
package org.easyrules.samples.fizzbuzz
|
||||
|
||||
import org.easyrules.annotation.Action
|
||||
import org.easyrules.annotation.Condition
|
||||
import org.easyrules.annotation.Priority
|
||||
import org.easyrules.annotation.Rule
|
||||
import Action
|
||||
import Condition
|
||||
import Priority
|
||||
import Rule
|
||||
|
||||
@Rule
|
||||
class BuzzRule {
|
@ -1,6 +1,6 @@
|
||||
package org.easyrules.samples.fizzbuzz
|
||||
|
||||
import org.easyrules.core.CompositeRule
|
||||
import CompositeRule
|
||||
|
||||
class FizzBuzzRule extends CompositeRule {
|
||||
|
@ -1,6 +1,6 @@
|
||||
package org.easyrules.samples.fizzbuzz
|
||||
|
||||
import org.easyrules.core.RulesEngineBuilder
|
||||
import RulesEngineBuilder
|
||||
|
||||
class FizzBuzzWithEasyRules {
|
||||
|
@ -1,9 +1,9 @@
|
||||
package org.easyrules.samples.fizzbuzz
|
||||
|
||||
import org.easyrules.annotation.Action
|
||||
import org.easyrules.annotation.Condition
|
||||
import org.easyrules.annotation.Priority
|
||||
import org.easyrules.annotation.Rule
|
||||
import Action
|
||||
import Condition
|
||||
import Priority
|
||||
import Rule
|
||||
|
||||
@Rule
|
||||
class FizzRule {
|
@ -1,9 +1,9 @@
|
||||
package org.easyrules.samples.fizzbuzz
|
||||
|
||||
import org.easyrules.annotation.Action
|
||||
import org.easyrules.annotation.Condition
|
||||
import org.easyrules.annotation.Priority
|
||||
import org.easyrules.annotation.Rule
|
||||
import Action
|
||||
import Condition
|
||||
import Priority
|
||||
import Rule
|
||||
|
||||
@Rule
|
||||
class NonFizzBuzzRule {
|
@ -1,8 +1,8 @@
|
||||
package org.easyrules.samples.helloworld
|
||||
|
||||
import org.easyrules.annotation.Action
|
||||
import org.easyrules.annotation.Condition
|
||||
import org.easyrules.annotation.Rule
|
||||
import Action
|
||||
import Condition
|
||||
import Rule
|
||||
|
||||
@Rule(name = "Hello World rule", description = "Say Hello to duke's friends only")
|
||||
class HelloWorldRule {
|
@ -1,6 +1,6 @@
|
||||
package org.easyrules.samples.helloworld
|
||||
|
||||
import static org.easyrules.core.RulesEngineBuilder.aNewRulesEngine
|
||||
import static RulesEngineBuilder.aNewRulesEngine
|
||||
import java.util.Scanner
|
||||
|
||||
class Launcher {
|
@ -1,6 +1,6 @@
|
||||
package org.easyrules.samples.scheduling
|
||||
|
||||
import org.easyrules.core.RulesEngineBuilder
|
||||
import RulesEngineBuilder
|
||||
import org.easyrules.quartz.RulesEngineScheduler
|
||||
|
||||
import java.util.Date
|
@ -1,8 +1,8 @@
|
||||
package org.easyrules.samples.scheduling
|
||||
|
||||
import org.easyrules.annotation.Action
|
||||
import org.easyrules.annotation.Condition
|
||||
import org.easyrules.annotation.Rule
|
||||
import Action
|
||||
import Condition
|
||||
import Rule
|
||||
|
||||
import java.util.Date
|
||||
|
@ -1,6 +1,6 @@
|
||||
package org.easyrules.samples.shop
|
||||
|
||||
import org.easyrules.core.BasicRule
|
||||
import BasicRule
|
||||
|
||||
public class AgeRule extends BasicRule {
|
||||
|
@ -1,8 +1,8 @@
|
||||
package org.easyrules.samples.shop
|
||||
|
||||
import static org.easyrules.core.RulesEngineBuilder.aNewRulesEngine
|
||||
import static RulesEngineBuilder.aNewRulesEngine
|
||||
|
||||
import org.easyrules.api.RulesEngine
|
||||
import RulesEngine
|
||||
|
||||
class Launcher {
|
||||
|
@ -1,8 +1,8 @@
|
||||
package org.easyrules.samples.shop
|
||||
|
||||
import org.easyrules.annotation.Action
|
||||
import org.easyrules.annotation.Condition
|
||||
import org.easyrules.core.BasicRule
|
||||
import Action
|
||||
import Condition
|
||||
import BasicRule
|
||||
|
||||
class OkToSellRule extends BasicRule {
|
||||
|
@ -1,8 +1,8 @@
|
||||
package org.easyrules.samples.shop
|
||||
|
||||
import org.easyrules.annotation.Action
|
||||
import org.easyrules.annotation.Condition
|
||||
import org.easyrules.core.BasicRule
|
||||
import Action
|
||||
import Condition
|
||||
import BasicRule
|
||||
|
||||
class UnderAgeRule extends BasicRule {
|
||||
|
@ -1,6 +1,6 @@
|
||||
package org.easyrules.samples.simple
|
||||
|
||||
import static org.easyrules.core.RulesEngineBuilder.aNewRulesEngine
|
||||
import static RulesEngineBuilder.aNewRulesEngine
|
||||
|
||||
class Launcher {
|
||||
|
@ -1,9 +1,9 @@
|
||||
package org.easyrules.samples.simple
|
||||
|
||||
import org.easyrules.annotation.Action
|
||||
import org.easyrules.annotation.Condition
|
||||
import org.easyrules.annotation.Priority
|
||||
import org.easyrules.annotation.Rule
|
||||
import Action
|
||||
import Condition
|
||||
import Priority
|
||||
import Rule
|
||||
|
||||
@Rule
|
||||
public class SimpleRule {
|
@ -1,8 +1,8 @@
|
||||
package org.easyrules.samples.spring
|
||||
|
||||
import org.easyrules.annotation.Action
|
||||
import org.easyrules.annotation.Condition
|
||||
import org.easyrules.annotation.Rule
|
||||
import Action
|
||||
import Condition
|
||||
import Rule
|
||||
|
||||
@Rule(name = "dummy rule")
|
||||
class DummyRule {
|
@ -1,6 +1,6 @@
|
||||
package org.easyrules.samples.spring
|
||||
|
||||
import org.easyrules.api.RulesEngine
|
||||
import RulesEngine
|
||||
import org.springframework.context.support.ClassPathXmlApplicationContext
|
||||
|
||||
class Launcher {
|
Loading…
Reference in New Issue