diff --git a/easy-rules-jexl/pom.xml b/easy-rules-jexl/pom.xml
new file mode 100644
index 0000000..fb9dc43
--- /dev/null
+++ b/easy-rules-jexl/pom.xml
@@ -0,0 +1,102 @@
+
+
+ 4.0.0
+
+ org.jeasy
+ easy-rules
+ 4.0.0-SNAPSHOT
+
+
+ easy-rules-jexl
+ jar
+ Easy Rules JEXL module
+ JEXL integration module
+
+
+ 3.1
+
+
+
+ git@github.com:j-easy/easy-rules.git
+ scm:git:git@github.com:j-easy/easy-rules.git
+ scm:git:git@github.com:j-easy/easy-rules.git
+ HEAD
+
+
+
+ GitHub
+ https://github.com/j-easy/easy-rules/issues
+
+
+
+ Travis CI
+ https://travis-ci.org/j-easy/easy-rules
+
+
+
+
+ benas
+ Mahmoud Ben Hassine
+ http://benas.github.io
+ mahmoud.benhassine@icloud.com
+
+ Lead developer
+
+
+
+
+
+
+ MIT License
+ http://opensource.org/licenses/mit-license.php
+
+
+
+
+
+ org.jeasy
+ easy-rules-support
+ ${project.version}
+
+
+ org.slf4j
+ slf4j-simple
+ ${slf4j-api.version}
+ test
+
+
+ org.apache.commons
+ commons-jexl3
+ ${jexl.version}
+
+
+ junit
+ junit
+ test
+
+
+ org.assertj
+ assertj-core
+ test
+
+
+ com.github.stefanbirkner
+ system-rules
+ ${system-rules.version}
+ test
+
+
+
+
+
+
+ com.mycila
+ license-maven-plugin
+
+ ${project.parent.basedir}/licence-header-template.txt
+
+
+
+
+
+
\ No newline at end of file
diff --git a/easy-rules-jexl/src/main/java/org/jeasy/rules/jexl/JexlAction.java b/easy-rules-jexl/src/main/java/org/jeasy/rules/jexl/JexlAction.java
new file mode 100644
index 0000000..ca2bf9b
--- /dev/null
+++ b/easy-rules-jexl/src/main/java/org/jeasy/rules/jexl/JexlAction.java
@@ -0,0 +1,66 @@
+/*
+ * The MIT License
+ *
+ * Copyright (c) 2020, 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.jexl;
+
+import java.util.Objects;
+
+import org.apache.commons.jexl3.JexlEngine;
+import org.apache.commons.jexl3.JexlException;
+import org.apache.commons.jexl3.JexlScript;
+import org.apache.commons.jexl3.MapContext;
+import org.jeasy.rules.api.Action;
+import org.jeasy.rules.api.Facts;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class JexlAction implements Action {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(JexlAction.class);
+
+ private final JexlScript compiledScript;
+ private final String expression;
+
+ public JexlAction(String expression) {
+ this.expression = Objects.requireNonNull(expression, "expression cannot be null");
+ this.compiledScript = JexlRule.DEFAULT_JEXL.createScript(expression);
+ }
+
+ public JexlAction(String expression, JexlEngine jexl) {
+ this.expression = Objects.requireNonNull(expression, "expression cannot be null");
+ Objects.requireNonNull(jexl, "jexl cannot be null");
+ this.compiledScript = jexl.createScript(expression);
+ }
+
+ @Override
+ public void execute(Facts facts) throws Exception {
+ Objects.requireNonNull(facts, "facts cannot be null");
+ MapContext ctx = new MapContext(facts.asMap());
+ try {
+ compiledScript.execute(ctx);
+ } catch (JexlException e) {
+ LOGGER.error("Unable to evaluate expression: '" + expression + "' on facts: " + facts, e);
+ throw e;
+ }
+ }
+}
diff --git a/easy-rules-jexl/src/main/java/org/jeasy/rules/jexl/JexlCondition.java b/easy-rules-jexl/src/main/java/org/jeasy/rules/jexl/JexlCondition.java
new file mode 100644
index 0000000..3abd78b
--- /dev/null
+++ b/easy-rules-jexl/src/main/java/org/jeasy/rules/jexl/JexlCondition.java
@@ -0,0 +1,65 @@
+/*
+ * The MIT License
+ *
+ * Copyright (c) 2020, 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.jexl;
+
+import java.util.Objects;
+
+import org.apache.commons.jexl3.JexlEngine;
+import org.apache.commons.jexl3.JexlScript;
+import org.apache.commons.jexl3.MapContext;
+import org.jeasy.rules.api.Condition;
+import org.jeasy.rules.api.Facts;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class JexlCondition implements Condition {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(JexlCondition.class);
+
+ private final JexlScript compiledScript;
+ private final String expression;
+
+ public JexlCondition(String expression) {
+ this.expression = Objects.requireNonNull(expression, "expression cannot be null");
+ this.compiledScript = JexlRule.DEFAULT_JEXL.createScript(expression);
+ }
+
+ public JexlCondition(String expression, JexlEngine jexl) {
+ this.expression = Objects.requireNonNull(expression, "expression cannot be null");
+ Objects.requireNonNull(jexl, "jexl cannot be null");
+ this.compiledScript = jexl.createScript(expression);
+ }
+
+ @Override
+ public boolean evaluate(Facts facts) {
+ Objects.requireNonNull(facts, "facts cannot be null");
+ MapContext ctx = new MapContext(facts.asMap());
+ try {
+ return (Boolean) compiledScript.execute(ctx);
+ } catch (Exception e) {
+ LOGGER.error("Unable to evaluate expression: '" + expression + "' on facts: " + facts, e);
+ return false;
+ }
+ }
+}
diff --git a/easy-rules-jexl/src/main/java/org/jeasy/rules/jexl/JexlRule.java b/easy-rules-jexl/src/main/java/org/jeasy/rules/jexl/JexlRule.java
new file mode 100644
index 0000000..2791769
--- /dev/null
+++ b/easy-rules-jexl/src/main/java/org/jeasy/rules/jexl/JexlRule.java
@@ -0,0 +1,107 @@
+/*
+ * The MIT License
+ *
+ * Copyright (c) 2020, 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.jexl;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+import org.apache.commons.jexl3.JexlBuilder;
+import org.apache.commons.jexl3.JexlEngine;
+import org.jeasy.rules.api.Action;
+import org.jeasy.rules.api.Condition;
+import org.jeasy.rules.api.Facts;
+import org.jeasy.rules.api.Rule;
+import org.jeasy.rules.core.BasicRule;
+
+public class JexlRule extends BasicRule {
+
+ static final JexlEngine DEFAULT_JEXL = new JexlBuilder().create();
+
+ private Condition condition = Condition.FALSE;
+ private final List actions = new ArrayList<>();
+ private final JexlEngine jexl;
+
+ public JexlRule() {
+ this(DEFAULT_JEXL);
+ }
+
+ public JexlRule(JexlEngine jexl) {
+ super(Rule.DEFAULT_NAME, Rule.DEFAULT_DESCRIPTION, Rule.DEFAULT_PRIORITY);
+ this.jexl = Objects.requireNonNull(jexl, "jexl cannot be null");
+ }
+
+ public JexlRule name(String name) {
+ this.name = Objects.requireNonNull(name, "name cannot be null");
+ return this;
+ }
+
+ public JexlRule description(String description) {
+ this.description = Objects.requireNonNull(description, "description cannot be null");
+ return this;
+ }
+
+ public JexlRule priority(int priority) {
+ this.priority = priority;
+ return this;
+ }
+
+ public JexlRule when(String condition) {
+ Objects.requireNonNull(condition, "condition cannot be null");
+ return when(condition, jexl);
+ }
+
+ public JexlRule when(String condition, JexlEngine jexl) {
+ Objects.requireNonNull(condition, "condition cannot be null");
+ Objects.requireNonNull(jexl, "jexl cannot be null");
+ this.condition = new JexlCondition(condition, jexl);
+ return this;
+ }
+
+ public JexlRule then(String action) {
+ Objects.requireNonNull(action, "action cannot be null");
+ return then(action, jexl);
+ }
+
+ public JexlRule then(String action, JexlEngine jexl) {
+ Objects.requireNonNull(action, "action cannot be null");
+ Objects.requireNonNull(jexl, "jexl cannot be null");
+ this.actions.add(new JexlAction(action, jexl));
+ return this;
+ }
+
+ @Override
+ public boolean evaluate(Facts facts) {
+ Objects.requireNonNull(facts, "facts cannot be null");
+ return condition.evaluate(facts);
+ }
+
+ @Override
+ public void execute(Facts facts) throws Exception {
+ Objects.requireNonNull(facts, "facts cannot be null");
+ for (Action action : actions) {
+ action.execute(facts);
+ }
+ }
+}
diff --git a/easy-rules-jexl/src/main/java/org/jeasy/rules/jexl/JexlRuleFactory.java b/easy-rules-jexl/src/main/java/org/jeasy/rules/jexl/JexlRuleFactory.java
new file mode 100644
index 0000000..0e0ed25
--- /dev/null
+++ b/easy-rules-jexl/src/main/java/org/jeasy/rules/jexl/JexlRuleFactory.java
@@ -0,0 +1,90 @@
+/*
+ * The MIT License
+ *
+ * Copyright (c) 2020, 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.jexl;
+
+import java.io.Reader;
+import java.util.List;
+import java.util.Objects;
+
+import org.apache.commons.jexl3.JexlEngine;
+import org.jeasy.rules.api.Rule;
+import org.jeasy.rules.api.Rules;
+import org.jeasy.rules.support.AbstractRuleFactory;
+import org.jeasy.rules.support.RuleDefinition;
+import org.jeasy.rules.support.RuleDefinitionReader;
+
+public class JexlRuleFactory extends AbstractRuleFactory {
+
+ private RuleDefinitionReader reader;
+
+ public JexlRuleFactory(RuleDefinitionReader reader) {
+ this.reader = Objects.requireNonNull(reader, "reader cannot be null");
+ }
+
+ public Rule createRule(Reader ruleDescriptor) throws Exception {
+ Objects.requireNonNull(ruleDescriptor, "ruleDescriptor cannot be null");
+ return createRule(ruleDescriptor, JexlRule.DEFAULT_JEXL);
+ }
+
+ public Rule createRule(Reader ruleDescriptor, JexlEngine jexl) throws Exception {
+ Objects.requireNonNull(ruleDescriptor, "ruleDescriptor cannot be null");
+ Objects.requireNonNull(jexl, "jexl cannot be null");
+ List ruleDefinitions = reader.read(ruleDescriptor);
+ if (ruleDefinitions.isEmpty()) {
+ throw new IllegalArgumentException("rule descriptor is empty");
+ }
+ return createRule(ruleDefinitions.get(0), jexl);
+ }
+
+ public Rules createRules(Reader rulesDescriptor) throws Exception {
+ Objects.requireNonNull(rulesDescriptor, "rulesDescriptor cannot be null");
+ return createRules(rulesDescriptor, JexlRule.DEFAULT_JEXL);
+ }
+
+ public Rules createRules(Reader rulesDescriptor, JexlEngine jexl) throws Exception {
+ Objects.requireNonNull(rulesDescriptor, "rulesDescriptor cannot be null");
+ Objects.requireNonNull(jexl, "jexl cannot be null");
+ Rules rules = new Rules();
+ List ruleDefinitions = reader.read(rulesDescriptor);
+ for (RuleDefinition ruleDefinition : ruleDefinitions) {
+ rules.register(createRule(ruleDefinition, jexl));
+ }
+ return rules;
+ }
+
+ @Override
+ protected Rule createSimpleRule(RuleDefinition ruleDefinition, JexlEngine jexl) {
+ Objects.requireNonNull(ruleDefinition, "ruleDefinition cannot be null");
+ Objects.requireNonNull(jexl, "jexl cannot be null");
+ JexlRule rule = new JexlRule()
+ .name(ruleDefinition.getName())
+ .description(ruleDefinition.getDescription())
+ .priority(ruleDefinition.getPriority())
+ .when(ruleDefinition.getCondition(), jexl);
+ for (String action : ruleDefinition.getActions()) {
+ rule.then(action, jexl);
+ }
+ return rule;
+ }
+}
diff --git a/easy-rules-jexl/src/main/java/org/jeasy/rules/jexl/package-info.java b/easy-rules-jexl/src/main/java/org/jeasy/rules/jexl/package-info.java
new file mode 100644
index 0000000..cb1c7ce
--- /dev/null
+++ b/easy-rules-jexl/src/main/java/org/jeasy/rules/jexl/package-info.java
@@ -0,0 +1,27 @@
+/*
+ * The MIT License
+ *
+ * Copyright (c) 2020, 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 classes to support JEXL.
+ */
+package org.jeasy.rules.jexl;
diff --git a/easy-rules-jexl/src/test/java/org/jeasy/rules/jexl/JexlActionTest.java b/easy-rules-jexl/src/test/java/org/jeasy/rules/jexl/JexlActionTest.java
new file mode 100644
index 0000000..7b99721
--- /dev/null
+++ b/easy-rules-jexl/src/test/java/org/jeasy/rules/jexl/JexlActionTest.java
@@ -0,0 +1,219 @@
+/*
+ * The MIT License
+ *
+ * Copyright (c) 2020, 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.jexl;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.Assert.fail;
+
+import java.io.File;
+import java.util.UUID;
+
+import org.apache.commons.jexl3.JexlBuilder;
+import org.apache.commons.jexl3.JexlEngine;
+import org.apache.commons.jexl3.JexlException;
+import org.apache.commons.jexl3.introspection.JexlSandbox;
+import org.jeasy.rules.api.Action;
+import org.jeasy.rules.api.Facts;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.contrib.java.lang.system.SystemOutRule;
+import org.junit.rules.ExpectedException;
+
+public class JexlActionTest {
+
+ @Rule
+ public final SystemOutRule systemOutRule = new SystemOutRule().enableLog();
+
+ @SuppressWarnings("deprecation")
+ @Rule
+ public final ExpectedException expectedException = ExpectedException.none();
+
+ @Test
+ public void testMVELActionExecution() throws Exception {
+ // given
+ Action markAsAdult = new JexlAction("person.setAdult(true);");
+ Facts facts = new Facts();
+ Person foo = new Person("foo", 20);
+ facts.put("person", foo);
+
+ // when
+ markAsAdult.execute(facts);
+
+ // then
+ assertThat(foo.isAdult()).isTrue();
+ }
+
+ @Test
+ public void testJexlFunctionExecution() throws Exception {
+ // given
+ Action printAction = new JexlAction("var hello = function() { System.out.println(\"Hello from JEXL!\"); }; hello();");
+ Facts facts = new Facts();
+ facts.put("System", System.class);
+
+ // when
+ printAction.execute(facts);
+
+ // then
+ assertThat(systemOutRule.getLog()).contains("Hello from JEXL!");
+ }
+
+ @Test
+ public void testMVELActionExecutionWithFailure() throws Exception {
+ // given
+ expectedException.expect(JexlException.Method.class);
+ expectedException.expectMessage("org.jeasy.rules.jexl.JexlAction.@1:7 unsolvable function/method 'setBlah'");
+ Action action = new JexlAction("person.setBlah(true);");
+ Facts facts = new Facts();
+ Person foo = new Person("foo", 20);
+ facts.put("person", foo);
+
+ // when
+ action.execute(facts);
+
+ // then
+ // excepted exception
+ }
+
+ @Test
+ public void testJexlActionWithExpressionAndFacts() throws Exception {
+ // given
+ JexlEngine jexl = new JexlBuilder()
+ .create();
+ Action printAction = new JexlAction(
+ "var random = function() { System.out.println(\"Random from JEXL = \" + new('java.util.Random', 123).nextInt(10)); }; random();",
+ jexl);
+ Facts facts = new Facts();
+ facts.put("System", System.class);
+
+ // when
+ printAction.execute(facts);
+
+ // then
+ assertThat(systemOutRule.getLog()).contains("Random from JEXL = 2");
+ }
+
+ @Test
+ public void testWithBlackSanbox() throws Exception {
+ JexlSandbox sandbox = new JexlSandbox(false);
+ sandbox.white(System.class.getName()).execute("currentTimeMillis");
+ JexlEngine jexl = new JexlBuilder()
+ .sandbox(sandbox)
+ .create();
+
+ Facts facts = new Facts();
+ facts.put("result", null);
+ facts.put("System", System.class);
+ facts.put("UUID", UUID.class);
+
+ long now = System.currentTimeMillis();
+ new JexlAction("result = System.currentTimeMillis()", jexl).execute(facts);
+ Object result = facts.get("result");
+ assertThat(result).isInstanceOf(Long.class);
+ assertThat((long) result).isGreaterThanOrEqualTo(now);
+
+ try {
+ new JexlAction("result = System.nanoTime()", jexl).execute(facts);
+ fail("Exception expected");
+ } catch (Exception e) {
+ assertThat(e).isInstanceOf(JexlException.Method.class);
+ }
+
+ try {
+ new JexlAction("result = new('java.lang.Double', 10)", jexl).execute(facts);
+ fail("Exception expected");
+ } catch (Exception e) {
+ assertThat(e).isInstanceOf(JexlException.Method.class);
+ }
+
+ try {
+ new JexlAction("result = new('java.io.File', '/tmp/jexl-sandbox-test')", jexl).execute(facts);
+ fail("Exception expected");
+ } catch (Exception e) {
+ assertThat(e).isInstanceOf(JexlException.Method.class);
+ }
+
+ try {
+ new JexlAction("result = UUID.randomUUID()", jexl).execute(facts);
+ fail("Exception expected");
+ } catch (Exception e) {
+ assertThat(e).isInstanceOf(JexlException.Method.class);
+ }
+
+ try {
+ new JexlAction("result = Math.PI", jexl).execute(facts);
+ fail("Exception expected");
+ } catch (Exception e) {
+ assertThat(e).isInstanceOf(JexlException.Variable.class);
+ }
+ }
+
+ @Test
+ public void testWithWhiteSanbox() throws Exception {
+ JexlSandbox sandbox = new JexlSandbox(true);
+ sandbox.black(System.class.getName()).execute("nanoTime");
+ JexlEngine jexl = new JexlBuilder()
+ .sandbox(sandbox)
+ .create();
+
+ Facts facts = new Facts();
+ facts.put("result", null);
+ facts.put("System", System.class);
+ facts.put("UUID", UUID.class);
+
+ long now = System.currentTimeMillis();
+ new JexlAction("result = System.currentTimeMillis()", jexl).execute(facts);
+ Object result = facts.get("result");
+ assertThat(result).isInstanceOf(Long.class);
+ assertThat((long) result).isGreaterThanOrEqualTo(now);
+
+ try {
+ new JexlAction("result = System.nanoTime()", jexl).execute(facts);
+ fail("Exception expected");
+ } catch (Exception e) {
+ assertThat(e).isInstanceOf(JexlException.Method.class);
+ }
+
+ new JexlAction("result = new('java.lang.Double', 10)", jexl).execute(facts);
+ result = facts.get("result");
+ assertThat(result).isInstanceOf(Double.class);
+ assertThat((double) result).isEqualTo(10.0);
+
+ new JexlAction("result = new('java.io.File', '/tmp/jexl-sandbox-test')", jexl).execute(facts);
+ result = facts.get("result");
+ assertThat(result).isInstanceOf(File.class);
+ assertThat(((File) result).exists()).isFalse();
+ assertThat(((File) result).getName()).isEqualTo("jexl-sandbox-test");
+
+ new JexlAction("result = UUID.randomUUID()", jexl).execute(facts);
+ result = facts.get("result");
+ assertThat(result).isInstanceOf(UUID.class);
+
+ try {
+ new JexlAction("result = Math.PI", jexl).execute(facts);
+ fail("Exception expected");
+ } catch (Exception e) {
+ assertThat(e).isInstanceOf(JexlException.Variable.class);
+ }
+ }
+}
diff --git a/easy-rules-jexl/src/test/java/org/jeasy/rules/jexl/JexlConditionTest.java b/easy-rules-jexl/src/test/java/org/jeasy/rules/jexl/JexlConditionTest.java
new file mode 100644
index 0000000..6ea38d0
--- /dev/null
+++ b/easy-rules-jexl/src/test/java/org/jeasy/rules/jexl/JexlConditionTest.java
@@ -0,0 +1,76 @@
+/*
+ * The MIT License
+ *
+ * Copyright (c) 2020, 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.jexl;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.util.Random;
+
+import org.jeasy.rules.api.Condition;
+import org.jeasy.rules.api.Facts;
+import org.junit.Test;
+
+public class JexlConditionTest {
+
+ @Test
+ public void testJexlExpressionEvaluation() {
+ // given
+ Condition isAdult = new JexlCondition("person.age > 18");
+ Facts facts = new Facts();
+ facts.put("person", new Person("foo", 20));
+
+ // when
+ boolean evaluationResult = isAdult.evaluate(facts);
+
+ // then
+ assertThat(evaluationResult).isTrue();
+ }
+
+ @Test
+ public void whenDeclaredFactIsNotPresent_thenShouldReturnFalse() {
+ // given
+ Condition isHot = new JexlCondition("temperature > 30");
+ Facts facts = new Facts();
+
+ // when
+ boolean evaluationResult = isHot.evaluate(facts);
+
+ // then
+ assertThat(evaluationResult).isFalse();
+ }
+
+ @Test
+ public void testJexlConditionWithFacts() {
+ // given
+ Condition condition = new JexlCondition("return rnd.nextBoolean();");
+ Facts facts = new Facts();
+ facts.put("rnd", new Random(123));
+
+ // when
+ boolean evaluationResult = condition.evaluate(facts);
+
+ // then
+ assertThat(evaluationResult).isTrue();
+ }
+}
diff --git a/easy-rules-jexl/src/test/java/org/jeasy/rules/jexl/JexlRuleFactoryTest.java b/easy-rules-jexl/src/test/java/org/jeasy/rules/jexl/JexlRuleFactoryTest.java
new file mode 100644
index 0000000..3679892
--- /dev/null
+++ b/easy-rules-jexl/src/test/java/org/jeasy/rules/jexl/JexlRuleFactoryTest.java
@@ -0,0 +1,191 @@
+/*
+ * The MIT License
+ *
+ * Copyright (c) 2020, 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.jexl;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.io.File;
+import java.io.FileReader;
+import java.io.Reader;
+import java.io.StringReader;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Iterator;
+
+import org.jeasy.rules.api.Rule;
+import org.jeasy.rules.api.Rules;
+import org.jeasy.rules.support.JsonRuleDefinitionReader;
+import org.jeasy.rules.support.UnitRuleGroup;
+import org.jeasy.rules.support.YamlRuleDefinitionReader;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class JexlRuleFactoryTest {
+
+ @Parameters
+ public static Collection