diff --git a/core/src/main/java/com/hortonworks/iotas/common/IotasEventImpl.java b/core/src/main/java/com/hortonworks/iotas/common/IotasEventImpl.java index 59f166aff..0f17a7639 100644 --- a/core/src/main/java/com/hortonworks/iotas/common/IotasEventImpl.java +++ b/core/src/main/java/com/hortonworks/iotas/common/IotasEventImpl.java @@ -1,7 +1,6 @@ package com.hortonworks.iotas.common; -import org.apache.commons.lang.StringUtils; - +import java.util.Collections; import java.util.Map; import java.util.UUID; @@ -30,7 +29,7 @@ public IotasEventImpl(Map keyValues, String dataSourceId, String @Override public Map getFieldsAndValues() { - return fieldsAndValues; + return Collections.unmodifiableMap(fieldsAndValues); } @Override @@ -52,7 +51,6 @@ public boolean equals(Object o) { IotasEventImpl that = (IotasEventImpl) o; return id.equals(that.id); - } @Override diff --git a/core/src/main/java/com/hortonworks/iotas/storage/Storable.java b/core/src/main/java/com/hortonworks/iotas/storage/Storable.java index 4f81bb5fe..8ef7e7eb8 100644 --- a/core/src/main/java/com/hortonworks/iotas/storage/Storable.java +++ b/core/src/main/java/com/hortonworks/iotas/storage/Storable.java @@ -7,8 +7,7 @@ /** * Represents any entity that can be stored by our storage layer. */ -public interface - Storable { +public interface Storable { //TODO: probably we can remove getNameSpace and getPrimaryKey since we now have getStorableKey. //TODO: Leaving it for now for discussion purposes, as well as not to break the client code /** diff --git a/layout/src/main/java/com/hortonworks/iotas/layout/design/component/Component.java b/layout/src/main/java/com/hortonworks/iotas/layout/design/component/Component.java index ceda00ab1..1de652926 100644 --- a/layout/src/main/java/com/hortonworks/iotas/layout/design/component/Component.java +++ b/layout/src/main/java/com/hortonworks/iotas/layout/design/component/Component.java @@ -66,4 +66,14 @@ public List getDeclaredInput() { public void setDeclaredInput(List declaredInput) { this.declaredInput = declaredInput; } + + @Override + public String toString() { + return "Component{" + + "id=" + id + + ", name='" + name + '\'' + + ", description='" + description + '\'' + + ", declaredInput=" + declaredInput + + '}'; + } } diff --git a/layout/src/main/java/com/hortonworks/iotas/layout/design/component/RulesProcessor.java b/layout/src/main/java/com/hortonworks/iotas/layout/design/component/RulesProcessor.java index 4bbf837ef..dadf45445 100644 --- a/layout/src/main/java/com/hortonworks/iotas/layout/design/component/RulesProcessor.java +++ b/layout/src/main/java/com/hortonworks/iotas/layout/design/component/RulesProcessor.java @@ -35,4 +35,11 @@ public List getRules() { public void setRules(List rules) { this.rules = rules; } + + @Override + public String toString() { + return "RulesProcessor{" + + "rules=" + rules + + "} " + super.toString(); + } } diff --git a/layout/src/main/java/com/hortonworks/iotas/layout/design/component/RulesProcessorBuilder.java b/layout/src/main/java/com/hortonworks/iotas/layout/design/component/RulesProcessorBuilder.java new file mode 100644 index 000000000..72c394806 --- /dev/null +++ b/layout/src/main/java/com/hortonworks/iotas/layout/design/component/RulesProcessorBuilder.java @@ -0,0 +1,10 @@ +package com.hortonworks.iotas.layout.design.component; + +import com.hortonworks.iotas.layout.design.rule.Rule; + +/** + * Created by pshah on 11/19/15. + */ +public interface RulesProcessorBuilder { + RulesProcessor getRulesProcessor (); +} diff --git a/layout/src/main/java/com/hortonworks/iotas/layout/design/component/RulesProcessorJsonBuilder.java b/layout/src/main/java/com/hortonworks/iotas/layout/design/component/RulesProcessorJsonBuilder.java new file mode 100644 index 000000000..5eff93dc9 --- /dev/null +++ b/layout/src/main/java/com/hortonworks/iotas/layout/design/component/RulesProcessorJsonBuilder.java @@ -0,0 +1,32 @@ +package com.hortonworks.iotas.layout.design.component; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; + +/** + * Created by pshah on 11/19/15. + */ +public class RulesProcessorJsonBuilder implements RulesProcessorBuilder { + protected static final Logger log = LoggerFactory.getLogger(RulesProcessorJsonBuilder.class); + private final String json; + RulesProcessorJsonBuilder (String json) { + this.json = json; + } + + @Override + public RulesProcessor getRulesProcessor () { + ObjectMapper mapper = new ObjectMapper(); + RulesProcessor rulesProcessor = null; + try { + rulesProcessor = mapper.readValue(json, RulesProcessor.class); + } catch (IOException e) { + log.error("Error creating RulesProcessor from json string. Check " + + "the json"); + e.printStackTrace(); + } + return rulesProcessor; + } +} diff --git a/layout/src/main/java/com/hortonworks/iotas/layout/design/component/Sink.java b/layout/src/main/java/com/hortonworks/iotas/layout/design/component/Sink.java new file mode 100644 index 000000000..b2a6fa60e --- /dev/null +++ b/layout/src/main/java/com/hortonworks/iotas/layout/design/component/Sink.java @@ -0,0 +1,29 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.hortonworks.iotas.layout.design.component; + +/** + * Marker class to clearly identify a {@link Sink}

+ * A {@link Sink} receives input but does not communicate with any downstream components, hence it emits no output + */ +public class Sink extends Component { + // Sink extending Component is a more accurate representation of the physical world than having Component implement + // a Sink interface because the later implies that Processor "is a" Sink, which is not correct. + // On the other hand Sink "is a" Component +} diff --git a/layout/src/main/java/com/hortonworks/iotas/layout/design/rule/action/Action.java b/layout/src/main/java/com/hortonworks/iotas/layout/design/rule/action/Action.java index daad3d78c..ea1ab9d56 100644 --- a/layout/src/main/java/com/hortonworks/iotas/layout/design/rule/action/Action.java +++ b/layout/src/main/java/com/hortonworks/iotas/layout/design/rule/action/Action.java @@ -65,4 +65,12 @@ public void setDeclaredOutput(List declaredOutput) { public List getDeclaredOutput() { return declaredOutput; } + + @Override + public String toString() { + return "Action{" + + "components=" + components + + ", declaredOutput=" + declaredOutput + + '}'; + } } diff --git a/storm/src/main/java/com/hortonworks/iotas/layout/runtime/processor/RuleProcessorRuntime.java b/layout/src/main/java/com/hortonworks/iotas/layout/runtime/processor/RuleProcessorRuntime.java similarity index 50% rename from storm/src/main/java/com/hortonworks/iotas/layout/runtime/processor/RuleProcessorRuntime.java rename to layout/src/main/java/com/hortonworks/iotas/layout/runtime/processor/RuleProcessorRuntime.java index 3e1892028..307581822 100644 --- a/storm/src/main/java/com/hortonworks/iotas/layout/runtime/processor/RuleProcessorRuntime.java +++ b/layout/src/main/java/com/hortonworks/iotas/layout/runtime/processor/RuleProcessorRuntime.java @@ -18,48 +18,46 @@ package com.hortonworks.iotas.layout.runtime.processor; -import backtype.storm.topology.OutputFieldsDeclarer; import com.hortonworks.iotas.layout.design.component.RulesProcessor; import com.hortonworks.iotas.layout.runtime.rule.RuleRuntime; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.io.Serializable; +import java.util.Collections; import java.util.List; /** - * Object representing a design time rules processor. + * Object representing a runtime rules processor + * @param Type of runtime input to this rule, for example {@code Tuple} + * @param Type of object required to execute this rule in the underlying streaming framework e.g {@code IOutputCollector} */ -public class RuleProcessorRuntime implements Serializable { +public class RuleProcessorRuntime implements Serializable { + protected static final Logger log = LoggerFactory.getLogger(RuleProcessorRuntime.class); protected RulesProcessor rulesProcessor; - protected List rulesRuntime; + protected List> rulesRuntime; - RuleProcessorRuntime(List rulesRuntime, RulesProcessor rulesProcessor) { - this.rulesRuntime = rulesRuntime; - - this.rulesProcessor = rulesProcessor; - } - - public List getRulesRuntime() { - return rulesRuntime; + public RuleProcessorRuntime(RuleProcessorRuntimeDependenciesBuilder builder) { + this.rulesProcessor = builder.getRulesProcessor(); + this.rulesRuntime = builder.getRulesRuntime(); } - public void declareOutput(OutputFieldsDeclarer declarer) { - for (RuleRuntime ruleRuntime:rulesRuntime) { - ruleRuntime.declareOutput(declarer); - } - } - - public void setRulesRuntime(List rulesRuntime) { - this.rulesRuntime = rulesRuntime; + public RulesProcessor getRulesProcessor() { + return rulesProcessor; } - public RulesProcessor getRuleProcessor() { - return rulesProcessor; + public List> getRulesRuntime() { + return Collections.unmodifiableList(rulesRuntime); } - public void setRuleProcessor(RulesProcessor rulesProcessor) { - this.rulesProcessor = rulesProcessor; + @Override + public String toString() { + return "RuleProcessorRuntime{" + + "rulesProcessor=" + rulesProcessor + + ", rulesRuntime=" + rulesRuntime + + '}'; } } diff --git a/layout/src/main/java/com/hortonworks/iotas/layout/runtime/processor/RuleProcessorRuntimeDependenciesBuilder.java b/layout/src/main/java/com/hortonworks/iotas/layout/runtime/processor/RuleProcessorRuntimeDependenciesBuilder.java new file mode 100644 index 000000000..a0c29c0f4 --- /dev/null +++ b/layout/src/main/java/com/hortonworks/iotas/layout/runtime/processor/RuleProcessorRuntimeDependenciesBuilder.java @@ -0,0 +1,78 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.hortonworks.iotas.layout.runtime.processor; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.hortonworks.iotas.layout.design.component.RulesProcessor; +import com.hortonworks.iotas.layout.design.component.RulesProcessorBuilder; +import com.hortonworks.iotas.layout.design.component.RulesProcessorJsonBuilder; +import com.hortonworks.iotas.layout.design.rule.Rule; +import com.hortonworks.iotas.layout.runtime.rule.RuleRuntime; +import com.hortonworks.iotas.layout.runtime.rule.RuleRuntimeBuilder; +import org.apache.hadoop.mapred.OutputCollector; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +/** + * @param Type of runtime input to this rule, for example {@code Tuple} + * @param Type of object required to execute this rule in the underlying streaming framework e.g {@code IOutputCollector} + */ +public class RuleProcessorRuntimeDependenciesBuilder { + protected static final Logger log = LoggerFactory.getLogger(RuleProcessorRuntimeDependenciesBuilder.class); + + private final RulesProcessor rulesProcessor; + private final RuleRuntimeBuilder ruleRuntimeBuilder; + + public RuleProcessorRuntimeDependenciesBuilder(RulesProcessorBuilder rulesProcessorBuilder, RuleRuntimeBuilder ruleRuntimeBuilder) { + this.rulesProcessor = rulesProcessorBuilder.getRulesProcessor(); + this.ruleRuntimeBuilder = ruleRuntimeBuilder; + } + + public List> getRulesRuntime() { + final List rules = rulesProcessor.getRules(); + final List> rulesRuntime = new ArrayList<>(); + + if (rules != null) { + for (Rule rule : rules) { + ruleRuntimeBuilder.buildExpression(rule); + ruleRuntimeBuilder.buildScriptEngine(); + ruleRuntimeBuilder.buildScript(); + RuleRuntime ruleRuntime = ruleRuntimeBuilder.getRuleRuntime(rule); + rulesRuntime.add(ruleRuntime); + log.trace("Added {}", ruleRuntime); + } + log.debug("Finished building. {}", this); + } + return rulesRuntime; + } + + public RulesProcessor getRulesProcessor() { + return rulesProcessor; + } + + @Override + public String toString() { + return "RuleProcessorRuntimeDependenciesBuilder{" + rulesProcessor + + ", "+ ruleRuntimeBuilder + '}'; + } +} diff --git a/layout/src/main/java/com/hortonworks/iotas/layout/runtime/rule/RuleRuntime.java b/layout/src/main/java/com/hortonworks/iotas/layout/runtime/rule/RuleRuntime.java new file mode 100644 index 000000000..b88e0957c --- /dev/null +++ b/layout/src/main/java/com/hortonworks/iotas/layout/runtime/rule/RuleRuntime.java @@ -0,0 +1,70 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.hortonworks.iotas.layout.runtime.rule; + +import com.hortonworks.iotas.common.IotasEvent; +import com.hortonworks.iotas.layout.design.rule.Rule; +import com.hortonworks.iotas.layout.design.rule.exception.ConditionEvaluationException; +import com.hortonworks.iotas.layout.runtime.rule.condition.script.Script; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.script.ScriptException; +import java.io.Serializable; + +/** + * @param Type of runtime input to this rule, for example {@code Tuple} + * @param Type of object required to execute this rule in the underlying streaming framework e.g {@code IOutputCollector} + */ +public abstract class RuleRuntime implements Serializable { + protected static final Logger log = LoggerFactory.getLogger(RuleRuntime.class); + + protected final Rule rule; + protected final Script script; // Script used to evaluate the condition + + RuleRuntime(Rule rule, Script script) { + this.rule = rule; + this.script = script; + } + + public boolean evaluate(IotasEvent input) { + try { + boolean evaluates = script.evaluate(input); + log.debug("Rule condition evaluated to [{}].\n\t[{}]\n\tInput[{}]", evaluates, rule, input); + return evaluates; + } catch (ScriptException e) { + throw new ConditionEvaluationException("Exception occurred when evaluating rule condition. " + this, e); + } + } + + /** + * Executes a {@link Rule}'s Action + * @param input runtime input to this rule, for example, {@code Tuple} for {@code Storm} + * @param executor object required to execute this rule's action in the underlying streaming framework e.g {@code OutputCollector} for {@code Storm} + */ + public abstract void execute(I input, E executor); + + @Override + public String toString() { + return "RuleRuntime{" + + "rule=" + rule + + ", script=" + script + + '}'; + } +} diff --git a/storm/src/main/java/com/hortonworks/iotas/layout/runtime/rule/RuleRuntimeBuilder.java b/layout/src/main/java/com/hortonworks/iotas/layout/runtime/rule/RuleRuntimeBuilder.java similarity index 78% rename from storm/src/main/java/com/hortonworks/iotas/layout/runtime/rule/RuleRuntimeBuilder.java rename to layout/src/main/java/com/hortonworks/iotas/layout/runtime/rule/RuleRuntimeBuilder.java index 20fcfa304..6bb799fac 100644 --- a/storm/src/main/java/com/hortonworks/iotas/layout/runtime/rule/RuleRuntimeBuilder.java +++ b/layout/src/main/java/com/hortonworks/iotas/layout/runtime/rule/RuleRuntimeBuilder.java @@ -24,7 +24,11 @@ import java.io.Serializable; -public interface RuleRuntimeBuilder extends Serializable { +/** + * @param Type of runtime input to this rule, for example {@code Tuple} + * @param Type of object required to execute this rule in the underlying streaming framework e.g {@code IOutputCollector} + */ +public interface RuleRuntimeBuilder extends Serializable { Logger log = LoggerFactory.getLogger(RuleRuntimeBuilder.class); void buildExpression(Rule rule); @@ -33,5 +37,5 @@ public interface RuleRuntimeBuilder extends Serializable { void buildScript(); - RuleRuntime getRuleRuntime(Rule rule); + RuleRuntime getRuleRuntime(Rule rule); } diff --git a/layout/src/main/java/com/hortonworks/iotas/layout/runtime/rule/condition/expression/Expression.java b/layout/src/main/java/com/hortonworks/iotas/layout/runtime/rule/condition/expression/Expression.java index e32f6476b..fb298e7d2 100644 --- a/layout/src/main/java/com/hortonworks/iotas/layout/runtime/rule/condition/expression/Expression.java +++ b/layout/src/main/java/com/hortonworks/iotas/layout/runtime/rule/condition/expression/Expression.java @@ -31,6 +31,8 @@ public abstract class Expression { protected final Condition condition; + protected String expression; + public Expression(Condition condition) { this.condition = condition; } @@ -47,4 +49,12 @@ protected String getName(Field field) { protected String getType(Field field) { return field.getType() + " "; } + + @Override + public String toString() { + return "Expression{" + + "condition=" + condition + + ", expression='" + expression + '\'' + + '}'; + } } diff --git a/layout/src/main/java/com/hortonworks/iotas/layout/runtime/rule/condition/expression/GroovyExpression.java b/layout/src/main/java/com/hortonworks/iotas/layout/runtime/rule/condition/expression/GroovyExpression.java index 3d7a5ea16..4d602c5a7 100644 --- a/layout/src/main/java/com/hortonworks/iotas/layout/runtime/rule/condition/expression/GroovyExpression.java +++ b/layout/src/main/java/com/hortonworks/iotas/layout/runtime/rule/condition/expression/GroovyExpression.java @@ -23,27 +23,28 @@ import java.util.Arrays; public class GroovyExpression extends Expression { - public GroovyExpression(Condition condition) { super(condition); } @Override public String getExpression() { - final StringBuilder builder = new StringBuilder(""); - for (Condition.ConditionElement element : condition.getConditionElements()) { - builder.append(getName(element.getFirstOperand())) // x - .append(getOperation(element.getOperation())) // ==, !=, >, <, ... - .append(element.getSecondOperand()); // 5 - it is a constant + if (expression == null) { + final StringBuilder builder = new StringBuilder(""); + for (Condition.ConditionElement element : condition.getConditionElements()) { + builder.append(getName(element.getFirstOperand())) // x + .append(getOperation(element.getOperation())) // ==, !=, >, <, ... + .append(element.getSecondOperand()); // 5 - it is a constant - if (element.getLogicalOperator() != null) { - builder.append(" "); - builder.append(getLogicalOperator(element.getLogicalOperator())); // && or || - builder.append(" "); + if (element.getLogicalOperator() != null) { + builder.append(" "); + builder.append(getLogicalOperator(element.getLogicalOperator())); // && or || + builder.append(" "); + } } + expression = builder.toString(); // x == 5 [&& or ||] + log.debug("Built expression [{}] for condition [{}]", expression, condition); } - final String expression = builder.toString(); // x == 5 [&& or ||] - log.debug("Built expression [{}] for condition [{}]", expression, condition); return expression; } @@ -78,4 +79,6 @@ private String getOperation(Condition.ConditionElement.Operation operation) { operation, Arrays.toString(Condition.ConditionElement.Operation.values()))); } } + + } diff --git a/layout/src/main/java/com/hortonworks/iotas/layout/runtime/rule/condition/script/GroovyScript.java b/layout/src/main/java/com/hortonworks/iotas/layout/runtime/rule/condition/script/GroovyScript.java index 9a242189b..ae5a00500 100644 --- a/layout/src/main/java/com/hortonworks/iotas/layout/runtime/rule/condition/script/GroovyScript.java +++ b/layout/src/main/java/com/hortonworks/iotas/layout/runtime/rule/condition/script/GroovyScript.java @@ -21,28 +21,43 @@ import com.hortonworks.iotas.common.IotasEvent; import com.hortonworks.iotas.layout.runtime.rule.condition.expression.Expression; import com.hortonworks.iotas.layout.runtime.rule.condition.script.engine.ScriptEngine; +import groovy.lang.Binding; import javax.script.ScriptException; import java.util.Map; //TODO -public class GroovyScript extends Script { +public class GroovyScript extends Script { + private static final Binding EMPTY_BINDING = new Binding(); public GroovyScript(Expression expression, - ScriptEngine scriptEngine) { + ScriptEngine scriptEngine) { super(expression, scriptEngine); log.debug("Created Groovy Script: {}", super.toString()); } @Override public boolean evaluate(IotasEvent iotasEvent) throws ScriptException { - log.debug("Evaluating {}" + iotasEvent); - for (Map.Entry fieldAndValue : iotasEvent.getFieldsAndValues().entrySet()) { - log.debug("{Putting into engine key = {}, val = {}", fieldAndValue.getKey(), fieldAndValue.getValue()); - scriptEngine.put(fieldAndValue.getKey(), fieldAndValue.getValue()); + log.debug("Evaluating [{}] with [{}]", expression, iotasEvent); + boolean evaluates = false; + try { + if (iotasEvent != null) { + final Map fieldsToValues = iotasEvent.getFieldsAndValues(); + scriptEngine.setBinding(new Binding(fieldsToValues)); + log.debug("Set script binding to [{}]", fieldsToValues); + evaluates = (boolean) scriptEngine.run(); + log.debug("Expression [{}] evaluated to [{}]", expression, evaluates); + } + } catch (groovy.lang.MissingPropertyException e) { + // Occurs when not all the properties required for evaluating the script are set. This can happen for example + // when receiving an IotasEvent that does not have all the fields required to evaluate the expression + log.debug("Missing property required to evaluate expression.", e); + evaluates = false; } - final boolean result = (boolean) scriptEngine.eval(expression); - log.debug("Expression [{}] evaluated to [{}]", expression, result); - return result; + finally { + scriptEngine.setBinding(EMPTY_BINDING); + log.debug("Script binding reset to empty binding"); + } + return evaluates; } } diff --git a/layout/src/main/java/com/hortonworks/iotas/layout/runtime/rule/condition/script/engine/GroovyScriptEngine.java b/layout/src/main/java/com/hortonworks/iotas/layout/runtime/rule/condition/script/engine/GroovyScriptEngine.java index 5a6f1cb65..1035e37a5 100644 --- a/layout/src/main/java/com/hortonworks/iotas/layout/runtime/rule/condition/script/engine/GroovyScriptEngine.java +++ b/layout/src/main/java/com/hortonworks/iotas/layout/runtime/rule/condition/script/engine/GroovyScriptEngine.java @@ -24,15 +24,6 @@ import java.io.Serializable; public class GroovyScriptEngine implements ScriptEngine, Serializable { - /*@Override - public javax.script.ScriptEngine getEngine() { - final ScriptEngineManager factory = new ScriptEngineManager(); - javax.script.ScriptEngine engine = factory.getEngineByName("groovy"); - Bindings bindings = engine.createBindings(); - bindings.put("engine", engine); - return engine; - }*/ - @Override public javax.script.ScriptEngine getEngine() { javax.script.ScriptEngine engine = new GroovyScriptEngineImplSerializable(); diff --git a/layout/src/main/java/com/hortonworks/iotas/layout/runtime/rule/condition/script/engine/GroovyShellScriptEngine.java b/layout/src/main/java/com/hortonworks/iotas/layout/runtime/rule/condition/script/engine/GroovyShellScriptEngine.java new file mode 100644 index 000000000..b0eb7256d --- /dev/null +++ b/layout/src/main/java/com/hortonworks/iotas/layout/runtime/rule/condition/script/engine/GroovyShellScriptEngine.java @@ -0,0 +1,45 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.hortonworks.iotas.layout.runtime.rule.condition.script.engine; + +import com.hortonworks.iotas.layout.runtime.rule.condition.expression.Expression; +import groovy.lang.GroovyShell; + +import java.io.Serializable; + +public class GroovyShellScriptEngine implements ScriptEngine, Serializable { + private Expression expression; + + public GroovyShellScriptEngine(Expression expression) { + this.expression = expression; + } + + @Override + public groovy.lang.Script getEngine() { + GroovyShell groovyShell = new GroovyShell(); + return groovyShell.parse(expression.getExpression()); + } + + @Override + public String toString() { + return "GroovyShellScriptEngine{" + + "parsed_expression=" + expression + + '}'; + } +} diff --git a/layout/src/main/java/com/hortonworks/iotas/layout/runtime/rule/condition/script/engine/SqlStreamEngine.java b/layout/src/main/java/com/hortonworks/iotas/layout/runtime/rule/condition/script/engine/SqlStreamEngine.java index 558f9e972..e288751ae 100644 --- a/layout/src/main/java/com/hortonworks/iotas/layout/runtime/rule/condition/script/engine/SqlStreamEngine.java +++ b/layout/src/main/java/com/hortonworks/iotas/layout/runtime/rule/condition/script/engine/SqlStreamEngine.java @@ -31,4 +31,8 @@ public Object getEngine() { } }*/ } + + /*public String toString() { + + }*/ } diff --git a/layout/src/test/java/com/hortonworks/iotas/layout/design/rule/RuleProcessorMockBuilder.java b/layout/src/test/java/com/hortonworks/iotas/layout/design/rule/RuleProcessorMockBuilder.java index f148a0ba3..c2cadb435 100644 --- a/layout/src/test/java/com/hortonworks/iotas/layout/design/rule/RuleProcessorMockBuilder.java +++ b/layout/src/test/java/com/hortonworks/iotas/layout/design/rule/RuleProcessorMockBuilder.java @@ -21,6 +21,7 @@ import com.hortonworks.iotas.common.Schema; import com.hortonworks.iotas.layout.design.component.Component; import com.hortonworks.iotas.layout.design.component.RulesProcessor; +import com.hortonworks.iotas.layout.design.component.Sink; import com.hortonworks.iotas.layout.design.rule.action.Action; import com.hortonworks.iotas.layout.design.rule.condition.Condition; @@ -30,11 +31,14 @@ public class RuleProcessorMockBuilder { public static final String TEMPERATURE = "temperature"; public static final String HUMIDITY = "humidity"; + public static final String RULE_PROCESSOR = "rule_processor"; + public static final String RULE = "rule"; + public static final String SINK = "sink"; private final long ruleProcessorId; private final int numRules; private final int numSinks; - private List declaredInputsOutputs; + private List declaredInputsOutputs; public RuleProcessorMockBuilder(long ruleProcessorId, int numRules, int numSinksPerRule) { this.ruleProcessorId = ruleProcessorId; @@ -46,18 +50,18 @@ public RulesProcessor build() { RulesProcessor rulesProcessor = new RulesProcessor(); rulesProcessor.setDeclaredInput(buildDeclaredInputsOutputs()); rulesProcessor.setId(ruleProcessorId); - rulesProcessor.setName("rule_processsor_" + ruleProcessorId); - rulesProcessor.setDescription("rule_processsor_" + ruleProcessorId + "_desc"); + rulesProcessor.setName(RULE_PROCESSOR + "_" + ruleProcessorId); + rulesProcessor.setDescription(RULE_PROCESSOR + "_" + ruleProcessorId + "_desc"); rulesProcessor.setRules(buildRules()); return rulesProcessor; } - private List buildDeclaredInputsOutputs() { - final Schema declaredInputsOutputs = new Schema.SchemaBuilder().fields(new ArrayList() {{ + private List buildDeclaredInputsOutputs() { + final Schema declaredInputsOutputs = new Schema.SchemaBuilder().fields(new ArrayList() {{ add(new Schema.Field(TEMPERATURE, Schema.Type.INTEGER)); add(new Schema.Field(HUMIDITY, Schema.Type.INTEGER)); }}).build(); - ; + this.declaredInputsOutputs = declaredInputsOutputs.getFields(); return declaredInputsOutputs.getFields(); } @@ -73,9 +77,9 @@ private List buildRules() { private Rule buildRule(long ruleId, Condition condition, Action action) { Rule rule = new Rule(); rule.setId(ruleId); - rule.setName("rule_" + ruleId); - rule.setDescription("rule_" + ruleId + "_desc"); - rule.setRuleProcessorName("rule_processsor_" + ruleProcessorId); + rule.setName(RULE + "_" + ruleId); + rule.setDescription(RULE + "_" + ruleId + "_desc"); + rule.setRuleProcessorName(RULE_PROCESSOR + "_" + ruleProcessorId); rule.setCondition(condition); rule.setAction(action); return rule; @@ -96,11 +100,11 @@ private List buildSinks() { return sinks; } - private Component buildSink(long sinkId) { - Component sink = new Component(); + private Sink buildSink(long sinkId) { + Sink sink = new Sink(); sink.setId(ruleProcessorId); - sink.setName("sink_" + sinkId); - sink.setDescription("sink_" + sinkId + "_desc"); + sink.setName(SINK + "_" + sinkId); + sink.setDescription(SINK + "_" + sinkId + "_desc"); sink.setDeclaredInput(declaredInputsOutputs); return sink; } diff --git a/storm/src/main/java/com/hortonworks/bolt/rules/RulesBolt.java b/storm/src/main/java/com/hortonworks/bolt/rules/RulesBolt.java index 3fcaf201c..fcdd96434 100644 --- a/storm/src/main/java/com/hortonworks/bolt/rules/RulesBolt.java +++ b/storm/src/main/java/com/hortonworks/bolt/rules/RulesBolt.java @@ -24,7 +24,7 @@ import backtype.storm.topology.base.BaseRichBolt; import backtype.storm.tuple.Tuple; import com.hortonworks.iotas.common.IotasEvent; -import com.hortonworks.iotas.layout.runtime.processor.RuleProcessorRuntime; +import com.hortonworks.iotas.layout.runtime.processor.RuleProcessorRuntimeStorm; import com.hortonworks.iotas.layout.runtime.rule.RuleRuntime; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -34,11 +34,11 @@ public class RulesBolt extends BaseRichBolt { protected static final Logger log = LoggerFactory.getLogger(RulesBolt.class); - private final RuleProcessorRuntime ruleProcessorRuntime; + private final RuleProcessorRuntimeStorm ruleProcessorRuntime; private OutputCollector collector; - public RulesBolt(RuleProcessorRuntime ruleProcessorRuntime) { + public RulesBolt(RuleProcessorRuntimeStorm ruleProcessorRuntime) { this.ruleProcessorRuntime = ruleProcessorRuntime; } @@ -49,21 +49,26 @@ public void prepare(Map stormConf, TopologyContext context, OutputCollector coll } @Override - public void execute(Tuple input) { // tuple input should an IotasEvent + public void execute(Tuple input) { // Input tuple is expected to be an IotasEvent try { - Object iotasEvent = input.getValueByField(IotasEvent.IOTAS_EVENT); - log.debug("++++++++ Executing tuple [{}] with IotasEvent [{}]", input, iotasEvent); + final Object iotasEvent = input.getValueByField(IotasEvent.IOTAS_EVENT); - for (RuleRuntime rule : ruleProcessorRuntime.getRulesRuntime()) { - if (rule.evaluate(input)) { - rule.execute(input, collector); // collector can be null when the rule does not forward a stream + if (iotasEvent instanceof IotasEvent) { + log.debug("++++++++ Executing tuple [{}] with IotasEvent [{}]", input, iotasEvent); + + for (RuleRuntime rule : ruleProcessorRuntime.getRulesRuntime()) { + if (rule.evaluate((IotasEvent) iotasEvent)) { + rule.execute(input, collector); // collector can be null when the rule does not forward a stream + } } + } else { + log.debug("Invalid tuple received. Tuple [{}]. IotasEvent [{}]. Tuple disregarded and rules not evaluated", input, iotasEvent); } collector.ack(input); } catch (Exception e) { collector.fail(input); collector.reportError(e); - log.debug("",e); // useful to debug unit tests + log.debug("",e); // useful to debug unit tests } } diff --git a/storm/src/main/java/com/hortonworks/iotas/layout/runtime/processor/RuleProcessorRuntimeConstructor.java b/storm/src/main/java/com/hortonworks/iotas/layout/runtime/processor/RuleProcessorRuntimeStorm.java similarity index 53% rename from storm/src/main/java/com/hortonworks/iotas/layout/runtime/processor/RuleProcessorRuntimeConstructor.java rename to storm/src/main/java/com/hortonworks/iotas/layout/runtime/processor/RuleProcessorRuntimeStorm.java index a3adb0b75..5592da156 100644 --- a/storm/src/main/java/com/hortonworks/iotas/layout/runtime/processor/RuleProcessorRuntimeConstructor.java +++ b/storm/src/main/java/com/hortonworks/iotas/layout/runtime/processor/RuleProcessorRuntimeStorm.java @@ -18,25 +18,24 @@ package com.hortonworks.iotas.layout.runtime.processor; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import backtype.storm.task.OutputCollector; +import backtype.storm.topology.OutputFieldsDeclarer; +import backtype.storm.tuple.Tuple; +import com.hortonworks.iotas.layout.runtime.rule.RuleRuntime; +import com.hortonworks.iotas.layout.runtime.rule.RuleRuntimeStorm; -public class RuleProcessorRuntimeConstructor { - private static final Logger log = LoggerFactory.getLogger(RuleProcessorRuntimeConstructor.class); - private RulesProcessorRuntimeBuilder builder; +public class RuleProcessorRuntimeStorm extends RuleProcessorRuntime { - public RuleProcessorRuntimeConstructor(RulesProcessorRuntimeBuilder builder) { - this.builder = builder; + public RuleProcessorRuntimeStorm(RuleProcessorRuntimeDependenciesBuilder builder) { + super(builder); } - public void construct() { - builder.build(); - } - - public RuleProcessorRuntime getRuleProcessorRuntime() { - RuleProcessorRuntime ruleProcessorRuntime = builder.getRuleProcessorRuntime(); - log.debug("Constructed: {}", ruleProcessorRuntime); - return ruleProcessorRuntime; + public void declareOutput(OutputFieldsDeclarer declarer) { + log.debug("Declaring output fields"); + for (RuleRuntime ruleRuntime : rulesRuntime) { + ((RuleRuntimeStorm)ruleRuntime).declareOutput(declarer); + } } } + diff --git a/storm/src/main/java/com/hortonworks/iotas/layout/runtime/processor/RulesProcessorRuntimeBuilder.java b/storm/src/main/java/com/hortonworks/iotas/layout/runtime/processor/RulesProcessorRuntimeBuilder.java deleted file mode 100644 index e09b7c9ba..000000000 --- a/storm/src/main/java/com/hortonworks/iotas/layout/runtime/processor/RulesProcessorRuntimeBuilder.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.hortonworks.iotas.layout.runtime.processor; - -import com.hortonworks.iotas.layout.design.component.RulesProcessor; -import com.hortonworks.iotas.layout.design.rule.Rule; -import com.hortonworks.iotas.layout.runtime.rule.RuleRuntime; -import com.hortonworks.iotas.layout.runtime.rule.RuleRuntimeConstructor; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.ArrayList; -import java.util.List; - -public class RulesProcessorRuntimeBuilder { - private static final Logger log = LoggerFactory.getLogger(RulesProcessorRuntimeBuilder.class); - - private final RulesProcessor rulesProcessor; - private final RuleRuntimeConstructor ruleRuntimeConstructor; - private List rulesRuntime; - - public RulesProcessorRuntimeBuilder(RulesProcessor rulesProcessor, - RuleRuntimeConstructor ruleRuntimeConstructor) { - this.rulesProcessor = rulesProcessor; - this.ruleRuntimeConstructor = ruleRuntimeConstructor; - } - - public void build() { - final List rules = rulesProcessor.getRules(); - - rulesRuntime = new ArrayList<>(); - - if (rules != null) { - for (Rule rule : rules) { - ruleRuntimeConstructor.construct(rule); - RuleRuntime ruleRuntime = ruleRuntimeConstructor.getRuleRuntime(rule); - rulesRuntime.add(ruleRuntime); - log.debug("Added {}", ruleRuntime); - } - } - } - - public RuleProcessorRuntime getRuleProcessorRuntime() { - return new RuleProcessorRuntime(rulesRuntime, rulesProcessor) ; - } -} diff --git a/storm/src/main/java/com/hortonworks/iotas/layout/runtime/rule/GroovyRuleRuntimeBuilder.java b/storm/src/main/java/com/hortonworks/iotas/layout/runtime/rule/GroovyRuleRuntimeBuilder.java index 9318837b3..00fe495c6 100644 --- a/storm/src/main/java/com/hortonworks/iotas/layout/runtime/rule/GroovyRuleRuntimeBuilder.java +++ b/storm/src/main/java/com/hortonworks/iotas/layout/runtime/rule/GroovyRuleRuntimeBuilder.java @@ -18,32 +18,39 @@ package com.hortonworks.iotas.layout.runtime.rule; +import backtype.storm.task.OutputCollector; +import backtype.storm.tuple.Tuple; import com.hortonworks.iotas.layout.design.rule.Rule; import com.hortonworks.iotas.layout.runtime.rule.condition.expression.GroovyExpression; import com.hortonworks.iotas.layout.runtime.rule.condition.script.GroovyScript; -import com.hortonworks.iotas.layout.runtime.rule.condition.script.engine.GroovyScriptEngine; +import com.hortonworks.iotas.layout.runtime.rule.condition.script.engine.GroovyShellScriptEngine; -public class GroovyRuleRuntimeBuilder implements RuleRuntimeBuilder { +public class GroovyRuleRuntimeBuilder implements RuleRuntimeBuilder { private GroovyExpression groovyExpression; - private GroovyScriptEngine groovyScriptEngine; + private GroovyShellScriptEngine groovyScriptEngine; private GroovyScript groovyScript; - public GroovyRuleRuntimeBuilder() { - } - public void buildExpression(Rule rule) { groovyExpression = new GroovyExpression(rule.getCondition()); } public void buildScriptEngine() { - groovyScriptEngine = new GroovyScriptEngine(); + groovyScriptEngine = new GroovyShellScriptEngine(groovyExpression); } public void buildScript() { groovyScript = new GroovyScript(groovyExpression, groovyScriptEngine); } - public RuleRuntime getRuleRuntime(Rule rule) { - return new RuleRuntime(rule, groovyScript); + public RuleRuntimeStorm getRuleRuntime(Rule rule) { + return new RuleRuntimeStorm(rule, groovyScript); + } + + @Override + public String toString() { + return "GroovyRuleRuntimeBuilder{" + + ", groovyScriptEngine=" + groovyScriptEngine + + ", groovyScript=" + groovyScript + + '}'; } } diff --git a/storm/src/main/java/com/hortonworks/iotas/layout/runtime/rule/RuleRuntimeConstructor.java b/storm/src/main/java/com/hortonworks/iotas/layout/runtime/rule/RuleRuntimeConstructor.java deleted file mode 100644 index 1d9ca7130..000000000 --- a/storm/src/main/java/com/hortonworks/iotas/layout/runtime/rule/RuleRuntimeConstructor.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.hortonworks.iotas.layout.runtime.rule; - -import com.hortonworks.iotas.layout.design.rule.Rule; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class RuleRuntimeConstructor { - protected static final Logger log = LoggerFactory.getLogger(RuleRuntimeConstructor.class); - private RuleRuntimeBuilder ruleRuntimeBuilder; - - - public RuleRuntimeConstructor(RuleRuntimeBuilder ruleRuntimeBuilder) { - this.ruleRuntimeBuilder = ruleRuntimeBuilder; - } - - public void construct(Rule rule) { - log.debug("Constructing RuleRuntime for rule: {}", rule); - ruleRuntimeBuilder.buildExpression(rule); - ruleRuntimeBuilder.buildScriptEngine(); - ruleRuntimeBuilder.buildScript(); - } - - public RuleRuntime getRuleRuntime(Rule rule) { - RuleRuntime ruleRuntime = ruleRuntimeBuilder.getRuleRuntime(rule); - log.debug("Rule [{}] => RuleRuntime [{}]", rule, ruleRuntime); - return ruleRuntime; - } -} diff --git a/storm/src/main/java/com/hortonworks/iotas/layout/runtime/rule/RuleRuntime.java b/storm/src/main/java/com/hortonworks/iotas/layout/runtime/rule/RuleRuntimeStorm.java similarity index 51% rename from storm/src/main/java/com/hortonworks/iotas/layout/runtime/rule/RuleRuntime.java rename to storm/src/main/java/com/hortonworks/iotas/layout/runtime/rule/RuleRuntimeStorm.java index 02b116681..a0e37cfe1 100644 --- a/storm/src/main/java/com/hortonworks/iotas/layout/runtime/rule/RuleRuntime.java +++ b/storm/src/main/java/com/hortonworks/iotas/layout/runtime/rule/RuleRuntimeStorm.java @@ -24,53 +24,30 @@ import backtype.storm.tuple.Tuple; import com.hortonworks.iotas.common.IotasEvent; import com.hortonworks.iotas.layout.design.rule.Rule; -import com.hortonworks.iotas.layout.design.rule.exception.ConditionEvaluationException; import com.hortonworks.iotas.layout.runtime.rule.condition.script.Script; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import javax.script.ScriptException; -import java.io.Serializable; +public class RuleRuntimeStorm extends RuleRuntime { + private static final Logger log = LoggerFactory.getLogger(RuleRuntimeStorm.class); -public class RuleRuntime implements Serializable { - private static final Logger log = LoggerFactory.getLogger(RuleRuntime.class); - - private final Rule rule; - private final Script script; // Script used to evaluate the condition - - RuleRuntime(Rule rule, Script script) { - this.rule = rule; - this.script = script; - } - - public boolean evaluate(Tuple input) { - try { - boolean evaluates = false; - IotasEvent iotasEvent; - if (input == null || (iotasEvent = (IotasEvent) input.getValueByField(IotasEvent.IOTAS_EVENT)) == null) { - throw new ConditionEvaluationException("Null or invalid tuple"); - } - log.debug("iotasEvent = " + iotasEvent); - log.debug("Evaluating condition for Rule: [{}] \n\tInput tuple: [{}]", rule, input); - evaluates = script.evaluate(iotasEvent); - log.debug("Rule condition evaluated to: [{}]. Rule: [{}] \n\tInput tuple: [{}]", evaluates, rule, input); - return evaluates; - } catch (ScriptException e) { - throw new ConditionEvaluationException("Exception occurred when evaluating rule condition. " + this, e); - } + RuleRuntimeStorm(Rule rule, Script script) { + super(rule, script); } public void execute(Tuple input, OutputCollector collector) { - log.debug("Executing rule: [{}] \n\tInput tuple: [{}] \n\tCollector: [{}] \n\tStream:[{}]", - rule, input, collector, getStreamId()); collector.emit(getStreamId(), input, input.getValues()); + log.debug("Executed rule [{}]\n\tInput tuple [{}]\n\tCollector [{}]\n\tStream:[{}]", + rule, input, collector, getStreamId()); } public void declareOutput(OutputFieldsDeclarer declarer) { - declarer.declareStream(getStreamId(), getFields()); + final String streamId = getStreamId(); + final Fields fields = getFields(); + declarer.declareStream(streamId, fields); + log.debug("Declared stream. Stream Id [{}] Fields [{}]", streamId, fields); } - //TODO public String getStreamId() { return rule.getRuleProcessorName() + "." + rule.getName() + "." + rule.getId(); } @@ -78,12 +55,4 @@ public String getStreamId() { private Fields getFields() { return new Fields(IotasEvent.IOTAS_EVENT); } - - @Override - public String toString() { - return "RuleRuntime{" + - "rule=" + rule + - ", script=" + script + - '}'; - } } diff --git a/storm/src/main/java/com/hortonworks/iotas/layout/runtime/rule/topology/RuleProcessorMockBuilder.java b/storm/src/main/java/com/hortonworks/iotas/layout/runtime/rule/topology/RuleProcessorMockBuilder.java index c92e5dd54..208f0b493 100644 --- a/storm/src/main/java/com/hortonworks/iotas/layout/runtime/rule/topology/RuleProcessorMockBuilder.java +++ b/storm/src/main/java/com/hortonworks/iotas/layout/runtime/rule/topology/RuleProcessorMockBuilder.java @@ -22,6 +22,8 @@ import com.hortonworks.iotas.common.Schema.Field; import com.hortonworks.iotas.layout.design.component.Component; import com.hortonworks.iotas.layout.design.component.RulesProcessor; +import com.hortonworks.iotas.layout.design.component.RulesProcessorBuilder; +import com.hortonworks.iotas.layout.design.component.Sink; import com.hortonworks.iotas.layout.design.rule.Rule; import com.hortonworks.iotas.layout.design.rule.action.Action; import com.hortonworks.iotas.layout.design.rule.condition.Condition; @@ -31,9 +33,12 @@ import java.util.ArrayList; import java.util.List; -public class RuleProcessorMockBuilder { +public class RuleProcessorMockBuilder implements RulesProcessorBuilder { public static final String TEMPERATURE = "temperature"; public static final String HUMIDITY = "humidity"; + public static final String RULE_PROCESSOR = "rule_processor"; + public static final String RULE = "rule"; + public static final String SINK = "sink"; private final long ruleProcessorId; private final int numRules; @@ -46,18 +51,19 @@ public RuleProcessorMockBuilder(long ruleProcessorId, int numRules, int numSinks this.numSinks = numSinksPerRule; } - public RulesProcessor build() { + @Override + public RulesProcessor getRulesProcessor () { RulesProcessor rulesProcessor = new RulesProcessor(); rulesProcessor.setDeclaredInput(buildDeclaredInputsOutputs()); rulesProcessor.setId(ruleProcessorId); - rulesProcessor.setName("rule_processsor_" + ruleProcessorId); - rulesProcessor.setDescription("rule_processsor_" + ruleProcessorId + "_desc"); + rulesProcessor.setName(RULE_PROCESSOR + "_" + ruleProcessorId); + rulesProcessor.setDescription(RULE_PROCESSOR + "_" + ruleProcessorId + "_desc"); rulesProcessor.setRules(buildRules()); return rulesProcessor; } private List buildDeclaredInputsOutputs() { - final Schema declaredInputsOutputs = new Schema.SchemaBuilder().fields(new ArrayList() {{ + final Schema declaredInputsOutputs = new Schema.SchemaBuilder().fields(new ArrayList() {{ add(new Field(TEMPERATURE, Schema.Type.INTEGER)); add(new Field(HUMIDITY, Schema.Type.INTEGER)); }}).build(); @@ -77,9 +83,9 @@ private List buildRules() { private Rule buildRule(long ruleId, Condition condition, Action action) { Rule rule = new Rule(); rule.setId(ruleId); - rule.setName("rule_" + ruleId); - rule.setDescription("rule_" + ruleId + "_desc"); - rule.setRuleProcessorName("rule_processsor_" + ruleProcessorId); + rule.setName(RULE + "_" + ruleId); + rule.setDescription(RULE + "_" + ruleId + "_desc"); + rule.setRuleProcessorName(RULE_PROCESSOR + "_" + ruleProcessorId); rule.setCondition(condition); rule.setAction(action); return rule; @@ -100,20 +106,20 @@ private List buildSinks() { return sinks; } - private Component buildSink(long sinkId) { - Component sink = new Component(); + private Sink buildSink(long sinkId) { + Sink sink = new Sink(); sink.setId(ruleProcessorId); - sink.setName("sink_" + sinkId); - sink.setDescription("sink_" + sinkId + "_desc"); + sink.setName(SINK + "_" + sinkId); + sink.setDescription(SINK + "_" + sinkId + "_desc"); sink.setDeclaredInput(declaredInputsOutputs); return sink; } private Condition buildCondition(int idx) { if (idx % 2 == 0) { - return buildCondition(buildConditionElements(Operation.GREATER_THAN)); + return buildCondition(buildConditionElements(Operation.GREATER_THAN)); // temperature > 100 && humidity > 50 } - return buildCondition(buildConditionElements(Operation.LESS_THAN)); + return buildCondition(buildConditionElements(Operation.LESS_THAN)); // temperature < 100 && humidity < 50 } private Condition buildCondition(List conditionElements) { @@ -132,10 +138,10 @@ private List buildConditionElements(Operation operat private Condition.ConditionElement buildConditionElement( String firstOperand, Operation operation, String secondOperand, LogicalOperator logicalOperator) { - Condition.ConditionElement conditionElement = - new Condition.ConditionElement(); - final Field temperature = new Field(firstOperand, Schema.Type.INTEGER); - conditionElement.setFirstOperand(temperature); + + final Condition.ConditionElement conditionElement = new Condition.ConditionElement(); + final Field firstOperandField = new Field(firstOperand, Schema.Type.INTEGER); + conditionElement.setFirstOperand(firstOperandField); conditionElement.setOperation(operation); conditionElement.setSecondOperand(secondOperand); if (logicalOperator != null) { diff --git a/storm/src/main/java/com/hortonworks/iotas/layout/runtime/rule/topology/RulesTestSpout.java b/storm/src/main/java/com/hortonworks/iotas/layout/runtime/rule/topology/RulesTestSpout.java index 31b087ded..5a14c60c1 100644 --- a/storm/src/main/java/com/hortonworks/iotas/layout/runtime/rule/topology/RulesTestSpout.java +++ b/storm/src/main/java/com/hortonworks/iotas/layout/runtime/rule/topology/RulesTestSpout.java @@ -51,8 +51,6 @@ public class RulesTestSpout extends BaseRichSpout { put("humidity", 49); }}, "dataSrcId_2", "24"); -// private static final Values VALUES = new Values(100, 50); -// private static final Values VALUES = new Values(IOTAS_EVENT_1); private static final List LIST_VALUES = Lists.newArrayList(new Values(IOTAS_EVENT_1), new Values(IOTAS_EVENT_2)); @Override @@ -72,7 +70,7 @@ public void nextTuple() { @Override public void declareOutputFields(OutputFieldsDeclarer declarer) { - log.debug("++++++++ DECLARING OUPTPUT FIELDS"); + log.debug("++++++++ DECLARING OUTPUT FIELDS"); declarer.declare(getOutputFields()); } diff --git a/storm/src/main/java/com/hortonworks/iotas/layout/runtime/rule/topology/RulesTopologyTest.java b/storm/src/main/java/com/hortonworks/iotas/layout/runtime/rule/topology/RulesTopologyTest.java index 982d66fdd..61cc06e7c 100644 --- a/storm/src/main/java/com/hortonworks/iotas/layout/runtime/rule/topology/RulesTopologyTest.java +++ b/storm/src/main/java/com/hortonworks/iotas/layout/runtime/rule/topology/RulesTopologyTest.java @@ -24,24 +24,26 @@ import backtype.storm.generated.AlreadyAliveException; import backtype.storm.generated.InvalidTopologyException; import backtype.storm.generated.StormTopology; +import backtype.storm.task.OutputCollector; import backtype.storm.topology.IRichBolt; import backtype.storm.topology.TopologyBuilder; +import backtype.storm.tuple.Tuple; import com.hortonworks.bolt.rules.RulesBolt; import com.hortonworks.iotas.layout.design.component.RulesProcessor; -import com.hortonworks.iotas.layout.runtime.processor.RuleProcessorRuntime; -import com.hortonworks.iotas.layout.runtime.processor.RuleProcessorRuntimeConstructor; -import com.hortonworks.iotas.layout.runtime.processor.RulesProcessorRuntimeBuilder; +import com.hortonworks.iotas.layout.design.component.RulesProcessorBuilder; +import com.hortonworks.iotas.layout.runtime.processor.RuleProcessorRuntimeDependenciesBuilder; +import com.hortonworks.iotas.layout.runtime.processor.RuleProcessorRuntimeStorm; import com.hortonworks.iotas.layout.runtime.rule.GroovyRuleRuntimeBuilder; import com.hortonworks.iotas.layout.runtime.rule.RuleRuntimeBuilder; -import com.hortonworks.iotas.layout.runtime.rule.RuleRuntimeConstructor; +import com.hortonworks.iotas.layout.runtime.rule.RuleRuntimeStorm; public class RulesTopologyTest { - protected static final String RULES_TEST_SPOUT = "RulesTestSpout"; protected static final String RULES_BOLT = "rulesBolt"; protected static final String RULES_TEST_SINK_BOLT = "RulesTestSinkBolt"; protected static final String RULES_TEST_SINK_BOLT_1 = RULES_TEST_SINK_BOLT + "_1"; protected static final String RULES_TEST_SINK_BOLT_2 = RULES_TEST_SINK_BOLT + "_2"; + private RuleProcessorRuntimeStorm ruleProcessorRuntime; public static void main(String[] args) throws AlreadyAliveException, InvalidTopologyException { RulesTopologyTest rulesTopologyTest = new RulesTopologyTest(); @@ -59,31 +61,26 @@ protected StormTopology createTopology() { TopologyBuilder builder = new TopologyBuilder(); builder.setSpout(RULES_TEST_SPOUT, new RulesTestSpout()); builder.setBolt(RULES_BOLT, createRulesBolt(createRulesProcessorRuntime())).shuffleGrouping(RULES_TEST_SPOUT); - builder.setBolt(RULES_TEST_SINK_BOLT_1, new RulesTestSinkBolt()).shuffleGrouping(RULES_BOLT, getStream1()); - builder.setBolt(RULES_TEST_SINK_BOLT_2, new RulesTestSinkBolt()).shuffleGrouping(RULES_BOLT, getStream2()); + builder.setBolt(RULES_TEST_SINK_BOLT_1, new RulesTestSinkBolt()).shuffleGrouping(RULES_BOLT, getStream(0)); + builder.setBolt(RULES_TEST_SINK_BOLT_2, new RulesTestSinkBolt()).shuffleGrouping(RULES_BOLT, getStream(1)); return builder.createTopology(); } - protected IRichBolt createRulesBolt(RuleProcessorRuntime rulesProcessorRuntime) { + protected IRichBolt createRulesBolt(RuleProcessorRuntimeStorm rulesProcessorRuntime) { return new RulesBolt(rulesProcessorRuntime); } - protected RuleProcessorRuntime createRulesProcessorRuntime() { - RulesProcessor rulesProcessor = new RuleProcessorMockBuilder(1,2,2).build(); - RuleRuntimeBuilder ruleRuntimeBuilder = new GroovyRuleRuntimeBuilder(); - RuleRuntimeConstructor ruleRuntimeConstructor = new RuleRuntimeConstructor(ruleRuntimeBuilder); - RulesProcessorRuntimeBuilder ruleProcessorRuntimeBuilder = new RulesProcessorRuntimeBuilder(rulesProcessor, ruleRuntimeConstructor); - RuleProcessorRuntimeConstructor ruleProcessorRuntimeConstructor = new RuleProcessorRuntimeConstructor(ruleProcessorRuntimeBuilder); - ruleProcessorRuntimeConstructor.construct(); - return ruleProcessorRuntimeConstructor.getRuleProcessorRuntime(); - } - - protected String getStream1() { - return "rule_processsor_1.rule_1.1"; + protected RuleProcessorRuntimeStorm createRulesProcessorRuntime() { + RulesProcessorBuilder rulesProcessorBuilder = new RuleProcessorMockBuilder(1,2,2); + RuleRuntimeBuilder ruleRuntimeBuilder = new GroovyRuleRuntimeBuilder(); + RuleProcessorRuntimeDependenciesBuilder dependenciesBuilder = + new RuleProcessorRuntimeDependenciesBuilder<> + (rulesProcessorBuilder, ruleRuntimeBuilder); + ruleProcessorRuntime = new RuleProcessorRuntimeStorm(dependenciesBuilder); + return ruleProcessorRuntime; } - - protected String getStream2() { - return "rule_processsor_1.rule_2.2"; + protected String getStream(int i) { + return ((RuleRuntimeStorm)ruleProcessorRuntime.getRulesRuntime().get(i)).getStreamId(); } } diff --git a/storm/src/test/java/com/hortonworks/bolt/RulesBoltTest.java b/storm/src/test/java/com/hortonworks/bolt/RulesBoltTest.java index ec504f856..b09d7eab4 100644 --- a/storm/src/test/java/com/hortonworks/bolt/RulesBoltTest.java +++ b/storm/src/test/java/com/hortonworks/bolt/RulesBoltTest.java @@ -24,15 +24,22 @@ import com.hortonworks.bolt.rules.RulesBolt; import com.hortonworks.iotas.common.IotasEvent; import com.hortonworks.iotas.common.IotasEventImpl; -import com.hortonworks.iotas.layout.runtime.processor.RuleProcessorRuntime; +import com.hortonworks.iotas.layout.runtime.processor.RuleProcessorRuntimeStorm; +import com.hortonworks.iotas.layout.runtime.rule.RuleRuntimeStorm; +import com.hortonworks.iotas.layout.runtime.rule.topology.RuleProcessorMockBuilder; import com.hortonworks.iotas.layout.runtime.rule.topology.RulesTopologyTest; import mockit.Expectations; import mockit.Injectable; import mockit.Tested; import mockit.VerificationsInOrder; import mockit.integration.junit4.JMockit; +import org.junit.Assert; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; +import org.junit.rules.TestName; +import org.junit.rules.TestWatcher; +import org.junit.runner.Description; import org.junit.runner.RunWith; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -43,77 +50,127 @@ public class RulesBoltTest extends RulesTopologyTest { protected static final Logger log = LoggerFactory.getLogger(RulesBoltTest.class); - public static final IotasEventImpl IOTAS_EVENT = new IotasEventImpl(new HashMap() {{ - put("temperature", 101); - put("humidity", 51); - }}, "dataSrcId", "23"); + // JUnit constructs for printing which tests are being run + public + @Rule + TestName testName = new TestName(); + public + @Rule + TestWatcher watchman = new TestWatcher() { + @Override + public void starting(final Description method) { + log.debug("RUNNING TEST [{}] ", method.getMethodName()); + } + }; + + private static final IotasEvent IOTAS_EVENT = new IotasEventImpl(new HashMap() {{ + put(RuleProcessorMockBuilder.TEMPERATURE, 101); + put(RuleProcessorMockBuilder.HUMIDITY, 51); + }}, "dataSrcId_1", "1"); + + private static final Values IOTAS_EVENT_VALUES = new Values(IOTAS_EVENT); - public static final IotasEventImpl IOTAS_EVENT_INVALID_FIELDS = new IotasEventImpl(new HashMap() {{ + private static final IotasEvent IOTAS_EVENT_INVALID_FIELDS = new IotasEventImpl(new HashMap() {{ put("non_existent_field1", 101); put("non_existent_field2", 51); put("non_existent_field3", 23); - }}, "dataSrcId", "23"); + }}, "dataSrcId_2", "2"); - private static final Values VALUES = new Values(IOTAS_EVENT); + private static final IotasEvent IOTAS_EVENT_GOOD_AND_INVALID_FIELDS = new IotasEventImpl(new HashMap() {{ + put("non_existent_field1", 101); + put(RuleProcessorMockBuilder.TEMPERATURE, 101); + put("non_existent_field2", 51); + put(RuleProcessorMockBuilder.HUMIDITY, 51); + put("non_existent_field3", 23); + }}, "dataSrcId_2", "3"); - //TODO: Check all of this + private static final Values IOTAS_EVENT_GOOD_AND_INVALID_FIELDS_VALUES = new Values(IOTAS_EVENT_GOOD_AND_INVALID_FIELDS); - private @Tested + private + @Tested RulesBolt rulesBolt; - - private @Injectable OutputCollector mockOutputCollector; - private @Injectable Tuple mockTuple; - private RuleProcessorRuntime ruleProcessorRuntime; + private + @Injectable + OutputCollector mockOutputCollector; + private + @Injectable + Tuple mockTuple; + private RuleProcessorRuntimeStorm ruleProcessorRuntime; @Before public void setup() throws Exception { ruleProcessorRuntime = createRulesProcessorRuntime(); rulesBolt = (RulesBolt) createRulesBolt(ruleProcessorRuntime); - rulesBolt.prepare(null, null, mockOutputCollector); } @Test - public void test_validTuple_oneRuleEvaluates() throws Exception { + public void test_tupleAllFieldsValid_oneRuleEvaluates_acks() throws Exception { + new Expectations() {{ + mockTuple.getValues(); + result = IOTAS_EVENT_VALUES; + mockTuple.getValueByField(IotasEvent.IOTAS_EVENT); + returns(IOTAS_EVENT); + }}; + + executeAndVerifyCollectorAcks(1, IOTAS_EVENT_VALUES); + } + + @Test + public void test_tupleSomeFieldsValid_oneRuleEvaluates_acks() throws Exception { new Expectations() {{ - mockTuple.getValueByField(IotasEvent.IOTAS_EVENT); returns(IOTAS_EVENT); + mockTuple.getValues(); + result = IOTAS_EVENT_GOOD_AND_INVALID_FIELDS_VALUES; + mockTuple.getValueByField(IotasEvent.IOTAS_EVENT); + returns(IOTAS_EVENT_GOOD_AND_INVALID_FIELDS); }}; - callExecuteAndVerifyCollectorInteraction(true); + executeAndVerifyCollectorAcks(1, IOTAS_EVENT_GOOD_AND_INVALID_FIELDS_VALUES); } @Test - public void test_invalidTuple_failsTuple() throws Exception { + public void test_tupleInvalidFields_ruleDoesNotEvaluate_acks() throws Exception { new Expectations() {{ - mockTuple.getValueByField(IotasEvent.IOTAS_EVENT); returns(null); + mockTuple.getValueByField(IotasEvent.IOTAS_EVENT); + returns(null); }}; - callExecuteAndVerifyCollectorInteraction(false); + executeAndVerifyCollectorAcks(0, null); } @Test - public void test_tupleInvalidFields_failsTuple() throws Exception { + public void test_tupleInvalidFields_ruleDoesNotEvaluate_fails() throws Exception { +// test_tupleAllFieldsValid_oneRuleEvaluates_acks(); new Expectations() {{ - mockTuple.getValueByField(IotasEvent.IOTAS_EVENT); returns(IOTAS_EVENT_INVALID_FIELDS); + mockTuple.getValueByField(IotasEvent.IOTAS_EVENT); + returns(IOTAS_EVENT_INVALID_FIELDS); }}; - callExecuteAndVerifyCollectorInteraction(false); + executeAndVerifyCollectorAcks(0, null); } - private void callExecuteAndVerifyCollectorInteraction(final boolean isSuccess) { + private void executeAndVerifyCollectorAcks(final int rule2NumTimes, final Values expectedValues) { rulesBolt.execute(mockTuple); - if(isSuccess) { - new VerificationsInOrder() {{ - mockOutputCollector.emit(ruleProcessorRuntime.getRulesRuntime().get(0).getStreamId(), mockTuple, withAny(VALUES)); times = 0; - mockOutputCollector.emit(ruleProcessorRuntime.getRulesRuntime().get(1).getStreamId(), mockTuple, withAny(VALUES)); times = 1; - mockOutputCollector.ack(mockTuple); times = 1; - }}; - - } else { - new VerificationsInOrder() {{ - mockOutputCollector.fail(mockTuple); - }}; - } + new VerificationsInOrder() {{ + mockOutputCollector.emit(((RuleRuntimeStorm) ruleProcessorRuntime.getRulesRuntime().get(0)).getStreamId(), + mockTuple, IOTAS_EVENT_VALUES); + times = 0; // rule 1 does not trigger + + Values actualValues; + mockOutputCollector.emit(((RuleRuntimeStorm) ruleProcessorRuntime.getRulesRuntime().get(1)).getStreamId(), + mockTuple, actualValues = withCapture()); + times = rule2NumTimes; // rule 2 triggers rule2NumTimes + + Assert.assertEquals(expectedValues, actualValues); + mockOutputCollector.ack(mockTuple); + times = 1; + }}; } + private void executeAndVerifyCollectorFails(final boolean isSuccess, final int rule2NumTimes, final Values expectedValues) { + rulesBolt.execute(mockTuple); + new VerificationsInOrder() {{ + mockOutputCollector.fail(mockTuple); + }}; + } } diff --git a/storm/src/test/java/com/hortonworks/rules/GroovyScriptTest.java b/storm/src/test/java/com/hortonworks/rules/GroovyScriptTest.java new file mode 100644 index 000000000..9db6b7146 --- /dev/null +++ b/storm/src/test/java/com/hortonworks/rules/GroovyScriptTest.java @@ -0,0 +1,87 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.hortonworks.rules; + +import com.hortonworks.iotas.common.IotasEventImpl; +import com.hortonworks.iotas.layout.design.rule.condition.Condition; +import com.hortonworks.iotas.layout.runtime.rule.condition.expression.Expression; +import com.hortonworks.iotas.layout.runtime.rule.condition.script.GroovyScript; +import com.hortonworks.iotas.layout.runtime.rule.condition.script.engine.ScriptEngine; +import mockit.Expectations; +import mockit.Injectable; +import mockit.Mocked; +import mockit.Tested; +import mockit.integration.junit4.JMockit; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.HashMap; + +@RunWith(JMockit.class) +public class GroovyScriptTest { + private static final IotasEventImpl IOTAS_EVENT = new IotasEventImpl(new HashMap() {{ + put("F1", 3); + put("F2", 4); + }}, "dataSrcId_1", "id_1"); + + private @Tested GroovyScript groovyScript; + private @Injectable Expression expression; +// private @Injectable String expression; + private @Injectable ScriptEngine scriptEngine; + private @Mocked Condition condition; + + @Before + public void setUp() throws Exception { +// expression = "F1 > 1 && F2 > 2"; + +// expression = new GroovyExpression(condition); +// scriptEngine = new GroovyScriptEngine(); + } + + @Test + public void test_2fields_and_condition_matches_true() throws Exception { + final String twoFieldsAndCondition = "F1 > 1 && F2 > 2"; + new ExpressionExpectation(twoFieldsAndCondition); + + System.out.println("Asserting"); + Assert.assertTrue(groovyScript.evaluate(IOTAS_EVENT)); + } + + private class ExpressionExpectation extends Expectations { + private String resultStr; + + public ExpressionExpectation(String result) { + System.out.println("constructor"); + resultStr = result; + } + + { + expression.getExpression(); result = "F1 > 1 && F2 > 2"; // F1 - Field1.getName() +// expression.getExpression(); result = resultStr; // F1 - Field1.getName() + System.out.println("static block"); + } + + } + + + + +} diff --git a/storm/src/test/java/com/hortonworks/rules/GroovyTest.java b/storm/src/test/java/com/hortonworks/rules/GroovyTest.java index 5beb39c43..0b7228b18 100644 --- a/storm/src/test/java/com/hortonworks/rules/GroovyTest.java +++ b/storm/src/test/java/com/hortonworks/rules/GroovyTest.java @@ -18,10 +18,12 @@ package com.hortonworks.rules; +import groovy.lang.Binding; +import groovy.lang.GroovyShell; +import groovy.lang.Script; import groovy.util.Eval; import org.junit.Test; -import javax.script.Bindings; import javax.script.ScriptEngine; import javax.script.ScriptEngineManager; @@ -30,8 +32,8 @@ public class GroovyTest { public void testName() throws Exception { final ScriptEngineManager factory = new ScriptEngineManager(); ScriptEngine engine = factory.getEngineByName("groovy"); - Bindings bindings = engine.createBindings(); - bindings.put("engine", engine); +// Bindings bindings = engine.createBindings(); +// bindings.put("engine", engine); // bindings.put("x", 5); // bindings.put("y", 3); @@ -63,5 +65,62 @@ public void testName1() throws Exception { int result = (int) Eval.me("33*3"); System.out.println(result); assert result == 99; + } + + @Test + public void testGroovyShell() throws Exception { + GroovyShell groovyShell = new GroovyShell(); + Binding binding = new Binding(); + binding.setProperty("x",5); + binding.setProperty("y",3); + final String s = "x > 2 && y > 1"; + Script script = groovyShell.parse(s); + script.setBinding(binding); + Object result = script.run(); + System.out.printf("evaluating [%s] with (x,y)=(%s,%s) => %s\n", s, script.getBinding().getProperty("x"), script.getBinding().getProperty("y"), result); + + boolean x = binding.hasVariable("x"); + +// script.setBinding(null); + script.setBinding(new Binding()); + + + /*Binding binding1 = new Binding(); + binding1.setProperty("x",55); + binding1.setProperty("y",33); + script.setBinding(binding1);*/ + Object result1 = script.run(); + System.out.printf("evaluating [%s] with (x,y)=(%s,%s) => %s\n", s, script.getBinding().getProperty("x"), script.getBinding().getProperty("y"), result); + + binding.setProperty("y",0); + result = script.run(); + System.out.printf("evaluating [%s] with (x,y)=(%s,%s) => %s\n", s, binding.getProperty("x"), binding.getProperty("y"), result); + + binding.setProperty("x",null); + binding.setProperty("y",null); + binding.setProperty("x1",-1); + binding.setProperty("y1",-3); + result = script.run(); + System.out.printf("evaluating [%s] with (x,y)=(%s,%s) => %s\n", s, binding.getProperty("x"), binding.getProperty("y"), result); + System.out.printf("evaluating [%s] with (x,y,z)=(%s,%s,%s) => %s\n", s, binding.getProperty("x"), binding.getProperty("y"), binding.getProperty("z"), result); + /* + + + def shell = new GroovyShell() + + def b1 = new Binding(x:3) + def b2 = new Binding(x:4) + def script = shell.parse('x = 2*x') + script.binding = b1 + script.run() + script.binding = b2 + script.run() + assert b1.getProperty('x') == 6 + assert b2.getProperty('x') == 8 + assert b1 != b2*/ + + + + } }