Author: pmouawad Date: Sun Sep 13 20:12:47 2015 New Revision: 1702817 URL: http://svn.apache.org/r1702817 Log: Bug 58406 - IfController : Use Nashorn Engine if available for JavaScript evaluation Bugzilla Id: 58406 Modified: jmeter/trunk/bin/jmeter.properties jmeter/trunk/src/core/org/apache/jmeter/control/IfController.java jmeter/trunk/xdocs/changes.xml Modified: jmeter/trunk/bin/jmeter.properties URL: http://svn.apache.org/viewvc/jmeter/trunk/bin/jmeter.properties?rev=1702817&r1=1702816&r2=1702817&view=diff ============================================================================== --- jmeter/trunk/bin/jmeter.properties (original) +++ jmeter/trunk/bin/jmeter.properties Sun Sep 13 20:12:47 2015 @@ -1023,6 +1023,13 @@ beanshell.server.file=../extras/startup. # Default is true. Use false to revert to previous behaviour #CookieManager.check.cookies=true +# Ability to revert to Rhino as default Javascript Engine used by IfController +# JMeter works as following: +# - JDK < 8 : Rhino +# - JDK >= 8 : Nashorn +# If you want to use Rhino on JDK8, set this property to true +#ifcontroller.use_rhino=false + # (2.0.3) JMeterThread behaviour has been changed to set the started flag before # the controllers are initialised. This is so controllers can access variables earlier. # In case this causes problems, the previous behaviour can be restored by uncommenting Modified: jmeter/trunk/src/core/org/apache/jmeter/control/IfController.java URL: http://svn.apache.org/viewvc/jmeter/trunk/src/core/org/apache/jmeter/control/IfController.java?rev=1702817&r1=1702816&r2=1702817&view=diff ============================================================================== --- jmeter/trunk/src/core/org/apache/jmeter/control/IfController.java (original) +++ jmeter/trunk/src/core/org/apache/jmeter/control/IfController.java Sun Sep 13 20:12:47 2015 @@ -20,8 +20,15 @@ package org.apache.jmeter.control; import java.io.Serializable; +import javax.script.ScriptContext; +import javax.script.ScriptEngine; +import javax.script.ScriptEngineManager; +import javax.script.SimpleScriptContext; + import org.apache.jmeter.samplers.Sampler; +import org.apache.jmeter.testelement.ThreadListener; import org.apache.jmeter.testelement.property.StringProperty; +import org.apache.jmeter.util.JMeterUtils; import org.apache.jorphan.logging.LoggingManager; import org.apache.log.Logger; import org.mozilla.javascript.Context; @@ -53,18 +60,92 @@ import org.mozilla.javascript.Scriptable // for unit test code @see TestIfController -public class IfController extends GenericController implements Serializable { +public class IfController extends GenericController implements Serializable, ThreadListener { private static final Logger logger = LoggingManager.getLoggerForClass(); - private static final long serialVersionUID = 240L; + private static final long serialVersionUID = 241L; private static final String CONDITION = "IfController.condition"; //$NON-NLS-1$ private static final String EVALUATE_ALL = "IfController.evaluateAll"; //$NON-NLS-1$ private static final String USE_EXPRESSION = "IfController.useExpression"; //$NON-NLS-1$ + + private static final String USE_RHINO_ENGINE_PROPERTY = "ifcontroller.use_rhino"; + + private static final boolean USE_RHINO_ENGINE = + getInstance().getEngineByName("nashorn") == null || //$NON-NLS-1$ + JMeterUtils.getPropDefault(USE_RHINO_ENGINE_PROPERTY, false) ; + + private static final ThreadLocal NASHORN_ENGINE = new ThreadLocal() { + + /* (non-Javadoc) + * @see java.lang.ThreadLocal#initialValue() + */ + @Override + protected ScriptEngine initialValue() { + return getInstance().getEngineByName("nashorn");//$NON-NLS-N$ + } + + }; + + private interface JsEvaluator { + boolean evaluate(String testElementName, String condition); + } + + private static class RhinoJsEngine implements JsEvaluator { + @Override + public boolean evaluate(String testElementName, String condition) { + boolean result = false; + // now evaluate the condition using JavaScript + Context cx = Context.enter(); + try { + Scriptable scope = cx.initStandardObjects(null); + Object cxResultObject = cx.evaluateString(scope, condition + /** * conditionString ** */ + , "", 1, null); + result = computeResultFromString(condition, Context.toString(cxResultObject)); + } catch (Exception e) { + logger.error(testElementName+": error while processing "+ "[" + condition + "]\n", e); + } finally { + Context.exit(); + } + return result; + } + } + + private static class NashornJsEngine implements JsEvaluator { + @Override + public boolean evaluate(String testElementName, String condition) { + try { + ScriptContext newContext = new SimpleScriptContext(); + newContext.setBindings(NASHORN_ENGINE.get().createBindings(), ScriptContext.ENGINE_SCOPE); + Object o = NASHORN_ENGINE.get().eval(condition, newContext); + return computeResultFromString(condition, o.toString()); + } catch (Exception ex) { + logger.error(testElementName+": error while processing "+ "[" + condition + "]\n", ex); + } + return false; + } + } + + private static JsEvaluator JAVASCRIPT_EVALUATOR = USE_RHINO_ENGINE ? new RhinoJsEngine() : new NashornJsEngine(); + + /** + * Initialization On Demand Holder pattern + */ + private static class LazyHolder { + public static final ScriptEngineManager INSTANCE = new ScriptEngineManager(); + } + + /** + * @return ScriptEngineManager singleton + */ + public static ScriptEngineManager getInstance() { + return LazyHolder.INSTANCE; + } /** * constructor */ @@ -101,38 +182,37 @@ public class IfController extends Generi * evaluate the condition clause log error if bad condition */ private boolean evaluateCondition(String cond) { - logger.debug(" getCondition() : [" + cond + "]"); - - String resultStr = ""; - boolean result = false; - - // now evaluate the condition using JavaScript - Context cx = Context.enter(); - try { - Scriptable scope = cx.initStandardObjects(null); - Object cxResultObject = cx.evaluateString(scope, cond - /** * conditionString ** */ - , "", 1, null); - resultStr = Context.toString(cxResultObject); - - if (resultStr.equals("false")) { //$NON-NLS-1$ - result = false; - } else if (resultStr.equals("true")) { //$NON-NLS-1$ - result = true; - } else { - throw new Exception(" BAD CONDITION :: " + cond + " :: expected true or false"); - } - - logger.debug(" >> evaluate Condition - [ " + cond + "] results is [" + result + "]"); - } catch (Exception e) { - logger.error(getName()+": error while processing "+ "[" + cond + "]\n", e); - } finally { - Context.exit(); + if(logger.isDebugEnabled()) { + logger.debug(" getCondition() : [" + cond + "]"); } + return JAVASCRIPT_EVALUATOR.evaluate(getName(), cond); + } + /** + * @param condition + * @param resultStr + * @return boolean + * @throws Exception + */ + private static final boolean computeResultFromString(String condition, String resultStr) throws Exception { + boolean result; + switch(resultStr) { + case "false": + result=false; + break; + case "true": + result=true; + break; + default: + throw new Exception(" BAD CONDITION :: " + condition + " :: expected true or false"); + } + if(logger.isDebugEnabled()) { + logger.debug(" >> evaluate Condition - [ " + condition + "] results is [" + result + "]"); + } return result; } - + + private static boolean evaluateExpression(String cond) { return cond.equalsIgnoreCase("true"); // $NON-NLS-1$ } @@ -209,4 +289,12 @@ public class IfController extends Generi public void setUseExpression(boolean selected) { setProperty(USE_EXPRESSION, selected, false); } + @Override + public void threadStarted() { + + } + @Override + public void threadFinished() { + NASHORN_ENGINE.remove(); + } } Modified: jmeter/trunk/xdocs/changes.xml URL: http://svn.apache.org/viewvc/jmeter/trunk/xdocs/changes.xml?rev=1702817&r1=1702816&r2=1702817&view=diff ============================================================================== --- jmeter/trunk/xdocs/changes.xml (original) +++ jmeter/trunk/xdocs/changes.xml Sun Sep 13 20:12:47 2015 @@ -74,6 +74,7 @@ Summary
  • In RandomTimer class, protected instance timer has been replaced by getTimer() protected method, this is related to 58100. This may impact 3rd party plugins.
  • +
  • If Controller will now use by default Nashorn Engine under Java8. If you want to revert to Rhino Engine, use property ifcontroller.use_rhino=true, see 58406
@@ -94,6 +95,7 @@ Summary

Controllers

    +
  • 58406IfController : Use Nashorn Engine if available for JavaScript evaluation

Listeners