Add support for annotation programming model, rules can be simple annotated POJOs
parent
dd167ed24f
commit
21a2bc327e
@ -0,0 +1,25 @@
|
||||
package io.github.benas.easyrules.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* 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 (md.benhassine@gmail.com)
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.METHOD)
|
||||
public @interface Action {
|
||||
|
||||
/**
|
||||
* The order in which the action should be executed.
|
||||
* @return he order in which the action should be executed
|
||||
*/
|
||||
int order() default 0;
|
||||
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
package io.github.benas.easyrules.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* 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 (md.benhassine@gmail.com)
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.METHOD)
|
||||
public @interface Condition {
|
||||
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
package io.github.benas.easyrules.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* 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 (md.benhassine@gmail.com)
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.METHOD)
|
||||
public @interface Priority {
|
||||
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
package io.github.benas.easyrules.annotation;
|
||||
|
||||
import io.github.benas.easyrules.util.EasyRulesConstants;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Annotation to mark a class as a rule.
|
||||
*
|
||||
* @author Mahmoud Ben Hassine (md.benhassine@gmail.com)
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.TYPE)
|
||||
public @interface Rule {
|
||||
|
||||
/**
|
||||
* The rule name which must be unique within an rules registry.
|
||||
* @return The rule name
|
||||
*/
|
||||
public String name() default EasyRulesConstants.DEFAULT_RULE_NAME;
|
||||
|
||||
/**
|
||||
* The rule description.
|
||||
* @return The rule description
|
||||
*/
|
||||
public String description() default EasyRulesConstants.DEFAULT_RULE_DESCRIPTION;
|
||||
|
||||
}
|
@ -0,0 +1,55 @@
|
||||
package io.github.benas.easyrules.core.test.annotation;
|
||||
|
||||
import io.github.benas.easyrules.core.AnnotatedRulesEngine;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
/**
|
||||
* Test class for annotated rules engine execution.
|
||||
*
|
||||
* @author Mahmoud Ben Hassine (md.benhassine@gmail.com)
|
||||
*/
|
||||
public class AnnotatedRulesEngineTest {
|
||||
|
||||
private SimpleAnnotatedRule simpleAnnotatedRule;
|
||||
|
||||
private AnnotatedRulesEngine rulesEngine;
|
||||
|
||||
@Before
|
||||
public void setup(){
|
||||
simpleAnnotatedRule = new SimpleAnnotatedRule();
|
||||
rulesEngine = new AnnotatedRulesEngine();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void wellAnnotatedRuleShouldBeExecuted() {
|
||||
rulesEngine.registerRule(simpleAnnotatedRule);
|
||||
rulesEngine.fireRules();
|
||||
//The annotated rule should be executed
|
||||
assertEquals(true, simpleAnnotatedRule.isExecuted());
|
||||
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void notAnnotatedRuleMustNotBeAccepted() {
|
||||
//an exception should be throw at rule registration time
|
||||
rulesEngine.registerRule(new Object());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void actionsMustBeExecutedInTheDefinedOrder() {
|
||||
rulesEngine.registerRule(simpleAnnotatedRule);
|
||||
rulesEngine.fireRules();
|
||||
//Actions must be executed in the defined order
|
||||
assertEquals("012", simpleAnnotatedRule.getActionSequence());
|
||||
}
|
||||
|
||||
@After
|
||||
public void clearRules() {
|
||||
rulesEngine.clearRules();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
package io.github.benas.easyrules.core.test.annotation;
|
||||
|
||||
import io.github.benas.easyrules.core.test.annotation.action.ActionMethodDefinitionTest;
|
||||
import io.github.benas.easyrules.core.test.annotation.condition.ConditionMethodDefinitionTest;
|
||||
import io.github.benas.easyrules.core.test.annotation.priority.PriorityMethodDefinitionTest;
|
||||
import junit.framework.TestSuite;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.Suite;
|
||||
|
||||
/**
|
||||
* Test suite for Easy Rules annotations.
|
||||
*
|
||||
* @author Mahmoud Ben Hassine (md.benhassine@gmail.com)
|
||||
*/
|
||||
|
||||
@RunWith(Suite.class)
|
||||
@Suite.SuiteClasses( {
|
||||
ActionMethodDefinitionTest.class,
|
||||
ConditionMethodDefinitionTest.class,
|
||||
PriorityMethodDefinitionTest.class,
|
||||
AnnotatedRulesEngineTest.class})
|
||||
public class EasyRulesAnnotationTestSuite extends TestSuite {
|
||||
|
||||
}
|
@ -0,0 +1,55 @@
|
||||
package io.github.benas.easyrules.core.test.annotation;
|
||||
|
||||
import io.github.benas.easyrules.annotation.Action;
|
||||
import io.github.benas.easyrules.annotation.Condition;
|
||||
import io.github.benas.easyrules.annotation.Rule;
|
||||
|
||||
@Rule(name = "myRule", description = "my rule description")
|
||||
public class SimpleAnnotatedRule {
|
||||
|
||||
private boolean executed;
|
||||
|
||||
private String actionSequence = "";
|
||||
|
||||
@Condition
|
||||
public boolean when() {
|
||||
return condition1() && condition2() || condition3();
|
||||
}
|
||||
|
||||
private boolean condition1() {
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean condition2() {
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean condition3() {
|
||||
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;
|
||||
}
|
||||
|
||||
public boolean isExecuted() {
|
||||
return executed;
|
||||
}
|
||||
|
||||
public String getActionSequence() {
|
||||
return actionSequence;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
package io.github.benas.easyrules.core.test.annotation.action;
|
||||
|
||||
import io.github.benas.easyrules.core.AnnotatedRulesEngine;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* Test class for action method definition.
|
||||
*
|
||||
* @author Mahmoud Ben Hassine (md.benhassine@gmail.com)
|
||||
*/
|
||||
public class ActionMethodDefinitionTest {
|
||||
|
||||
private AnnotatedRulesEngine rulesEngine;
|
||||
|
||||
@Before
|
||||
public void setup(){
|
||||
rulesEngine = new AnnotatedRulesEngine();
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void actionMethodMustBeDefined() {
|
||||
//an exception should be throw at rule registration time
|
||||
rulesEngine.registerRule(new AnnotatedRuleWithoutActionMethod());
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void actionMethodMustBePublic() {
|
||||
//an exception should be throw at rule registration time
|
||||
rulesEngine.registerRule(new AnnotatedRuleWithNotPublicActionMethod());
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void actionMethodMustHaveNoArguments() {
|
||||
//an exception should be throw at rule registration time
|
||||
rulesEngine.registerRule(new AnnotatedRuleWithActionMethodHavingArguments());
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
package io.github.benas.easyrules.core.test.annotation.action;
|
||||
|
||||
import io.github.benas.easyrules.annotation.Action;
|
||||
import io.github.benas.easyrules.annotation.Condition;
|
||||
import io.github.benas.easyrules.annotation.Rule;
|
||||
|
||||
@Rule
|
||||
public class AnnotatedRuleWithActionMethodHavingArguments {
|
||||
|
||||
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,26 @@
|
||||
package io.github.benas.easyrules.core.test.annotation.action;
|
||||
|
||||
import io.github.benas.easyrules.annotation.Action;
|
||||
import io.github.benas.easyrules.annotation.Condition;
|
||||
import io.github.benas.easyrules.annotation.Rule;
|
||||
|
||||
@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,24 @@
|
||||
package io.github.benas.easyrules.core.test.annotation.action;
|
||||
|
||||
import io.github.benas.easyrules.annotation.Condition;
|
||||
import io.github.benas.easyrules.annotation.Rule;
|
||||
|
||||
@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,26 @@
|
||||
package io.github.benas.easyrules.core.test.annotation.condition;
|
||||
|
||||
import io.github.benas.easyrules.annotation.Action;
|
||||
import io.github.benas.easyrules.annotation.Condition;
|
||||
import io.github.benas.easyrules.annotation.Rule;
|
||||
|
||||
@Rule
|
||||
public class AnnotatedRuleWithConditionMethodHavingArguments {
|
||||
|
||||
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,26 @@
|
||||
package io.github.benas.easyrules.core.test.annotation.condition;
|
||||
|
||||
import io.github.benas.easyrules.annotation.Action;
|
||||
import io.github.benas.easyrules.annotation.Condition;
|
||||
import io.github.benas.easyrules.annotation.Rule;
|
||||
|
||||
@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,26 @@
|
||||
package io.github.benas.easyrules.core.test.annotation.condition;
|
||||
|
||||
import io.github.benas.easyrules.annotation.Action;
|
||||
import io.github.benas.easyrules.annotation.Condition;
|
||||
import io.github.benas.easyrules.annotation.Rule;
|
||||
|
||||
@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,24 @@
|
||||
package io.github.benas.easyrules.core.test.annotation.condition;
|
||||
|
||||
import io.github.benas.easyrules.annotation.Action;
|
||||
import io.github.benas.easyrules.annotation.Rule;
|
||||
|
||||
@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,47 @@
|
||||
package io.github.benas.easyrules.core.test.annotation.condition;
|
||||
|
||||
import io.github.benas.easyrules.core.AnnotatedRulesEngine;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* Test class for condition method definition.
|
||||
*
|
||||
* @author Mahmoud Ben Hassine (md.benhassine@gmail.com)
|
||||
*/
|
||||
public class ConditionMethodDefinitionTest {
|
||||
|
||||
private AnnotatedRulesEngine rulesEngine;
|
||||
|
||||
@Before
|
||||
public void setup(){
|
||||
rulesEngine = new AnnotatedRulesEngine();
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void conditionMethodMustBeDefined() {
|
||||
//an exception should be throw at rule registration time
|
||||
rulesEngine.registerRule(new AnnotatedRuleWithoutCondition());
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void conditionMethodMustBePublic() {
|
||||
//an exception should be throw at rule registration time
|
||||
rulesEngine.registerRule(new AnnotatedRuleWithNotPublicConditionMethod());
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void conditionMethodMustHaveNoArguments() {
|
||||
//an exception should be throw at rule registration time
|
||||
rulesEngine.registerRule(new AnnotatedRuleWithConditionMethodHavingArguments());
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void conditionMethodMustReturnBooleanType() {
|
||||
//an exception should be throw at rule registration time
|
||||
rulesEngine.registerRule(new AnnotatedRuleWithConditionMethodHavingNonBooleanReturnType());
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
package io.github.benas.easyrules.core.test.annotation.priority;
|
||||
|
||||
import io.github.benas.easyrules.annotation.Action;
|
||||
import io.github.benas.easyrules.annotation.Condition;
|
||||
import io.github.benas.easyrules.annotation.Priority;
|
||||
import io.github.benas.easyrules.annotation.Rule;
|
||||
|
||||
@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,32 @@
|
||||
package io.github.benas.easyrules.core.test.annotation.priority;
|
||||
|
||||
import io.github.benas.easyrules.annotation.Action;
|
||||
import io.github.benas.easyrules.annotation.Condition;
|
||||
import io.github.benas.easyrules.annotation.Priority;
|
||||
import io.github.benas.easyrules.annotation.Rule;
|
||||
|
||||
@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,32 @@
|
||||
package io.github.benas.easyrules.core.test.annotation.priority;
|
||||
|
||||
import io.github.benas.easyrules.annotation.Action;
|
||||
import io.github.benas.easyrules.annotation.Condition;
|
||||
import io.github.benas.easyrules.annotation.Priority;
|
||||
import io.github.benas.easyrules.annotation.Rule;
|
||||
|
||||
@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,32 @@
|
||||
package io.github.benas.easyrules.core.test.annotation.priority;
|
||||
|
||||
import io.github.benas.easyrules.annotation.Action;
|
||||
import io.github.benas.easyrules.annotation.Condition;
|
||||
import io.github.benas.easyrules.annotation.Priority;
|
||||
import io.github.benas.easyrules.annotation.Rule;
|
||||
|
||||
@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,45 @@
|
||||
package io.github.benas.easyrules.core.test.annotation.priority;
|
||||
|
||||
import io.github.benas.easyrules.core.AnnotatedRulesEngine;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* Test class for action method definition.
|
||||
*
|
||||
* @author Mahmoud Ben Hassine (md.benhassine@gmail.com)
|
||||
*/
|
||||
public class PriorityMethodDefinitionTest {
|
||||
|
||||
private AnnotatedRulesEngine rulesEngine;
|
||||
|
||||
@Before
|
||||
public void setup(){
|
||||
rulesEngine = new AnnotatedRulesEngine();
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void priorityMethodMustBeUnique() {
|
||||
//an exception should be throw at rule registration time
|
||||
rulesEngine.registerRule(new AnnotatedRuleWithMoreThanOnePriorityMethod());
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void priorityMethodMustBePublic() {
|
||||
//an exception should be throw at rule registration time
|
||||
rulesEngine.registerRule(new AnnotatedRuleWithNotPublicPriorityMethod());
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void priorityMethodMustHaveNoArguments() {
|
||||
//an exception should be throw at rule registration time
|
||||
rulesEngine.registerRule(new AnnotatedRuleWithPriorityMethodHavingArguments());
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void priorityMethodReturnTypeMustBeInteger() {
|
||||
//an exception should be throw at rule registration time
|
||||
rulesEngine.registerRule(new AnnotatedRuleWithPriorityMethodHavingNonIntegerReturnType());
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue