You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by he...@apache.org on 2016/04/28 15:59:39 UTC
svn commit: r1741426 - in /commons/proper/jexl/trunk: ./
src/main/java/org/apache/commons/jexl3/internal/ src/site/xdoc/
src/test/java/org/apache/commons/jexl3/
Author: henrib
Date: Thu Apr 28 13:59:38 2016
New Revision: 1741426
URL: http://svn.apache.org/viewvc?rev=1741426&view=rev
Log:
JEXL:
Fixing JEXL-193, refined handling of interruption (InterruptedException handling & interrupted()), more checks around cancellation (blocks,set/map/array literals), refactored internal Callable, more tests
Modified:
commons/proper/jexl/trunk/RELEASE-NOTES.txt
commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/Closure.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/Script.java
commons/proper/jexl/trunk/src/site/xdoc/changes.xml
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
Modified: commons/proper/jexl/trunk/RELEASE-NOTES.txt
URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/RELEASE-NOTES.txt?rev=1741426&r1=1741425&r2=1741426&view=diff
==============================================================================
--- commons/proper/jexl/trunk/RELEASE-NOTES.txt (original)
+++ commons/proper/jexl/trunk/RELEASE-NOTES.txt Thu Apr 28 13:59:38 2016
@@ -28,6 +28,7 @@ Version 3.0.1 is a micro release to fix
Bugs Fixed in 3.0.1:
====================
+* JEXL-193: InterruptedException is swallowed in function call in silent and non-strict mode
* JEXL-192: Invalid return type when expected result is null
* JEXL-191: Jexl3 unsolvable property exception when using enum
* JEXL-190: local function within context is not resolved if function resolver class without namespace is specified
Modified: commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/Closure.java
URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/Closure.java?rev=1741426&r1=1741425&r2=1741426&view=diff
==============================================================================
--- commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/Closure.java (original)
+++ commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/Closure.java Thu Apr 28 13:59:38 2016
@@ -20,7 +20,6 @@ import org.apache.commons.jexl3.JexlCont
import org.apache.commons.jexl3.parser.ASTJexlLambda;
import org.apache.commons.jexl3.parser.JexlNode;
-import java.util.concurrent.Callable;
/**
* A Script closure.
@@ -125,23 +124,16 @@ public class Closure extends Script {
}
@Override
- public Callable<Object> callable(JexlContext context, Object... args) {
+ public Callable callable(JexlContext context, Object... args) {
Scope.Frame local = null;
if (frame != null) {
local = frame.assign(args);
}
- final Interpreter interpreter = jexl.createInterpreter(context, local);
- return new Callable<Object>() {
- /** Use interpreter as marker for not having run. */
- private Object result = interpreter;
-
+ return new Callable(jexl.createInterpreter(context, local)) {
@Override
- public Object call() throws Exception {
- if (result == interpreter) {
- JexlNode block = script.jjtGetChild(script.jjtGetNumChildren() - 1);
- result = interpreter.interpret(block);
- }
- return result;
+ public Object interpret() {
+ JexlNode block = script.jjtGetChild(script.jjtGetNumChildren() - 1);
+ return interpreter.interpret(block);
}
};
}
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=1741426&r1=1741425&r2=1741426&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 Thu Apr 28 13:59:38 2016
@@ -200,14 +200,17 @@ public class Interpreter extends ParserV
}
return node.jjtAccept(this, null);
} catch (JexlException.Return xreturn) {
- Object value = xreturn.getValue();
- return value;
+ return xreturn.getValue();
+ } catch (JexlException.Cancel xcancel) {
+ cancelled |= Thread.interrupted();
+ if (!silent && strictEngine) {
+ throw xcancel.clean();
+ }
} catch (JexlException xjexl) {
- if (silent) {
- logger.warn(xjexl.getMessage(), xjexl.getCause());
- return null;
+ if (!silent) {
+ throw xjexl.clean();
}
- throw xjexl.clean();
+ logger.warn(xjexl.getMessage(), xjexl.getCause());
} finally {
if (functors != null && AUTOCLOSEABLE != null) {
for (Object functor : functors.values()) {
@@ -225,6 +228,7 @@ public class Interpreter extends ParserV
jexl.putThreadLocal(local);
}
}
+ return null;
}
/** Java7 AutoCloseable interface defined?. */
@@ -336,21 +340,51 @@ public class Interpreter extends ParserV
if (!silent) {
logger.warn(xjexl.getMessage(), xjexl.getCause());
}
- if (strictEngine || xjexl instanceof JexlException.Return) {
+ if (strictEngine
+ || xjexl instanceof JexlException.Return
+ || xjexl instanceof JexlException.Cancel) {
throw xjexl;
}
return null;
}
/**
- * Checks whether this interpreter execution was cancelled due to thread interruption.
- * @return true if cancelled, false otherwise
+ * 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 boolean isCancelled() {
- if (cancelled || Thread.currentThread().isInterrupted()) {
+ 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 cancelled;
+ return new JexlException(node, methodName, xany);
+ }
+
+ /**
+ * 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;
+ }
+
+ /**
+ * Cancels this evaluation, setting the cancel flag that will result in a JexlException.Cancel to be thrown.
+ * @return true in all cases
+ */
+ protected boolean cancel() {
+ cancelled = true;
+ return cancelled;
}
/**
@@ -722,6 +756,9 @@ public class Interpreter extends ParserV
int numChildren = node.jjtGetNumChildren();
Object result = null;
for (int i = 0; i < numChildren; i++) {
+ if (isCancelled()) {
+ throw new JexlException.Cancel(node);
+ }
result = node.jjtGetChild(i).jjtAccept(this, data);
}
return result;
@@ -897,6 +934,9 @@ public class Interpreter extends ParserV
if (ab != null) {
boolean extended = false;
for (int i = 0; i < childCount; i++) {
+ if (isCancelled()) {
+ throw new JexlException.Cancel(node);
+ }
JexlNode child = node.jjtGetChild(i);
if (child instanceof ASTExtendedLiteral) {
extended = true;
@@ -922,6 +962,9 @@ public class Interpreter extends ParserV
JexlArithmetic.SetBuilder mb = arithmetic.setBuilder(childCount);
if (mb != null) {
for (int i = 0; i < childCount; i++) {
+ if (isCancelled()) {
+ throw new JexlException.Cancel(node);
+ }
Object entry = node.jjtGetChild(i).jjtAccept(this, data);
mb.add(entry);
}
@@ -937,6 +980,9 @@ public class Interpreter extends ParserV
JexlArithmetic.MapBuilder mb = arithmetic.mapBuilder(childCount);
if (mb != null) {
for (int i = 0; i < childCount; i++) {
+ if (isCancelled()) {
+ throw new JexlException.Cancel(node);
+ }
Object[] entry = (Object[]) (node.jjtGetChild(i)).jjtAccept(this, data);
mb.put(entry[0], entry[1]);
}
@@ -1016,6 +1062,9 @@ public class Interpreter extends ParserV
for (int i = 0; i < numChildren; i++) {
JexlNode child = node.jjtGetChild(i);
result = child.jjtAccept(this, data);
+ if (isCancelled()) {
+ throw new JexlException.Cancel(child);
+ }
}
return result;
}
@@ -1067,6 +1116,9 @@ public class Interpreter extends ParserV
return null;
}
Object index = nindex.jjtAccept(this, null);
+ if (isCancelled()) {
+ throw new JexlException.Cancel(node);
+ }
object = getAttribute(object, index, nindex);
}
return object;
@@ -1135,6 +1187,9 @@ public class Interpreter extends ParserV
}
// attempt to evaluate the property within the object (visit(ASTIdentifierAccess node))
object = objectNode.jjtAccept(this, object);
+ if (isCancelled()) {
+ throw new JexlException.Cancel(node);
+ }
if (object != null) {
// disallow mixing antish variable & bean with same root; avoid ambiguity
antish = false;
@@ -1710,7 +1765,7 @@ public class Interpreter extends ParserV
} catch (JexlException.Method xmethod) {
throw xmethod;
} catch (Exception xany) {
- xjexl = new JexlException(node, methodName, xany);
+ xjexl = invocationException(node, methodName, xany);
}
return invocationFailed(xjexl);
}
@@ -1763,7 +1818,7 @@ public class Interpreter extends ParserV
throw xmethod;
} catch (Exception xany) {
String dbgStr = cobject != null ? cobject.toString() : null;
- xjexl = new JexlException(node, dbgStr, xany);
+ xjexl = invocationException(node, dbgStr, xany);
}
return invocationFailed(xjexl);
}
Modified: commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/Script.java
URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/Script.java?rev=1741426&r1=1741425&r2=1741426&view=diff
==============================================================================
--- commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/Script.java (original)
+++ commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/Script.java Thu Apr 28 13:59:38 2016
@@ -26,7 +26,6 @@ import org.apache.commons.jexl3.parser.J
import java.util.List;
import java.util.Map;
import java.util.Set;
-import java.util.concurrent.Callable;
/**
* <p>A JexlScript implementation.</p>
@@ -322,7 +321,7 @@ public class Script implements JexlScrip
* @return the callable
*/
@Override
- public Callable<Object> callable(JexlContext context) {
+ public Callable callable(JexlContext context) {
return callable(context, (Object[]) null);
}
@@ -335,20 +334,45 @@ public class Script implements JexlScrip
* @return the callable
*/
@Override
- public Callable<Object> callable(JexlContext context, Object... args) {
- final Interpreter interpreter = jexl.createInterpreter(context, script.createFrame(args));
- return new Callable<Object>() {
- /** Use interpreter as marker for not having run. */
- private Object result = interpreter;
+ public Callable callable(JexlContext context, Object... args) {
+ return new Callable(jexl.createInterpreter(context, script.createFrame(args)));
+ }
+
+ /**
+ * Implements the Future and Callable interfaces to help delegation.
+ */
+ public class Callable implements java.util.concurrent.Callable<Object> {
+ /** The actual interpreter. */
+ protected final Interpreter interpreter;
+ /** Use interpreter as marker for not having run. */
+ protected Object result;
+
+ /**
+ * The base constructor.
+ * @param intrprtr the interpreter to use
+ */
+ protected Callable(Interpreter intrprtr) {
+ this.interpreter = intrprtr;
+ this.result = intrprtr;
+ }
- @Override
- public Object call() throws Exception {
+ /**
+ * Run the interpreter.
+ * @return the evaluation result
+ */
+ protected Object interpret() {
+ return interpreter.interpret(script);
+ }
+
+ @Override
+ public Object call() throws Exception {
+ synchronized(this) {
if (result == interpreter) {
checkCacheVersion();
- result = interpreter.interpret(script);
+ result = interpret();
}
return result;
}
- };
+ }
}
}
\ No newline at end of file
Modified: commons/proper/jexl/trunk/src/site/xdoc/changes.xml
URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/site/xdoc/changes.xml?rev=1741426&r1=1741425&r2=1741426&view=diff
==============================================================================
--- commons/proper/jexl/trunk/src/site/xdoc/changes.xml (original)
+++ commons/proper/jexl/trunk/src/site/xdoc/changes.xml Thu Apr 28 13:59:38 2016
@@ -26,6 +26,9 @@
</properties>
<body>
<release version="3.0.1" date="unreleased">
+ <action dev="henrib" type="fix" issue="JEXL-193" due-to="Dmitri Blinov">
+ InterruptedException is swallowed in function call in silent and non-strict mode
+ </action>
<action dev="henrib" type="fix" issue="JEXL-192" due-to="Constantin Mitocaru">
Invalid return type when expected result is null
</action>
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=1741426&r1=1741425&r2=1741426&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 Thu Apr 28 13:59:38 2016
@@ -1123,25 +1123,26 @@ public class IssuesTest extends JexlTest
}
public static class C192 {
- public C192() {}
+ public C192() {
+ }
- public static Integer callme(Integer n) {
- if (n == null) {
- return null;
- } else {
- return n >= 0? 42 : -42;
- }
- }
+ public static Integer callme(Integer n) {
+ if (n == null) {
+ return null;
+ } else {
+ return n >= 0 ? 42 : -42;
+ }
+ }
- public static Object kickme() {
- return C192.class;
- }
+ public static Object kickme() {
+ return C192.class;
+ }
}
@Test
public void test192() throws Exception {
JexlContext jc = new MapContext();
- jc.set("x.y.z", C192.class);
+ jc.set("x.y.z", C192.class);
JexlEngine jexl = new JexlBuilder().create();
JexlExpression js0 = jexl.createExpression("x.y.z.callme(t)");
jc.set("t", null);
@@ -1162,58 +1163,4 @@ public class IssuesTest extends JexlTest
jc.set("t", null);
Assert.assertNull(js0.evaluate(jc));
}
-//
-//
-// @Test
-// public void testUnderscoreInName() {
-// JexlEngine jexl = new Engine();
-// String jexlExp = "(x.length_mm * x.width)";
-// JexlExpression e = jexl.createExpression( jexlExp );
-// JexlContext jc = new MapContext();
-//
-// LazyDynaMap object = new LazyDynaMap();
-// object.set("length_mm", "10.0");
-// object.set("width", "5.0");
-//
-// jc.set("x", object );
-//
-// Assert.assertEquals(null, ((Double)e.evaluate(jc)).doubleValue(), 50d, 0d);
-// }
-//
-// @Test
-// public void testFullStopInName() {
-// JexlEngine jexl = new Engine();
-// String jexlExp = "(x.length.mm * x.width)";
-// JexlExpression e = jexl.createExpression( jexlExp );
-// JexlContext jc = new MapContext();
-//
-// LazyDynaMap object = new LazyDynaMap();
-// object.set("length.mm", "10.0");
-// object.set("width", "5.0");
-//
-// Assert.assertEquals(null, object.get("length.mm"), "10.0");
-//
-// jc.set("x", object );
-//
-// Assert.assertEquals(null, ((Double)e.evaluate(jc)).doubleValue(), 50d, 0d);
-// }
-//
-// @Test
-// public void testFullStopInNameMakingSubObject() {
-// JexlEngine jexl = new Engine();
-// String jexlExp = "(x.length.mm * x.width)";
-// JexlExpression e = jexl.createExpression( jexlExp );
-// JexlContext jc = new MapContext();
-//
-// LazyDynaMap object = new LazyDynaMap();
-// LazyDynaMap subObject = new LazyDynaMap();
-// object.set("length", subObject);
-// subObject.set("mm", "10.0");
-// object.set("width", "5.0");
-//
-// jc.set("x", object );
-//
-// Assert.assertEquals(null, ((Double)e.evaluate(jc)).doubleValue(), 50d, 0d);
-// }
-
}
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=1741426&r1=1741425&r2=1741426&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 Thu Apr 28 13:59:38 2016
@@ -17,6 +17,8 @@
package org.apache.commons.jexl3;
import java.util.concurrent.Callable;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
@@ -29,6 +31,7 @@ import org.junit.Test;
/**
* Tests around asynchronous script execution and interrupts.
*/
+@SuppressWarnings({"UnnecessaryBoxing", "AssertEqualsBetweenInconvertibleTypes"})
public class ScriptCallableTest extends JexlTestCase {
//Logger LOGGER = Logger.getLogger(VarTest.class.getName());
public ScriptCallableTest() {
@@ -42,50 +45,61 @@ public class ScriptCallableTest extends
ExecutorService executor = Executors.newFixedThreadPool(1);
executor.submit(future);
+ Object t = 42;
try {
- future.get(100, TimeUnit.MILLISECONDS);
+ t = future.get(100, TimeUnit.MILLISECONDS);
Assert.fail("should have timed out");
} catch (TimeoutException xtimeout) {
// ok, ignore
+ future.cancel(true);
+ } finally {
+ executor.shutdown();
}
- Thread.sleep(100);
- future.cancel(true);
Assert.assertTrue(future.isCancelled());
+ Assert.assertEquals(42, t);
}
@Test
public void testCallable() throws Exception {
JexlScript e = JEXL.createScript("while(true);");
Callable<Object> c = e.callable(null);
+ Object t = 42;
ExecutorService executor = Executors.newFixedThreadPool(1);
Future<?> future = executor.submit(c);
try {
- future.get(100, TimeUnit.MILLISECONDS);
+ t = future.get(100, TimeUnit.MILLISECONDS);
Assert.fail("should have timed out");
} catch (TimeoutException xtimeout) {
// ok, ignore
+ future.cancel(true);
+ } finally {
+ executor.shutdown();
}
- future.cancel(true);
Assert.assertTrue(future.isCancelled());
+ Assert.assertEquals(42, t);
}
@Test
public void testCallableClosure() throws Exception {
JexlScript e = JEXL.createScript("function(t) {while(t);}");
Callable<Object> c = e.callable(null, Boolean.TRUE);
+ Object t = 42;
ExecutorService executor = Executors.newFixedThreadPool(1);
Future<?> future = executor.submit(c);
try {
- future.get(100, TimeUnit.MILLISECONDS);
+ t = future.get(100, TimeUnit.MILLISECONDS);
Assert.fail("should have timed out");
} catch (TimeoutException xtimeout) {
// ok, ignore
+ future.cancel(true);
+ } finally {
+ executor.shutdown();
}
- future.cancel(true);
Assert.assertTrue(future.isCancelled());
+ Assert.assertEquals(42, t);
}
public static class TestContext extends MapContext implements JexlContext.NamespaceResolver {
@@ -118,6 +132,19 @@ public class ScriptCallableTest extends
}
return 1;
}
+
+ public int interrupt() throws InterruptedException {
+ Thread.currentThread().interrupt();
+ return 42;
+ }
+
+ public void sleep(long millis) throws InterruptedException {
+ try {
+ Thread.sleep(millis);
+ } catch (InterruptedException xint) {
+ throw xint;
+ }
+ }
}
@Test
@@ -126,10 +153,14 @@ public class ScriptCallableTest extends
Callable<Object> c = e.callable(new TestContext());
ExecutorService executor = Executors.newFixedThreadPool(1);
- Future<?> future = executor.submit(c);
- Object t = future.get(2, TimeUnit.SECONDS);
- Assert.assertTrue(future.isDone());
- Assert.assertEquals(0, t);
+ try {
+ Future<?> future = executor.submit(c);
+ Object t = future.get(2, TimeUnit.SECONDS);
+ Assert.assertTrue(future.isDone());
+ Assert.assertEquals(0, t);
+ } finally {
+ executor.shutdown();
+ }
}
@Test
@@ -138,9 +169,13 @@ public class ScriptCallableTest extends
Callable<Object> c = e.callable(new TestContext());
ExecutorService executor = Executors.newFixedThreadPool(1);
- Future<?> future = executor.submit(c);
- Object t = future.get(2, TimeUnit.SECONDS);
- Assert.assertEquals(1, t);
+ try {
+ Future<?> future = executor.submit(c);
+ Object t = future.get(2, TimeUnit.SECONDS);
+ Assert.assertEquals(1, t);
+ } finally {
+ executor.shutdown();
+ }
}
@Test
@@ -149,15 +184,21 @@ public class ScriptCallableTest extends
Callable<Object> c = e.callable(new TestContext());
ExecutorService executor = Executors.newFixedThreadPool(1);
- Future<?> future = executor.submit(c);
try {
- future.get(100, TimeUnit.MILLISECONDS);
- Assert.fail("should have timed out");
- } catch (TimeoutException xtimeout) {
- // ok, ignore
+ Future<?> future = executor.submit(c);
+ Object t = 42;
+ try {
+ t = future.get(100, TimeUnit.MILLISECONDS);
+ Assert.fail("should have timed out");
+ } catch (TimeoutException xtimeout) {
+ // ok, ignore
+ future.cancel(true);
+ }
+ Assert.assertTrue(future.isCancelled());
+ Assert.assertEquals(42, t);
+ } finally {
+ executor.shutdown();
}
- future.cancel(true);
- Assert.assertTrue(future.isCancelled());
}
@Test
@@ -167,14 +208,19 @@ public class ScriptCallableTest extends
ExecutorService executor = Executors.newFixedThreadPool(1);
Future<?> future = executor.submit(c);
+ Object t = 42;
+
try {
- future.get(100, TimeUnit.MILLISECONDS);
+ t = future.get(100, TimeUnit.MILLISECONDS);
Assert.fail("should have timed out");
} catch (TimeoutException xtimeout) {
// ok, ignore
+ future.cancel(true);
+ } finally {
+ executor.shutdown();
}
- future.cancel(true);
Assert.assertTrue(future.isCancelled());
+ Assert.assertEquals(42, t);
}
@Test
@@ -184,14 +230,19 @@ public class ScriptCallableTest extends
ExecutorService executor = Executors.newFixedThreadPool(1);
Future<?> future = executor.submit(c);
+ Object t = 42;
+
try {
- future.get(100, TimeUnit.MILLISECONDS);
+ t = future.get(100, TimeUnit.MILLISECONDS);
Assert.fail("should have timed out");
} catch (TimeoutException xtimeout) {
// ok, ignore
+ future.cancel(true);
+ } finally {
+ executor.shutdown();
}
- future.cancel(true);
Assert.assertTrue(future.isCancelled());
+ Assert.assertEquals(42, t);
}
@Test
@@ -201,13 +252,147 @@ public class ScriptCallableTest extends
ExecutorService executor = Executors.newFixedThreadPool(1);
Future<?> future = executor.submit(c);
+ Object t = 42;
+
try {
- future.get(100, TimeUnit.MILLISECONDS);
+ t = future.get(100, TimeUnit.MILLISECONDS);
Assert.fail("should have timed out");
} catch (TimeoutException xtimeout) {
- // ok, ignore
+ future.cancel(true);
+ } finally {
+ executor.shutdown();
}
- future.cancel(true);
Assert.assertTrue(future.isCancelled());
+ Assert.assertEquals(42, t);
+ }
+
+ @Test
+ public void testInterruptVerboseStrict() throws Exception {
+ runInterrupt(false, true);
+ }
+
+ @Test
+ public void testInterruptVerboseLenient() throws Exception {
+ runInterrupt(false, false);
+ }
+
+ @Test
+ public void testInterruptSilentStrict() throws Exception {
+ runInterrupt(true, true);
+ }
+
+ @Test
+ public void testInterruptSilentLenient() throws Exception {
+ runInterrupt(true, true);
+ }
+
+ /**
+ * Redundant test with previous ones but impervious to JEXL engine configuation.
+ * @param silent silent engine flag
+ * @param strict strict (aka not lenient) engine flag
+ * @throws Exception if there is a regression
+ */
+ private void runInterrupt(boolean silent, boolean strict) throws Exception {
+ ExecutorService exec = Executors.newFixedThreadPool(2);
+ try {
+ JexlContext ctxt = new TestContext();
+ JexlEngine jexl = new JexlBuilder().silent(silent).strict(strict).create();
+
+ // run an interrupt
+ JexlScript sint = jexl.createScript("interrupt(); return 42");
+ Object t = null;
+ try {
+ Callable<Object> c = sint.callable(ctxt);
+ t = c.call();
+ } catch (JexlException.Cancel xjexl) {
+ if (silent || !strict) {
+ Assert.fail("should not have thrown " + xjexl);
+ }
+ }
+ Assert.assertNotEquals(42, t);
+
+ // self interrupt
+ Future<Object> c = null;
+ try {
+ c = exec.submit(sint.callable(ctxt));
+ t = c.get();
+ } catch (ExecutionException xexec) {
+ if (silent || !strict) {
+ Assert.fail("should not have thrown " + xexec);
+ }
+ }
+ Assert.assertNotEquals(42, t);
+
+ // timeout a sleep
+ JexlScript ssleep = jexl.createScript("sleep(30000); return 42");
+ try {
+ c = exec.submit(ssleep.callable(ctxt));
+ t = c.get(100L, TimeUnit.MILLISECONDS);
+ Assert.fail("should timeout");
+ } catch (TimeoutException xtimeout) {
+ if (c != null) {
+ c.cancel(true);
+ }
+ }
+ Assert.assertNotEquals(42, t);
+
+ // cancel a sleep
+ try {
+ final Future<Object> fc = exec.submit(ssleep.callable(ctxt));
+ Runnable cancels = new Runnable() {
+ @Override
+ public void run() {
+ try {
+ Thread.sleep(200L);
+ } catch (Exception xignore) {
+
+ }
+ fc.cancel(true);
+ }
+ };
+ exec.submit(cancels);
+ t = c.get();
+ Assert.fail("should be cancelled");
+ } catch (CancellationException xexec) {
+ // this is the expected result
+ }
+
+ // timeout a while(true)
+ JexlScript swhile = jexl.createScript("while(true); return 42");
+ try {
+ c = exec.submit(swhile.callable(ctxt));
+ t = c.get(100L, TimeUnit.MILLISECONDS);
+ Assert.fail("should timeout");
+ } catch (TimeoutException xtimeout) {
+ if (c != null) {
+ c.cancel(true);
+ }
+ }
+ Assert.assertNotEquals(42, t);
+
+ // cancel a while(true)
+ try {
+ final Future<Object> fc = exec.submit(swhile.callable(ctxt));
+ Runnable cancels = new Runnable() {
+ @Override
+ public void run() {
+ try {
+ Thread.sleep(200L);
+ } catch (Exception xignore) {
+
+ }
+ fc.cancel(true);
+ }
+ };
+ exec.submit(cancels);
+ t = c.get();
+ Assert.fail("should be cancelled");
+ } catch (CancellationException xexec) {
+ // this is the expected result
+ }
+ Assert.assertNotEquals(42, t);
+ } finally {
+ exec.shutdown();
+ }
}
}