Author: henrib Date: Mon Aug 1 13:07:22 2016 New Revision: 1754746 URL: http://svn.apache.org/viewvc?rev=1754746&view=rev Log: JEXL: JEXL-207: split Interpreter in 2 (class was too big), reworked error/exception handling to be more coherent, added tests
Added: commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/InterpreterBase.java (with props) commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/CaptureLog.java (with props) commons/proper/jexl/trunk/src/test/scripts/testAdd.jexl - copied, changed from r1719037, commons/proper/jexl/trunk/src/test/scripts/testA.jexl Modified: commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/JexlException.java commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/Interpreter.java commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/Operators.java commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/AnnotationTest.java commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/ArithmeticOperatorTest.java commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/ExceptionTest.java commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/IssuesTest.java commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/ScriptCallableTest.java commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/ScriptTest.java commons/proper/jexl/trunk/src/test/resources/log4j.xml Modified: commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/JexlException.java URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/JexlException.java?rev=1754746&r1=1754745&r2=1754746&view=diff ============================================================================== --- commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/JexlException.java (original) +++ commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/JexlException.java Mon Aug 1 13:07:22 2016 @@ -416,16 +416,6 @@ public class JexlException extends Runti /** * Creates a new Property exception instance. * - * @param node the offending ASTnode - * @param var the unknown variable - */ - public Property(JexlNode node, String var) { - this(node, var, null); - } - - /** - * Creates a new Property exception instance. - * * @param node the offending ASTnode * @param var the unknown variable * @param cause the exception causing the error Modified: commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/Interpreter.java URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/Interpreter.java?rev=1754746&r1=1754745&r2=1754746&view=diff ============================================================================== --- commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/Interpreter.java (original) +++ commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/Interpreter.java Mon Aug 1 13:07:22 2016 @@ -23,11 +23,12 @@ import org.apache.commons.jexl3.JexlEngi import org.apache.commons.jexl3.JexlException; import org.apache.commons.jexl3.JexlOperator; import org.apache.commons.jexl3.JexlScript; + import org.apache.commons.jexl3.introspection.JexlMethod; import org.apache.commons.jexl3.introspection.JexlPropertyGet; import org.apache.commons.jexl3.introspection.JexlPropertySet; -import org.apache.commons.jexl3.introspection.JexlUberspect; import org.apache.commons.jexl3.introspection.JexlUberspect.PropertyResolver; + import org.apache.commons.jexl3.parser.ASTAddNode; import org.apache.commons.jexl3.parser.ASTAndNode; import org.apache.commons.jexl3.parser.ASTAnnotatedStatement; @@ -102,7 +103,6 @@ import org.apache.commons.jexl3.parser.A import org.apache.commons.jexl3.parser.ASTWhileStatement; import org.apache.commons.jexl3.parser.JexlNode; import org.apache.commons.jexl3.parser.Node; -import org.apache.commons.jexl3.parser.ParserVisitor; import java.util.HashMap; import java.util.Iterator; @@ -110,40 +110,25 @@ import java.util.List; import java.util.Map; import java.util.concurrent.Callable; -import org.apache.commons.logging.Log; /** * An interpreter of JEXL syntax. * * @since 2.0 */ -public class Interpreter extends ParserVisitor { - /** The JEXL engine. */ - protected final Engine jexl; - /** The logger. */ - protected final Log logger; - /** The uberspect. */ - protected final JexlUberspect uberspect; - /** The arithmetic handler. */ - protected final JexlArithmetic arithmetic; +public class Interpreter extends InterpreterBase { /** The operators evaluation delegate. */ protected final Operators operators; - /** The map of symboled functions. */ - protected final Map<String, Object> functions; - /** The map of symboled functions. */ - protected Map<String, Object> functors; - /** The context to store/retrieve variables. */ - protected final JexlContext context; - /** symbol values. */ + /** Cache executors. */ + protected final boolean cache; + /** Symbol values. */ protected final Scope.Frame frame; /** The context to store/retrieve variables. */ protected final JexlContext.NamespaceResolver ns; - /** Cache executors. */ - protected final boolean cache; - /** Cancellation support. */ - protected volatile boolean cancelled = false; - /** Empty parameters for method matching. */ - protected static final Object[] EMPTY_PARAMS = new Object[0]; + /** The map of 'prefix:function' to object resolving as namespaces. */ + protected final Map<String, Object> functions; + /** The map of dynamically creates namespaces, NamespaceFunctor or duck-types of those. */ + protected Map<String, Object> functors; /** * Creates an interpreter. @@ -152,32 +137,17 @@ public class Interpreter extends ParserV * @param eFrame the interpreter evaluation frame */ protected Interpreter(Engine engine, JexlContext aContext, Scope.Frame eFrame) { - this.jexl = engine; - this.logger = jexl.logger; - this.uberspect = jexl.uberspect; - this.context = aContext != null ? aContext : Engine.EMPTY_CONTEXT; - if (this.context instanceof JexlEngine.Options) { - JexlEngine.Options opts = (JexlEngine.Options) context; - this.arithmetic = jexl.arithmetic.options(opts); - if (!arithmetic.getClass().equals(jexl.arithmetic.getClass())) { - logger.error( - "expected arithmetic to be " + jexl.arithmetic.getClass().getSimpleName() + - ", got " + arithmetic.getClass().getSimpleName() - ); - } - } else { - this.arithmetic = jexl.arithmetic; - } + super(engine, aContext); + this.operators = new Operators(this); + this.cache = jexl.cache != null; + this.frame = eFrame; if (this.context instanceof JexlContext.NamespaceResolver) { ns = ((JexlContext.NamespaceResolver) context); } else { ns = Engine.EMPTY_NS; } this.functions = jexl.functions; - this.cache = jexl.cache != null; - this.frame = eFrame; this.functors = null; - this.operators = new Operators(this); } /** @@ -212,12 +182,17 @@ public class Interpreter extends ParserV } logger.warn(xjexl.getMessage(), xjexl.getCause()); } finally { - if (functors != null && AUTOCLOSEABLE != null) { - for (Object functor : functors.values()) { - closeIfSupported(functor); + synchronized(this) { + if (functors != null) { + if (AUTOCLOSEABLE != null) { + for (Object functor : functors.values()) { + closeIfSupported(functor); + } + } + functors.clear(); + functors = null; } } - functors = null; if (context instanceof JexlContext.ThreadLocal) { jexl.putThreadLocal(local); } @@ -225,249 +200,10 @@ public class Interpreter extends ParserV return null; } - /** Java7 AutoCloseable interface defined?. */ - private static final Class<?> AUTOCLOSEABLE; - static { - Class<?> c; - try { - c = Class.forName("java.lang.AutoCloseable"); - } catch (ClassNotFoundException xclass) { - c = null; - } - AUTOCLOSEABLE = c; - } - - /** - * Attempt to call close() if supported. - * <p>This is used when dealing with auto-closeable (duck-like) objects - * @param closeable the object we'd like to close - */ - protected void closeIfSupported(Object closeable) { - if (closeable != null) { - //if (AUTOCLOSEABLE == null || AUTOCLOSEABLE.isAssignableFrom(closeable.getClass())) { - JexlMethod mclose = uberspect.getMethod(closeable, "close", EMPTY_PARAMS); - if (mclose != null) { - try { - mclose.invoke(closeable, EMPTY_PARAMS); - } catch (Exception xignore) { - logger.warn(xignore); - } - } - //} - } - } - - /** - * Whether this interpreter is currently evaluating with a strict engine flag. - * @return true if strict engine, false otherwise - */ - protected boolean isStrictEngine() { - if (this.context instanceof JexlEngine.Options) { - JexlEngine.Options opts = (JexlEngine.Options) context; - Boolean strict = opts.isStrict(); - if (strict != null) { - return strict.booleanValue(); - } - } - return jexl.isStrict(); - } - - /** - * Whether this interpreter is currently evaluating with a silent mode. - * @return true if silent, false otherwise - */ - protected boolean isSilent() { - if (this.context instanceof JexlEngine.Options) { - JexlEngine.Options opts = (JexlEngine.Options) context; - Boolean silent = opts.isSilent(); - if (silent != null) { - return silent.booleanValue(); - } - } - return jexl.isSilent(); - } - - /** @return true if interrupt throws a JexlException.Cancel. */ - protected boolean isCancellable() { - if (this.context instanceof JexlEngine.Options) { - JexlEngine.Options opts = (JexlEngine.Options) context; - Boolean ocancellable = opts.isCancellable(); - if (ocancellable != null) { - return ocancellable.booleanValue(); - } - } - return jexl.cancellable; - } - - /** - * Finds the node causing a NPE for diadic operators. - * @param xrt the RuntimeException - * @param node the parent node - * @param left the left argument - * @param right the right argument - * @return the left, right or parent node - */ - protected JexlNode findNullOperand(RuntimeException xrt, JexlNode node, Object left, Object right) { - if (xrt instanceof JexlArithmetic.NullOperand) { - if (left == null) { - return node.jjtGetChild(0); - } - if (right == null) { - return node.jjtGetChild(1); - } - } - return node; - } - - /** - * Triggered when a variable can not be resolved. - * @param node the node where the error originated from - * @param var the variable name - * @param undef whether the variable is undefined or null - * @return throws JexlException if isStrict, null otherwise - */ - protected Object unsolvableVariable(JexlNode node, String var, boolean undef) { - if (!isSilent()) { - logger.warn(JexlException.variableError(node, var, undef)); - } - if (isStrictEngine() && (undef || arithmetic.isStrict())) { - throw new JexlException.Variable(node, var, undef); - } - return null; - } - - /** - * Triggered when a method can not be resolved. - * @param node the node where the error originated from - * @param method the method name - * @return throws JexlException if isStrict, null otherwise - */ - protected Object unsolvableMethod(JexlNode node, String method) { - if (!isSilent()) { - logger.warn(JexlException.methodError(node, method)); - } - if (isStrictEngine()) { - throw new JexlException.Method(node, method); - } - return null; - } - - /** - * Triggered when a property can not be resolved. - * @param node the node where the error originated from - * @param var the property name - * @param cause the cause if any - * @return throws JexlException if isStrict, null otherwise - */ - protected Object unsolvableProperty(JexlNode node, String var, Throwable cause) { - if (!isSilent()) { - logger.warn(JexlException.propertyError(node, var), cause); - } - if (isStrictEngine()) { - throw new JexlException.Property(node, var, cause); - } - return null; - } - - /** - * Triggered when an operator fails. - * @param node the node where the error originated from - * @param operator the method name - * @param cause the cause of error (if any) - * @throws JexlException if isStrict - */ - protected void operatorError(JexlNode node, JexlOperator operator, Throwable cause) { - if (cause != null) { - if (!isSilent()) { - logger.warn(JexlException.operatorError(node, operator.getOperatorSymbol()), cause); - } - if (isStrictEngine()) { - throw new JexlException.Operator(node, operator.getOperatorSymbol(), cause); - } - } - } - - /** - * Triggered when method, function or constructor invocation fails. - * @param xjexl the JexlException wrapping the original error - * @return throws JexlException if isStrict, null otherwise - */ - protected Object invocationFailed(JexlException xjexl) { - if (!isSilent()) { - logger.warn(xjexl.getMessage(), xjexl.getCause()); - } - if (isStrictEngine() - || xjexl instanceof JexlException.Return - || xjexl instanceof JexlException.Cancel) { - throw xjexl; - } - return null; - } - - /** - * Wraps an exception thrown by an invocation. - * @param node the node triggering the exception - * @param methodName the method/function name - * @param xany the cause - * @return a JexlException - */ - protected JexlException invocationException(JexlNode node, String methodName, Exception xany) { - Throwable cause = xany.getCause(); - if (cause instanceof JexlException) { - throw (JexlException) cause; - } - if (cause instanceof InterruptedException) { - cancelled = true; - return new JexlException.Cancel(node); - } - return new JexlException(node, methodName, xany); - } - - /** - * Triggered when an annotation processing fails. - * @param node the node where the error originated from - * @param annotation the annotation name - * @param cause the cause of error (if any) - * @throws JexlException if isStrict - */ - protected void annotationError(JexlNode node, String annotation, Throwable cause) { - if (!isSilent()) { - logger.warn(JexlException.annotationError(node, annotation), cause); - } - if (isStrictEngine()) { - throw new JexlException.Annotation(node, annotation, cause); - } - } - - /** - * Cancels this evaluation, setting the cancel flag that will result in a JexlException.Cancel to be thrown. - * @return false if already cancelled, true otherwise - */ - protected boolean cancel() { - if (cancelled) { - return false; - } else { - cancelled = true; - return true; - } - } - - /** - * Checks whether this interpreter execution was canceled due to thread interruption. - * @return true if canceled, false otherwise - */ - protected boolean isCancelled() { - if (!cancelled) { - cancelled = Thread.currentThread().isInterrupted(); - } - return cancelled; - } - /** * Resolves a namespace, eventually allocating an instance using context as constructor argument. * <p> - * The lifetime of - * such instances span the current expression or script evaluation.</p> + * The lifetime of such instances span the current expression or script evaluation.</p> * @param prefix the prefix name (may be null for global namespace) * @param node the AST node * @return the namespace instance @@ -475,10 +211,12 @@ public class Interpreter extends ParserV protected Object resolveNamespace(String prefix, JexlNode node) { Object namespace; // check whether this namespace is a functor - if (functors != null) { - namespace = functors.get(prefix); - if (namespace != null) { - return namespace; + synchronized (this) { + if (functors != null) { + namespace = functors.get(prefix); + if (namespace != null) { + return namespace; + } } } // check if namespace is a resolver @@ -506,10 +244,12 @@ public class Interpreter extends ParserV } // got a functor, store it and return it if (functor != null) { - if (functors == null) { - functors = new HashMap<String, Object>(); + synchronized (this) { + if (functors == null) { + functors = new HashMap<String, Object>(); + } + functors.put(prefix, functor); } - functors.put(prefix, functor); return functor; } else { return namespace; @@ -875,7 +615,7 @@ public class Interpreter extends ParserV // get an iterator for the collection/array etc via the introspector. Object forEach = null; try { - forEach = operators.tryForeachOverload(node, iterableValue); + forEach = operators.tryOverload(node, JexlOperator.FOR_EACH, iterableValue); Iterator<?> itemsIterator = forEach instanceof Iterator ? (Iterator<?>) forEach : uberspect.getIterator(iterableValue); @@ -1257,6 +997,7 @@ public class Interpreter extends ParserV JexlNode objectNode; StringBuilder ant = null; boolean antish = !(parent instanceof ASTReference); + boolean pty = true; int v = 1; main: for (int c = 0; c < numChildren; c++) { @@ -1279,9 +1020,14 @@ public class Interpreter extends ParserV } else if (antish) { // if we still have a null object, check for an antish variable if (ant == null) { JexlNode first = node.jjtGetChild(0); - if (first instanceof ASTIdentifier && ((ASTIdentifier) first).getSymbol() < 0) { - ant = new StringBuilder(((ASTIdentifier) first).getName()); + if (first instanceof ASTIdentifier) { + if (((ASTIdentifier) first).getSymbol() < 0) { + ant = new StringBuilder(((ASTIdentifier) first).getName()); + } else { + break; + } } else { + pty = false; break; } } @@ -1299,10 +1045,14 @@ public class Interpreter extends ParserV break; } } - if (object == null && antish && ant != null && !isTernaryProtected(node)) { - boolean undefined = !(context.has(ant.toString()) || isLocalVariable(node, 0)); - // variable unknown in context and not a local - return unsolvableVariable(node, ant.toString(), undefined); + if (object == null && !isTernaryProtected(node)) { + if (antish && ant != null) { + boolean undefined = !(context.has(ant.toString()) || isLocalVariable(node, 0)); + // variable unknown in context and not a local + return unsolvableVariable(node, ant.toString(), undefined); + } else if (!pty) { + return unsolvableProperty(node, "<null>.<?>", null); + } } return object; } @@ -1495,11 +1245,11 @@ public class Interpreter extends ParserV } if (property == null) { // no property, we fail - throw new JexlException(propertyNode, "property is null"); + return unsolvableProperty(propertyNode, "<?>.<null>", null); } if (object == null) { // no object, we fail - throw new JexlException(objectNode, "bean is null"); + return unsolvableProperty(objectNode, "<null>.<?>", null); } // 3: one before last, assign if (assignop != null) { @@ -1536,7 +1286,7 @@ public class Interpreter extends ParserV object = data; if (object == null) { // no object, we fail - throw new JexlException(objectNode, "object is null"); + return unsolvableMethod(objectNode, "<null>.<?>(...)"); } } else { method = methodNode.jjtAccept(this, null); @@ -1545,7 +1295,7 @@ public class Interpreter extends ParserV for (int a = 1; a < node.jjtGetNumChildren(); ++a) { if (result == null) { // no method, we fail - throw new JexlException(methodNode, "method is null"); + return unsolvableMethod(methodNode, "<?>.<null>(...)"); } ASTArguments argNode = (ASTArguments) node.jjtGetChild(a); result = call(node, object, result, argNode); @@ -1849,10 +1599,10 @@ public class Interpreter extends ParserV return eval; } return unsolvableMethod(node, methodName); - } catch (JexlException.Method xmethod) { - throw xmethod; + } catch (JexlException xthru) { + throw xthru; } catch (Exception xany) { - xjexl = invocationException(node, methodName, xany); + xjexl = exceptionOnInvocation(node, methodName, xany); } return invocationFailed(xjexl); } @@ -1901,11 +1651,11 @@ public class Interpreter extends ParserV node.jjtSetValue(ctor); } return instance; - } catch (JexlException.Method xmethod) { - throw xmethod; + } catch (JexlException xthru) { + throw xthru; } catch (Exception xany) { String dbgStr = cobject != null ? cobject.toString() : null; - xjexl = invocationException(node, dbgStr, xany); + xjexl = exceptionOnInvocation(node, dbgStr, xany); } return invocationFailed(xjexl); } @@ -2117,15 +1867,15 @@ public class Interpreter extends ParserV Object result = processAnnotation(aname, argv, jstmt); // not processing an annotation is an error if (!processed[0]) { - annotationError(anode, aname, null); + return annotationError(anode, aname, null); + } else { + return result; } - return result; } catch(JexlException xjexl) { throw xjexl; } catch(Exception xany) { - annotationError(anode, aname, xany); + return annotationError(anode, aname, xany); } - return null; } /** Added: commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/InterpreterBase.java URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/InterpreterBase.java?rev=1754746&view=auto ============================================================================== --- commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/InterpreterBase.java (added) +++ commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/InterpreterBase.java Mon Aug 1 13:07:22 2016 @@ -0,0 +1,333 @@ +/* + * 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 org.apache.commons.jexl3.internal; + + +import org.apache.commons.jexl3.JexlArithmetic; +import org.apache.commons.jexl3.JexlContext; +import org.apache.commons.jexl3.JexlEngine; +import org.apache.commons.jexl3.JexlException; +import org.apache.commons.jexl3.JexlOperator; +import org.apache.commons.jexl3.introspection.JexlMethod; +import org.apache.commons.jexl3.introspection.JexlUberspect; +import org.apache.commons.jexl3.parser.JexlNode; +import org.apache.commons.jexl3.parser.ParserVisitor; + + +import org.apache.commons.logging.Log; + +/** + * The helper base of an interpreter of JEXL syntax. + * @since 3.0 + */ +public abstract class InterpreterBase extends ParserVisitor { + /** The JEXL engine. */ + protected final Engine jexl; + /** The logger. */ + protected final Log logger; + /** The uberspect. */ + protected final JexlUberspect uberspect; + /** The arithmetic handler. */ + protected final JexlArithmetic arithmetic; + /** The context to store/retrieve variables. */ + protected final JexlContext context; + /** Cancellation support. */ + protected volatile boolean cancelled = false; + /** Empty parameters for method matching. */ + protected static final Object[] EMPTY_PARAMS = new Object[0]; + + /** + * Creates an interpreter base. + * @param engine the engine creating this interpreter + * @param aContext the context to evaluate expression + */ + protected InterpreterBase(Engine engine, JexlContext aContext) { + this.jexl = engine; + this.logger = jexl.logger; + this.uberspect = jexl.uberspect; + this.context = aContext != null ? aContext : Engine.EMPTY_CONTEXT; + if (this.context instanceof JexlEngine.Options) { + JexlEngine.Options opts = (JexlEngine.Options) context; + this.arithmetic = jexl.arithmetic.options(opts); + if (!arithmetic.getClass().equals(jexl.arithmetic.getClass())) { + logger.error("expected arithmetic to be " + jexl.arithmetic.getClass().getSimpleName() + + ", got " + arithmetic.getClass().getSimpleName() + ); + } + } else { + this.arithmetic = jexl.arithmetic; + } + } + + /** Java7 AutoCloseable interface defined?. */ + protected static final Class<?> AUTOCLOSEABLE; + static { + Class<?> c; + try { + c = Class.forName("java.lang.AutoCloseable"); + } catch (ClassNotFoundException xclass) { + c = null; + } + AUTOCLOSEABLE = c; + } + + /** + * Attempt to call close() if supported. + * <p>This is used when dealing with auto-closeable (duck-like) objects + * @param closeable the object we'd like to close + */ + protected void closeIfSupported(Object closeable) { + if (closeable != null) { + //if (AUTOCLOSEABLE == null || AUTOCLOSEABLE.isAssignableFrom(closeable.getClass())) { + JexlMethod mclose = uberspect.getMethod(closeable, "close", EMPTY_PARAMS); + if (mclose != null) { + try { + mclose.invoke(closeable, EMPTY_PARAMS); + } catch (Exception xignore) { + logger.warn(xignore); + } + } + //} + } + } + + /** + * Whether this interpreter is currently evaluating with a strict engine flag. + * @return true if strict engine, false otherwise + */ + protected boolean isStrictEngine() { + if (this.context instanceof JexlEngine.Options) { + JexlEngine.Options opts = (JexlEngine.Options) context; + Boolean strict = opts.isStrict(); + if (strict != null) { + return strict.booleanValue(); + } + } + return jexl.isStrict(); + } + + /** + * Whether this interpreter is currently evaluating with a silent mode. + * @return true if silent, false otherwise + */ + protected boolean isSilent() { + if (this.context instanceof JexlEngine.Options) { + JexlEngine.Options opts = (JexlEngine.Options) context; + Boolean silent = opts.isSilent(); + if (silent != null) { + return silent.booleanValue(); + } + } + return jexl.isSilent(); + } + + /** @return true if interrupt throws a JexlException.Cancel. */ + protected boolean isCancellable() { + if (this.context instanceof JexlEngine.Options) { + JexlEngine.Options opts = (JexlEngine.Options) context; + Boolean ocancellable = opts.isCancellable(); + if (ocancellable != null) { + return ocancellable.booleanValue(); + } + } + return jexl.cancellable; + } + + /** + * Finds the node causing a NPE for diadic operators. + * @param xrt the RuntimeException + * @param node the parent node + * @param left the left argument + * @param right the right argument + * @return the left, right or parent node + */ + protected JexlNode findNullOperand(RuntimeException xrt, JexlNode node, Object left, Object right) { + if (xrt instanceof JexlArithmetic.NullOperand) { + if (left == null) { + return node.jjtGetChild(0); + } + if (right == null) { + return node.jjtGetChild(1); + } + } + return node; + } + + /** + * Triggered when a variable can not be resolved. + * @param node the node where the error originated from + * @param var the variable name + * @param undef whether the variable is undefined or null + * @return throws JexlException if strict and not silent, null otherwise + */ + protected Object unsolvableVariable(JexlNode node, String var, boolean undef) { + if (isStrictEngine()) { + if (isSilent()) { + logger.warn(JexlException.variableError(node, var, undef)); + } else if (undef || arithmetic.isStrict()){ + throw new JexlException.Variable(node, var, undef); + } + } else if (logger.isDebugEnabled()) { + logger.debug(JexlException.variableError(node, var, undef)); + } + return null; + } + + /** + * Triggered when a method can not be resolved. + * @param node the node where the error originated from + * @param method the method name + * @return throws JexlException if strict and not silent, null otherwise + */ + protected Object unsolvableMethod(JexlNode node, String method) { + if (isStrictEngine()) { + if (isSilent()) { + logger.warn(JexlException.methodError(node, method)); + } else { + throw new JexlException.Method(node, method); + } + } else if (logger.isDebugEnabled()) { + logger.debug(JexlException.methodError(node, method)); + } + return null; + } + + /** + * Triggered when a property can not be resolved. + * @param node the node where the error originated from + * @param var the property name + * @param cause the cause if any + * @return throws JexlException if strict and not silent, null otherwise + */ + protected Object unsolvableProperty(JexlNode node, String var, Throwable cause) { + if (isStrictEngine()) { + if (isSilent()) { + logger.warn(JexlException.propertyError(node, var), cause); + } else { + throw new JexlException.Property(node, var, cause); + } + } else if (logger.isDebugEnabled()) { + logger.debug(JexlException.propertyError(node, var), cause); + } + return null; + } + + /** + * Triggered when an operator fails. + * @param node the node where the error originated from + * @param operator the method name + * @param cause the cause of error (if any) + * @return throws JexlException if strict and not silent, null otherwise + */ + protected Object operatorError(JexlNode node, JexlOperator operator, Throwable cause) { + if (isStrictEngine()) { + if (isSilent()) { + logger.warn(JexlException.operatorError(node, operator.getOperatorSymbol()), cause); + } else { + throw new JexlException.Operator(node, operator.getOperatorSymbol(), cause); + } + } else if (logger.isDebugEnabled()) { + logger.debug(JexlException.operatorError(node, operator.getOperatorSymbol()), cause); + } + return null; + } + + /** + * Triggered when method, function or constructor invocation fails. + * @param xjexl the JexlException wrapping the original error + * @return throws a JexlException if strict and not silent, null otherwise + */ + protected Object invocationFailed(JexlException xjexl) { + if (xjexl instanceof JexlException.Return + || xjexl instanceof JexlException.Cancel) { + throw xjexl; + } + if (isStrictEngine()) { + if (isSilent()) { + logger.warn(xjexl.getMessage(), xjexl.getCause()); + } else { + throw xjexl; + } + } else if (logger.isDebugEnabled()) { + logger.debug(xjexl); + } + return null; + } + + /** + * Wraps an exception thrown by an invocation. + * @param node the node triggering the exception + * @param methodName the method/function name + * @param xany the cause + * @return a JexlException + */ + protected JexlException exceptionOnInvocation(JexlNode node, String methodName, Exception xany) { + Throwable cause = xany.getCause(); + if (cause instanceof JexlException) { + return (JexlException) cause; + } + if (cause instanceof InterruptedException) { + cancelled = true; + return new JexlException.Cancel(node); + } + return new JexlException(node, methodName, xany); + } + + /** + * Triggered when an annotation processing fails. + * @param node the node where the error originated from + * @param annotation the annotation name + * @param cause the cause of error (if any) + * @return throws a JexlException if strict and not silent, null otherwise + */ + protected Object annotationError(JexlNode node, String annotation, Throwable cause) { + if (isStrictEngine()) { + if (isSilent()) { + logger.warn(JexlException.annotationError(node, annotation), cause); + } else { + throw new JexlException.Annotation(node, annotation, cause); + } + } else if (logger.isDebugEnabled()) { + logger.debug(JexlException.annotationError(node, annotation), cause); + } + return null; + } + + /** + * Cancels this evaluation, setting the cancel flag that will result in a JexlException.Cancel to be thrown. + * @return false if already cancelled, true otherwise + */ + protected synchronized boolean cancel() { + if (cancelled) { + return false; + } else { + cancelled = true; + return true; + } + } + + /** + * Checks whether this interpreter execution was canceled due to thread interruption. + * @return true if canceled, false otherwise + */ + protected synchronized boolean isCancelled() { + if (!cancelled) { + cancelled = Thread.currentThread().isInterrupted(); + } + return cancelled; + } +} Propchange: commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/InterpreterBase.java ------------------------------------------------------------------------------ svn:eol-style = native Modified: commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/Operators.java URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/Operators.java?rev=1754746&r1=1754745&r2=1754746&view=diff ============================================================================== --- commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/Operators.java (original) +++ commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/Operators.java Mon Aug 1 13:07:22 2016 @@ -24,7 +24,6 @@ import org.apache.commons.jexl3.introspe import org.apache.commons.jexl3.introspection.JexlUberspect; import org.apache.commons.jexl3.parser.JexlNode; -import java.util.Iterator; /** * Helper class to deal with operator overloading and specifics. * @since 3.0 @@ -92,45 +91,7 @@ public class Operators { return result; } } catch (Exception xany) { - interpreter.operatorError(node, operator, xany); - } - } - return JexlEngine.TRY_FAILED; - } - - /** - * Attempts to call an overloaded forEach operator. - * <p> - * This takes care of finding and caching the operator method when appropriate - * @param node the syntactic node - * @param arg the argument - * @return the result of the operator evaluation or TRY_FAILED - */ - protected Object tryForeachOverload(JexlNode node, Object arg) { - if (operators != null && operators.overloads(JexlOperator.FOR_EACH)) { - final JexlArithmetic arithmetic = interpreter.arithmetic; - final boolean cache = interpreter.cache; - if (cache) { - Object cached = node.jjtGetValue(); - if (cached instanceof JexlMethod) { - JexlMethod me = (JexlMethod) cached; - Object eval = me.tryInvoke(JexlOperator.FOR_EACH.getMethodName(), arithmetic, arg); - if (!me.tryFailed(eval)) { - return eval; - } - } - } - try { - JexlMethod vm = operators.getOperator(JexlOperator.FOR_EACH, arg); - if (vm != null && Iterator.class.isAssignableFrom(vm.getReturnType())) { - Object result = vm.invoke(arithmetic, arg); - if (cache) { - node.jjtSetValue(vm); - } - return result; - } - } catch (Exception xany) { - interpreter.operatorError(node, JexlOperator.FOR_EACH, xany); + return interpreter.operatorError(node, operator, xany); } } return JexlEngine.TRY_FAILED; Modified: commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/AnnotationTest.java URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/AnnotationTest.java?rev=1754746&r1=1754745&r2=1754746&view=diff ============================================================================== --- commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/AnnotationTest.java (original) +++ commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/AnnotationTest.java Mon Aug 1 13:07:22 2016 @@ -124,33 +124,59 @@ public class AnnotationTest extends Jexl @Test public void testError() throws Exception { + testError(true); + testError(false); + } + + private void testError(boolean silent) throws Exception { + CaptureLog log = new CaptureLog(); AnnotationContext jc = new AnnotationContext(); - JexlEngine jexl = new JexlBuilder().create(); + JexlEngine jexl = new JexlBuilder().logger(log).strict(true).silent(silent).create(); JexlScript e = jexl.createScript("@error('42') { return 42; }"); try { Object r = e.execute(jc); - Assert.fail("should have failed"); + if (!silent) { + Assert.fail("should have failed"); + } else { + Assert.assertEquals(1, log.count("warn")); + } } catch (JexlException.Annotation xjexl) { Assert.assertEquals("error", xjexl.getAnnotation()); } Assert.assertEquals(1, jc.getCount()); Assert.assertTrue(jc.getNames().contains("error")); Assert.assertTrue(jc.getNames().contains("42")); + if (!silent) { + Assert.assertEquals(0, log.count("warn")); + } } @Test public void testUnknown() throws Exception { + testUnknown(true); + testUnknown(false); + } + + private void testUnknown(boolean silent) throws Exception { + CaptureLog log = new CaptureLog(); AnnotationContext jc = new AnnotationContext(); - JexlEngine jexl = new JexlBuilder().create(); + JexlEngine jexl = new JexlBuilder().logger(log).strict(true).silent(silent).create(); JexlScript e = jexl.createScript("@unknown('42') { return 42; }"); try { Object r = e.execute(jc); - Assert.fail("should have failed"); + if (!silent) { + Assert.fail("should have failed"); + } else { + Assert.assertEquals(1, log.count("warn")); + } } catch (JexlException.Annotation xjexl) { Assert.assertEquals("unknown", xjexl.getAnnotation()); } Assert.assertEquals(1, jc.getCount()); Assert.assertTrue(jc.getNames().contains("unknown")); Assert.assertFalse(jc.getNames().contains("42")); + if (!silent) { + Assert.assertEquals(0, log.count("warn")); + } } } Modified: commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/ArithmeticOperatorTest.java URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/ArithmeticOperatorTest.java?rev=1754746&r1=1754745&r2=1754746&view=diff ============================================================================== --- commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/ArithmeticOperatorTest.java (original) +++ commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/ArithmeticOperatorTest.java Mon Aug 1 13:07:22 2016 @@ -369,6 +369,10 @@ public class ArithmeticOperatorTest exte public Date now() { return new Date(System.currentTimeMillis()); } + + public Date multiply(Date d0, Date d1) { + throw new ArithmeticException("unsupported"); + } } public static class DateContext extends MapContext { @@ -388,6 +392,34 @@ public class ArithmeticOperatorTest exte } } + @Test + public void testOperatorError() throws Exception { + testOperatorError(true); + testOperatorError(false); + } + + private void testOperatorError(boolean silent) throws Exception { + CaptureLog log = new CaptureLog(); + DateContext jc = new DateContext(); + Date d = new Date(); + JexlEngine jexl = new JexlBuilder().logger(log).strict(true).silent(silent).cache(32) + .arithmetic(new DateArithmetic(true)).create(); + JexlScript expr0 = jexl.createScript("date * date", "date"); + try { + Object value0 = expr0.execute(jc, d); + if (!silent) { + Assert.fail("should have failed"); + } else { + Assert.assertEquals(1, log.count("warn")); + } + } catch(JexlException.Operator xop) { + Assert.assertEquals("*", xop.getSymbol()); + } + if (!silent) { + Assert.assertEquals(0, log.count("warn")); + } + } + @Test public void testDateArithmetic() throws Exception { Date d = new Date(); Added: commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/CaptureLog.java URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/CaptureLog.java?rev=1754746&view=auto ============================================================================== --- commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/CaptureLog.java (added) +++ commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/CaptureLog.java Mon Aug 1 13:07:22 2016 @@ -0,0 +1,137 @@ +/* + * Copyright 2016 The Apache Software Foundation. + * + * Licensed 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 org.apache.commons.jexl3; + +import java.util.ArrayList; +import java.util.List; +import org.apache.commons.logging.Log; + +/** + * A log implementation to help control tests results. + */ +public class CaptureLog implements Log { + private List<Object[]> captured = new ArrayList<Object[]>(); + + static Object caller() { + StackTraceElement[] stack = new Exception().fillInStackTrace().getStackTrace(); + return stack[2]; + } + + public boolean isEmpty() { + return captured.isEmpty(); + } + + public int count(String type) { + int count = 0; + for (Object[] l : captured) { + if (type.equals(l[0].toString())) { + count += 1; + } + } + return count; + } + + @Override + public void debug(Object o) { + captured.add(new Object[]{"debug", caller(), o}); + } + + @Override + public void debug(Object o, Throwable thrwbl) { + captured.add(new Object[]{"debug", caller(), o, thrwbl}); + } + + @Override + public void error(Object o) { + captured.add(new Object[]{"error", caller(), o}); + } + + @Override + public void error(Object o, Throwable thrwbl) { + captured.add(new Object[]{"error", caller(), o, thrwbl}); + } + + @Override + public void fatal(Object o) { + captured.add(new Object[]{"fatal", caller(), o}); + } + + @Override + public void fatal(Object o, Throwable thrwbl) { + captured.add(new Object[]{"fatal", caller(), o, thrwbl}); + } + + @Override + public void info(Object o) { + captured.add(new Object[]{"info", caller(), o}); + } + + @Override + public void info(Object o, Throwable thrwbl) { + captured.add(new Object[]{"info", caller(), o, thrwbl}); + } + + @Override + public boolean isDebugEnabled() { + return true; + } + + @Override + public boolean isErrorEnabled() { + return true; + } + + @Override + public boolean isFatalEnabled() { + return true; + } + + @Override + public boolean isInfoEnabled() { + return true; + } + + @Override + public boolean isTraceEnabled() { + return true; + } + + @Override + public boolean isWarnEnabled() { + return true; + } + + @Override + public void trace(Object o) { + captured.add(new Object[]{"trace", caller(), o}); + } + + @Override + public void trace(Object o, Throwable thrwbl) { + captured.add(new Object[]{"trace", caller(), o, thrwbl}); + } + + @Override + public void warn(Object o) { + captured.add(new Object[]{"warn", caller(), o}); + } + + @Override + public void warn(Object o, Throwable thrwbl) { + captured.add(new Object[]{"warn", caller(), o, thrwbl}); + } + +} Propchange: commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/CaptureLog.java ------------------------------------------------------------------------------ svn:eol-style = native Modified: commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/ExceptionTest.java URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/ExceptionTest.java?rev=1754746&r1=1754745&r2=1754746&view=diff ============================================================================== --- commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/ExceptionTest.java (original) +++ commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/ExceptionTest.java Mon Aug 1 13:07:22 2016 @@ -16,7 +16,10 @@ */ package org.apache.commons.jexl3; +import java.util.ArrayList; +import java.util.List; import org.apache.commons.jexl3.internal.Engine; +import org.apache.commons.logging.Log; import org.junit.Assert; import org.junit.Test; @@ -160,4 +163,52 @@ public class ExceptionTest extends JexlT Assert.assertTrue(msg.indexOf("c.e") > 0); } } + + + @Test + public void test206() throws Exception { + String src = "null.1 = 2; return 42"; + doTest206(src, false, false); + doTest206(src, false, true); + doTest206(src, true, false); + doTest206(src, true, true); + src = "x = null.1; return 42"; + doTest206(src, false, false); + doTest206(src, false, true); + doTest206(src, true, false); + doTest206(src, true, true); + src = "x = y.1; return 42"; + doTest206(src, false, false); + doTest206(src, false, true); + doTest206(src, true, false); + doTest206(src, true, true); + } + private void doTest206(String src, boolean strict, boolean silent) throws Exception { + CaptureLog l = new CaptureLog(); + JexlContext jc = new MapContext(); + JexlEngine jexl = new JexlBuilder().logger(l).strict(strict).silent(silent).create(); + JexlScript e; + Object r = -1; + e = jexl.createScript(src); + try { + r = e.execute(jc); + if (strict && !silent) { + Assert.fail("should have thrown an exception"); + } + } catch(JexlException xjexl) { + if (!strict || silent) { + Assert.fail("should not have thrown an exception"); + } + } + if (strict) { + if (silent && l.count("warn") == 0) { + Assert.fail("should have generated a warning"); + } + } else { + if (l.count("debug") == 0) { + Assert.fail("should have generated a debug"); + } + Assert.assertEquals(42, r); + } + } } Modified: commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/IssuesTest.java URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/IssuesTest.java?rev=1754746&r1=1754745&r2=1754746&view=diff ============================================================================== --- commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/IssuesTest.java (original) +++ commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/IssuesTest.java Mon Aug 1 13:07:22 2016 @@ -17,17 +17,19 @@ package org.apache.commons.jexl3; import org.apache.commons.jexl3.internal.Engine; -import java.math.BigDecimal; -import java.math.MathContext; -import java.util.HashMap; -import java.util.Map; import org.apache.commons.jexl3.internal.introspection.Uberspect; + import java.io.File; +import java.math.BigDecimal; +import java.math.MathContext; import java.net.URL; import java.util.ArrayList; import java.util.Arrays; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.Set; + import org.junit.Assert; import org.junit.Before; import org.junit.Test; Modified: commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/ScriptCallableTest.java URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/ScriptCallableTest.java?rev=1754746&r1=1754745&r2=1754746&view=diff ============================================================================== --- commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/ScriptCallableTest.java (original) +++ commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/ScriptCallableTest.java Mon Aug 1 13:07:22 2016 @@ -16,6 +16,7 @@ */ package org.apache.commons.jexl3; +import java.util.List; import java.util.concurrent.Callable; import java.util.concurrent.CancellationException; import java.util.concurrent.ExecutionException; @@ -27,6 +28,8 @@ import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import org.apache.commons.jexl3.internal.Script; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; import org.junit.Assert; import org.junit.Test; @@ -35,7 +38,7 @@ import org.junit.Test; */ @SuppressWarnings({"UnnecessaryBoxing", "AssertEqualsBetweenInconvertibleTypes"}) public class ScriptCallableTest extends JexlTestCase { - //Logger LOGGER = Logger.getLogger(VarTest.class.getName()); + //private Log logger = LogFactory.getLog(JexlEngine.class); public ScriptCallableTest() { super("ScriptCallableTest"); } @@ -64,6 +67,7 @@ public class ScriptCallableTest extends @Test public void testCallableCancel() throws Exception { + List<Runnable> lr = null; final Semaphore latch = new Semaphore(0); JexlContext ctxt = new MapContext(); ctxt.set("latch", latch); @@ -89,13 +93,15 @@ public class ScriptCallableTest extends // ok, ignore Assert.assertTrue(xexec.getCause() instanceof JexlException.Cancel); } finally { - executor.shutdown(); + lr = executor.shutdownNow(); } Assert.assertTrue(c.isCancelled()); + Assert.assertTrue(lr == null || lr.isEmpty()); } @Test public void testCallableTimeout() throws Exception { + List<Runnable> lr = null; final Semaphore latch = new Semaphore(0); JexlContext ctxt = new MapContext(); ctxt.set("latch", latch); @@ -114,14 +120,16 @@ public class ScriptCallableTest extends // ok, ignore future.cancel(true); } finally { - executor.shutdown(); + lr = executor.shutdownNow(); } Assert.assertTrue(future.isCancelled()); Assert.assertEquals(42, t); + Assert.assertTrue(lr.isEmpty()); } @Test public void testCallableClosure() throws Exception { + List<Runnable> lr = null; JexlScript e = JEXL.createScript("function(t) {while(t);}"); Callable<Object> c = e.callable(null, Boolean.TRUE); Object t = 42; @@ -135,10 +143,11 @@ public class ScriptCallableTest extends // ok, ignore future.cancel(true); } finally { - executor.shutdown(); + lr = executor.shutdownNow(); } Assert.assertTrue(future.isCancelled()); Assert.assertEquals(42, t); + Assert.assertTrue(lr.isEmpty()); } public static class TestContext extends MapContext implements JexlContext.NamespaceResolver { @@ -191,6 +200,7 @@ public class ScriptCallableTest extends @Test public void testNoWait() throws Exception { + List<Runnable> lr = null; JexlScript e = JEXL.createScript("wait(0)"); Callable<Object> c = e.callable(new TestContext()); @@ -201,12 +211,14 @@ public class ScriptCallableTest extends Assert.assertTrue(future.isDone()); Assert.assertEquals(0, t); } finally { - executor.shutdown(); + lr = executor.shutdownNow(); } + Assert.assertTrue(lr.isEmpty()); } @Test public void testWait() throws Exception { + List<Runnable> lr = null; JexlScript e = JEXL.createScript("wait(1)"); Callable<Object> c = e.callable(new TestContext()); @@ -216,12 +228,14 @@ public class ScriptCallableTest extends Object t = future.get(2, TimeUnit.SECONDS); Assert.assertEquals(1, t); } finally { - executor.shutdown(); + lr = executor.shutdownNow(); } + Assert.assertTrue(lr.isEmpty()); } @Test public void testCancelWait() throws Exception { + List<Runnable> lr = null; JexlScript e = JEXL.createScript("wait(10)"); Callable<Object> c = e.callable(new TestContext()); @@ -239,12 +253,14 @@ public class ScriptCallableTest extends Assert.assertTrue(future.isCancelled()); Assert.assertEquals(42, t); } finally { - executor.shutdown(); + lr = executor.shutdownNow(); } + Assert.assertTrue(lr.isEmpty()); } @Test public void testCancelWaitInterrupt() throws Exception { + List<Runnable> lr = null; JexlScript e = JEXL.createScript("waitInterrupt(42)"); Callable<Object> c = e.callable(new TestContext()); @@ -259,14 +275,16 @@ public class ScriptCallableTest extends // ok, ignore future.cancel(true); } finally { - executor.shutdown(); + lr = executor.shutdownNow(); } Assert.assertTrue(future.isCancelled()); Assert.assertEquals(42, t); + Assert.assertTrue(lr.isEmpty()); } @Test public void testCancelForever() throws Exception { + List<Runnable> lr = null; final Semaphore latch = new Semaphore(0); JexlContext ctxt = new TestContext(); ctxt.set("latch", latch); @@ -286,14 +304,16 @@ public class ScriptCallableTest extends // ok, ignore future.cancel(true); } finally { - executor.shutdown(); + lr = executor.shutdownNow(); } Assert.assertTrue(future.isCancelled()); Assert.assertEquals(42, t); + Assert.assertTrue(lr.isEmpty()); } @Test public void testCancelLoopWait() throws Exception { + List<Runnable> lr = null; JexlScript e = JEXL.createScript("while (true) { wait(10) }"); Callable<Object> c = e.callable(new TestContext()); @@ -307,10 +327,11 @@ public class ScriptCallableTest extends } catch (TimeoutException xtimeout) { future.cancel(true); } finally { - executor.shutdown(); + lr = executor.shutdownNow(); } Assert.assertTrue(future.isCancelled()); Assert.assertEquals(42, t); + Assert.assertTrue(lr.isEmpty()); } @Test @@ -345,6 +366,7 @@ public class ScriptCallableTest extends * @throws Exception if there is a regression */ private void runInterrupt(JexlEngine jexl) throws Exception { + List<Runnable> lr = null; ExecutorService exec = Executors.newFixedThreadPool(2); try { JexlContext ctxt = new TestContext(); @@ -411,7 +433,7 @@ public class ScriptCallableTest extends } }; exec.submit(cancels); - t = fc.get(); + t = f.get(100L, TimeUnit.MILLISECONDS); Assert.fail("should be cancelled"); } catch (CancellationException xexec) { // this is the expected result @@ -452,8 +474,9 @@ public class ScriptCallableTest extends } Assert.assertNotEquals(42, t); } finally { - exec.shutdown(); + lr = exec.shutdownNow(); } + Assert.assertTrue(lr.isEmpty()); } @Test Modified: commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/ScriptTest.java URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/ScriptTest.java?rev=1754746&r1=1754745&r2=1754746&view=diff ============================================================================== --- commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/ScriptTest.java (original) +++ commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/ScriptTest.java Mon Aug 1 13:07:22 2016 @@ -28,6 +28,7 @@ import org.junit.Test; @SuppressWarnings({"UnnecessaryBoxing", "AssertEqualsBetweenInconvertibleTypes"}) public class ScriptTest extends JexlTestCase { static final String TEST1 = "src/test/scripts/test1.jexl"; + static final String TEST_ADD = "src/test/scripts/testAdd.jexl"; // test class for testScriptUpdatesContext // making this class private static will cause the test to fail. @@ -74,8 +75,18 @@ public class ScriptTest extends JexlTest Assert.assertEquals("Wrong result", new Integer(7), result); } + @Test public void testArgScriptFromFile() throws Exception { + File testScript = new File(TEST_ADD); + JexlScript s = JEXL.createScript(testScript,new String[]{"x","y"}); + JexlContext jc = new MapContext(); + jc.set("out", System.out); + Object result = s.execute(jc, 13, 29); + Assert.assertNotNull("No result", result); + Assert.assertEquals("Wrong result", new Integer(42), result); + } + @Test public void testScriptFromURL() throws Exception { - URL testUrl = new File("src/test/scripts/test1.jexl").toURI().toURL(); + URL testUrl = new File(TEST1).toURI().toURL(); JexlScript s = JEXL.createScript(testUrl); JexlContext jc = new MapContext(); jc.set("out", System.out); @@ -84,6 +95,16 @@ public class ScriptTest extends JexlTest Assert.assertEquals("Wrong result", new Integer(7), result); } + @Test public void testArgScriptFromURL() throws Exception { + URL testUrl = new File(TEST_ADD).toURI().toURL(); + JexlScript s = JEXL.createScript(testUrl,new String[]{"x","y"}); + JexlContext jc = new MapContext(); + jc.set("out", System.out); + Object result = s.execute(jc, 13, 29); + Assert.assertNotNull("No result", result); + Assert.assertEquals("Wrong result", new Integer(42), result); + } + @Test public void testScriptUpdatesContext() throws Exception { String jexlCode = "resultat.setCode('OK')"; JexlExpression e = JEXL.createExpression(jexlCode); Modified: commons/proper/jexl/trunk/src/test/resources/log4j.xml URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/test/resources/log4j.xml?rev=1754746&r1=1754745&r2=1754746&view=diff ============================================================================== --- commons/proper/jexl/trunk/src/test/resources/log4j.xml (original) +++ commons/proper/jexl/trunk/src/test/resources/log4j.xml Mon Aug 1 13:07:22 2016 @@ -21,7 +21,7 @@ <param name="ConversionPattern" value="%d{ABSOLUTE} %5p %c{1}:%L - %m%n"/> </layout> </appender> - + <logger name="org.apache.commons.jexl3" additivity="false"> <level value="error"/> <appender-ref ref="Console"/> Copied: commons/proper/jexl/trunk/src/test/scripts/testAdd.jexl (from r1719037, commons/proper/jexl/trunk/src/test/scripts/testA.jexl) URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/test/scripts/testAdd.jexl?p2=commons/proper/jexl/trunk/src/test/scripts/testAdd.jexl&p1=commons/proper/jexl/trunk/src/test/scripts/testA.jexl&r1=1719037&r2=1754746&rev=1754746&view=diff ============================================================================== --- commons/proper/jexl/trunk/src/test/scripts/testA.jexl (original) +++ commons/proper/jexl/trunk/src/test/scripts/testAdd.jexl Mon Aug 1 13:07:22 2016 @@ -14,20 +14,5 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -$out.println("Start Script Prompt Dependencies"); -//attention company doit être multivalué -/*var vCountry = $in['company']; -$out.add("The user input is the following: "); -for (var g : members(vCountry)) -{ - $out.add("Country: " + name(g)); -}*/ - -var vCountry = $in['company']; -var vResp = $in['responsible']; -{ - $out.add("Then, " + vResp.name + " is responsible for : " + vCountry.name); -} - -$out.println("Stop Script Prompt Dependencies"); +//(x, y)->{ x + y } +x + y