Add support for annotation programming model, rules can be simple annotated POJOs

pull/3/head
Mahmoud Ben Hassine 11 years ago
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;
}

@ -22,31 +22,7 @@
* THE SOFTWARE.
*/
package io.github.benas.easyrules.api;
import javax.management.MXBean;
/**
* Abstraction for a priority rule : rules registered in a PriorityRulesEngine will be fired
* according to their natural order.<br/>
*
* Priority rules are by default manageable via JMX to allow changing their priorities dynamically at runtime.
*
* @author Mahmoud Ben Hassine (md.benhassine@gmail.com)
* This package contains Easy Rules annotations.
*/
@MXBean
public interface PriorityRule extends Rule {
/**
* Getter for rule priority.
* @return rule priority
*/
int getPriority();
/**
* Setter for rule priority.
* @param priority the priority to set
*/
void setPriority(int priority);
}
package io.github.benas.easyrules.annotation;

@ -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…
Cancel
Save