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 2018/05/21 11:18:10 UTC
[commons-jexl] branch master updated: JEXL-261: api change,
added logic in engine and interpreter, added test,
updated release notes & changes
This is an automated email from the ASF dual-hosted git repository.
henrib pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-jexl.git
The following commit(s) were added to refs/heads/master by this push:
new f1f466d JEXL-261: api change, added logic in engine and interpreter, added test, updated release notes & changes
f1f466d is described below
commit f1f466dee130b42c1b422e312d96db3268612eea
Author: henrib <he...@apache.org>
AuthorDate: Mon May 21 13:17:48 2018 +0200
JEXL-261: api change, added logic in engine and interpreter, added test, updated release notes & changes
---
RELEASE-NOTES.txt | 1 +
.../java/org/apache/commons/jexl3/JexlEngine.java | 4 +-
.../org/apache/commons/jexl3/internal/Engine.java | 21 +++
.../apache/commons/jexl3/internal/Interpreter.java | 66 +++++----
.../jexl3/internal/introspection/Introspector.java | 6 +-
.../internal/introspection/SandboxUberspect.java | 5 +
.../jexl3/internal/introspection/Uberspect.java | 5 +
.../commons/jexl3/introspection/JexlUberspect.java | 6 +
.../org/apache/commons/jexl3/parser/JexlNode.java | 9 +-
src/site/xdoc/changes.xml | 3 +
.../org/apache/commons/jexl3/ClassCreator.java | 128 +++++++++++------
.../org/apache/commons/jexl3/ClassCreatorTest.java | 152 ++++++++++++++++++++-
12 files changed, 336 insertions(+), 70 deletions(-)
diff --git a/RELEASE-NOTES.txt b/RELEASE-NOTES.txt
index f7b5af5..59ffe09 100644
--- a/RELEASE-NOTES.txt
+++ b/RELEASE-NOTES.txt
@@ -55,6 +55,7 @@ New Features in 3.2:
Bugs Fixed in 3.2:
==================
+* JEXL-261: JexlEngine.setClassLoader(...) should reload namespaces that are classes
* JEXL-246: Intermittent ambiguous method invocation when processing assignOverload
* JEXL-245: Engine in strict mode fails to fail on unsolvable variables or properties
* JEXL-244: Webapp classloader memory leaks
diff --git a/src/main/java/org/apache/commons/jexl3/JexlEngine.java b/src/main/java/org/apache/commons/jexl3/JexlEngine.java
index bccf7e4..deafdf8 100644
--- a/src/main/java/org/apache/commons/jexl3/JexlEngine.java
+++ b/src/main/java/org/apache/commons/jexl3/JexlEngine.java
@@ -268,8 +268,8 @@ public abstract class JexlEngine {
/**
* Sets the class loader used to discover classes in 'new' expressions.
- * <p>This method is <em>not</em> thread safe; it should be called as an optional step of the JexlEngine
- * initialization code before expression creation & evaluation.</p>
+ * <p>This method is <em>not</em> thread safe; it may be called after JexlEngine
+ * initialization and allow scripts to use new classes definitions.</p>
*
* @param loader the class loader to use
*/
diff --git a/src/main/java/org/apache/commons/jexl3/internal/Engine.java b/src/main/java/org/apache/commons/jexl3/internal/Engine.java
index 0ae5ddf..17a972b 100644
--- a/src/main/java/org/apache/commons/jexl3/internal/Engine.java
+++ b/src/main/java/org/apache/commons/jexl3/internal/Engine.java
@@ -238,6 +238,27 @@ public class Engine extends JexlEngine {
@Override
public void setClassLoader(ClassLoader loader) {
uberspect.setClassLoader(loader);
+ if (functions != null) {
+ ClassLoader nloader = uberspect.getClassLoader();
+ List<String> names = new ArrayList<String>(functions.keySet());
+ for(String name : names) {
+ Object functor = functions.get(name);
+ if (functor instanceof Class<?>) {
+ Class<?> fclass = ((Class<?>) functor);
+ try {
+ Class<?> nclass = nloader.loadClass(fclass.getName());
+ if (nclass != fclass) {
+ if (nclass == null) {
+ nclass = Void.class;
+ }
+ functions.put(name, nclass);
+ }
+ } catch (ClassNotFoundException xany) {
+ functions.remove(name);
+ }
+ }
+ }
+ }
}
@Override
diff --git a/src/main/java/org/apache/commons/jexl3/internal/Interpreter.java b/src/main/java/org/apache/commons/jexl3/internal/Interpreter.java
index 6eb0fa0..e7761fa 100644
--- a/src/main/java/org/apache/commons/jexl3/internal/Interpreter.java
+++ b/src/main/java/org/apache/commons/jexl3/internal/Interpreter.java
@@ -249,33 +249,51 @@ public class Interpreter extends InterpreterBase {
throw new JexlException(node, "no such function namespace " + prefix, null);
}
}
- // allow namespace to instantiate a functor with context if possible, not an error otherwise
- Object functor = null;
- if (namespace instanceof JexlContext.NamespaceFunctor) {
- functor = ((JexlContext.NamespaceFunctor) namespace).createFunctor(context);
- } else if (namespace instanceof Class<?>) {
- Object[] args = new Object[]{context};
- JexlMethod ctor = uberspect.getConstructor(namespace, args);
- if (ctor != null) {
- try {
- functor = ctor.invoke(namespace, args);
- } catch (Exception xinst) {
- throw new JexlException(node, "unable to instantiate namespace " + prefix, xinst);
+ // shortcut if ns is known to be not-a-functor
+ final boolean cacheable = cache;
+ Object cached = cacheable ? node.jjtGetValue() : null;
+ if (cached != JexlContext.NamespaceFunctor.class) {
+ // allow namespace to instantiate a functor with context if possible, not an error otherwise
+ Object functor = null;
+ if (namespace instanceof JexlContext.NamespaceFunctor) {
+ functor = ((JexlContext.NamespaceFunctor) namespace).createFunctor(context);
+ } else if (namespace != null) {
+ // attempt to reuse last ctor cached in volatile JexlNode.value
+ if (cached instanceof JexlMethod) {
+ Object eval = ((JexlMethod) cached).tryInvoke(null, context);
+ if (JexlEngine.TRY_FAILED != eval) {
+ functor = eval;
+ }
+ }
+ if (functor == null) {
+ JexlMethod ctor = uberspect.getConstructor(namespace, context);
+ if (ctor != null) {
+ try {
+ functor = ctor.invoke(namespace, context);
+ if (cacheable && ctor.isCacheable()) {
+ node.jjtSetValue(ctor);
+ }
+ } catch (Exception xinst) {
+ throw new JexlException(node, "unable to instantiate namespace " + prefix, xinst);
+ }
+ }
}
}
- }
- // got a functor, store it and return it
- if (functor != null) {
- synchronized (this) {
- if (functors == null) {
- functors = new HashMap<String, Object>();
+ // got a functor, store it and return it
+ if (functor != null) {
+ synchronized (this) {
+ if (functors == null) {
+ functors = new HashMap<String, Object>();
+ }
+ functors.put(prefix, functor);
}
- functors.put(prefix, functor);
+ return functor;
+ } else {
+ // use the NamespaceFunctor class to tag this node as not-a-functor
+ node.jjtSetValue(JexlContext.NamespaceFunctor.class);
}
- return functor;
- } else {
- return namespace;
}
+ return namespace;
}
@Override
@@ -1431,7 +1449,7 @@ public class Interpreter extends InterpreterBase {
/**
* Cached function call.
*/
- private static class Funcall {
+ private static class Funcall implements JexlNode.Funcall {
/** Whether narrow should be applied to arguments. */
protected final boolean narrow;
/** The JexlMethod to delegate the call to. */
@@ -1712,7 +1730,7 @@ public class Interpreter extends InterpreterBase {
try {
boolean cacheable = cache;
// attempt to reuse last funcall cached in volatile JexlNode.value
- if (cache) {
+ if (cacheable) {
Object cached = node.jjtGetValue();
if (cached instanceof Funcall) {
Object eval = ((Funcall) cached).tryInvoke(this, null, target, argv);
diff --git a/src/main/java/org/apache/commons/jexl3/internal/introspection/Introspector.java b/src/main/java/org/apache/commons/jexl3/internal/introspection/Introspector.java
index e89ef95..4566638 100644
--- a/src/main/java/org/apache/commons/jexl3/internal/introspection/Introspector.java
+++ b/src/main/java/org/apache/commons/jexl3/internal/introspection/Introspector.java
@@ -273,14 +273,14 @@ public final class Introspector {
constructorsMap.put(key, CTOR_MISS);
}
} catch (ClassNotFoundException xnotfound) {
- if (rlog != null && rlog.isInfoEnabled()) {
- rlog.info("unable to find class: "
+ if (rlog != null && rlog.isDebugEnabled()) {
+ rlog.debug("unable to find class: "
+ cname + "."
+ key.debugString(), xnotfound);
}
ctor = null;
} catch (MethodKey.AmbiguousException xambiguous) {
- if (rlog != null && rlog.isInfoEnabled()) {
+ if (rlog != null && xambiguous.isSevere() && rlog.isInfoEnabled()) {
rlog.info("ambiguous constructor invocation: "
+ cname + "."
+ key.debugString(), xambiguous);
diff --git a/src/main/java/org/apache/commons/jexl3/internal/introspection/SandboxUberspect.java b/src/main/java/org/apache/commons/jexl3/internal/introspection/SandboxUberspect.java
index 583a9d4..bb7a980 100644
--- a/src/main/java/org/apache/commons/jexl3/internal/introspection/SandboxUberspect.java
+++ b/src/main/java/org/apache/commons/jexl3/internal/introspection/SandboxUberspect.java
@@ -57,6 +57,11 @@ public final class SandboxUberspect implements JexlUberspect {
public void setClassLoader(ClassLoader loader) {
uberspect.setClassLoader(loader);
}
+
+ @Override
+ public ClassLoader getClassLoader() {
+ return uberspect.getClassLoader();
+ }
@Override
public int getVersion() {
diff --git a/src/main/java/org/apache/commons/jexl3/internal/introspection/Uberspect.java b/src/main/java/org/apache/commons/jexl3/internal/introspection/Uberspect.java
index 8ebdd08..c9b5b82 100644
--- a/src/main/java/org/apache/commons/jexl3/internal/introspection/Uberspect.java
+++ b/src/main/java/org/apache/commons/jexl3/internal/introspection/Uberspect.java
@@ -124,6 +124,11 @@ public class Uberspect implements JexlUberspect {
version.incrementAndGet();
}
}
+
+ @Override
+ public ClassLoader getClassLoader() {
+ return loader.get();
+ }
@Override
public int getVersion() {
diff --git a/src/main/java/org/apache/commons/jexl3/introspection/JexlUberspect.java b/src/main/java/org/apache/commons/jexl3/introspection/JexlUberspect.java
index 0e3b1f6..41e014c 100644
--- a/src/main/java/org/apache/commons/jexl3/introspection/JexlUberspect.java
+++ b/src/main/java/org/apache/commons/jexl3/introspection/JexlUberspect.java
@@ -214,6 +214,12 @@ public interface JexlUberspect {
* @param loader the class loader
*/
void setClassLoader(ClassLoader loader);
+
+ /**
+ * Gets the current class loader.
+ * @return the class loader
+ */
+ ClassLoader getClassLoader();
/**
* Gets this uberspect version.
diff --git a/src/main/java/org/apache/commons/jexl3/parser/JexlNode.java b/src/main/java/org/apache/commons/jexl3/parser/JexlNode.java
index 705788e..9009954 100644
--- a/src/main/java/org/apache/commons/jexl3/parser/JexlNode.java
+++ b/src/main/java/org/apache/commons/jexl3/parser/JexlNode.java
@@ -81,6 +81,11 @@ public abstract class JexlNode extends SimpleNode {
return info;
}
}
+
+ /**
+ * Marker interface for cachable function calls.
+ */
+ public interface Funcall {}
/**
* Clears any cached value of type JexlProperty{G,S}et or JexlMethod.
@@ -92,7 +97,8 @@ public abstract class JexlNode extends SimpleNode {
final Object value = jjtGetValue();
if (value instanceof JexlPropertyGet
|| value instanceof JexlPropertySet
- || value instanceof JexlMethod) {
+ || value instanceof JexlMethod
+ || value instanceof Funcall ) {
jjtSetValue(null);
}
for (int n = 0; n < jjtGetNumChildren(); ++n) {
@@ -227,7 +233,6 @@ public abstract class JexlNode extends SimpleNode {
* over the error generation; ie, ternaries can return null even if the engine in strict mode
* would normally throw an exception.
* </p>
- * @param node the expression node
* @return true if nullable variable, false otherwise
*/
public boolean isTernaryProtected() {
diff --git a/src/site/xdoc/changes.xml b/src/site/xdoc/changes.xml
index 225cf4e..dc77663 100644
--- a/src/site/xdoc/changes.xml
+++ b/src/site/xdoc/changes.xml
@@ -26,6 +26,9 @@
</properties>
<body>
<release version="3.2" date="unreleased">
+ <action dev="henrib" type="add" issue="JEXL-261">
+ JexlEngine.setClassLoader(...) should reload namespaces that are classes
+ </action>
<action dev="henrib" type="add" issue="JEXL-260">
Automatically inject JexlContext in constructor call when possible
</action>
diff --git a/src/test/java/org/apache/commons/jexl3/ClassCreator.java b/src/test/java/org/apache/commons/jexl3/ClassCreator.java
index c71fbc1..4303ed0 100644
--- a/src/test/java/org/apache/commons/jexl3/ClassCreator.java
+++ b/src/test/java/org/apache/commons/jexl3/ClassCreator.java
@@ -16,13 +16,15 @@
*/
package org.apache.commons.jexl3;
-
import java.io.File;
import java.io.FileWriter;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
+import java.util.ArrayList;
import java.util.Arrays;
+import java.util.List;
+import javax.tools.Diagnostic;
import javax.tools.DiagnosticCollector;
import javax.tools.JavaCompiler;
import javax.tools.JavaFileObject;
@@ -30,25 +32,30 @@ import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
/**
- * Helper class to test GC / reference interactions.
- * Dynamically creates a class by compiling generated source Java code and
- * load it through a dedicated class loader.
+ * Helper class to test GC / reference interactions. Dynamically creates a class
+ * by compiling generated source Java code and load it through a dedicated class
+ * loader.
*/
public class ClassCreator {
- private final JexlEngine jexl;
+
+ //private final JexlEngine jexl;
private final File base;
private File packageDir = null;
private int seed = 0;
+ private String ctorBody = "";
private String className = null;
private String sourceName = null;
private ClassLoader loader = null;
public static final boolean canRun = true;//comSunToolsJavacMain();
+ static final String JEXL_PACKAGE = "org.apache.commons.jexl3";
static final String GEN_PATH = "/org/apache/commons/jexl3/generated";
static final String GEN_PACKAGE = "org.apache.commons.jexl3.generated";
static final String GEN_CLASS = GEN_PACKAGE + ".";
+
/**
* Check if we can invoke Sun's java compiler.
+ *
* @return true if it is possible, false otherwise
*/
private static boolean comSunToolsJavacMain() {
@@ -61,13 +68,13 @@ public class ClassCreator {
}
public ClassCreator(JexlEngine theJexl, File theBase) throws Exception {
- jexl = theJexl;
+ //jexl = theJexl;
base = theBase;
}
-
public void clear() {
seed = 0;
+ ctorBody = "";
packageDir = null;
className = null;
sourceName = null;
@@ -84,10 +91,14 @@ public class ClassCreator {
loader = null;
}
+ public void setCtorBody(String arg) {
+ ctorBody = arg;
+ }
+
public String getClassName() {
return GEN_CLASS + className;
}
-
+
public Class<?> getClassInstance() throws Exception {
return getClassLoader().loadClass(getClassName());
}
@@ -95,31 +106,52 @@ public class ClassCreator {
public ClassLoader getClassLoader() throws Exception {
if (loader == null) {
URL classpath = (new File(base, Integer.toString(seed))).toURI().toURL();
- loader = new URLClassLoader(new URL[]{classpath}, null);
+ loader = new URLClassLoader(new URL[]{classpath}, getClass().getClassLoader());
}
return loader;
}
public Class<?> createClass() throws Exception {
+ return createClass(false);
+ }
+
+ public Class<?> createClass(boolean ftor) throws Exception {
// generate, compile & validate
- generate();
+ generate(ftor);
Class<?> clazz = compile();
if (clazz == null) {
throw new Exception("failed to compile foo" + seed);
}
+ if (ftor) {
+ return clazz;
+ }
Object v = validate(clazz);
if (v instanceof Integer && ((Integer) v).intValue() == seed) {
return clazz;
}
throw new Exception("failed to validate foo" + seed);
+ }
+
+ Object newInstance(Class<?> clazz, JexlContext ctxt) throws Exception {
+ return clazz.getConstructor(JexlContext.class).newInstance(ctxt);
}
-
- void generate() throws Exception {
+
+ void generate(boolean ftor) throws Exception {
FileWriter aWriter = new FileWriter(new File(packageDir, sourceName), false);
aWriter.write("package ");
aWriter.write(GEN_PACKAGE);
aWriter.write(";\n");
- aWriter.write("public class " + className + "{\n");
+ if (ftor) {
+ aWriter.write("import "+ JEXL_PACKAGE +".JexlContext;");
+ aWriter.write(";\n");
+ }
+ aWriter.write("public class " + className);
+ aWriter.write(" {\n");
+ if (ftor) {
+ aWriter.write("public " + className + "(JexlContext ctxt) {\n");
+ aWriter.write(ctorBody);
+ aWriter.write(" }\n");
+ }
aWriter.write("private int value =");
aWriter.write(Integer.toString(seed));
aWriter.write(";\n");
@@ -134,42 +166,61 @@ public class ClassCreator {
aWriter.close();
}
- Class<?> compile0() throws Exception {
- String source = packageDir.getPath() + "/" + sourceName;
- Class<?> javac = getClassLoader().loadClass("com.sun.tools.javac.Main");
- if (javac == null) {
- return null;
- }
- Integer r;
- try {
- r = (Integer) jexl.invokeMethod(javac, "compile", source);
- if (r.intValue() >= 0) {
- return getClassLoader().loadClass(GEN_CLASS + className);
- }
- } catch (JexlException xignore) {
- // ignore
- }
- r = (Integer) jexl.invokeMethod(javac, "compile", (Object) new String[]{source});
- if (r.intValue() >= 0) {
- return getClassLoader().loadClass(GEN_CLASS + className);
- }
- return null;
- }
+// Class<?> compile0() throws Exception {
+// String source = packageDir.getPath() + "/" + sourceName;
+// Class<?> javac = getClassLoader().loadClass("com.sun.tools.javac.Main");
+// if (javac == null) {
+// return null;
+// }
+// Integer r;
+// try {
+// r = (Integer) jexl.invokeMethod(javac, "compile", source);
+// if (r.intValue() >= 0) {
+// return getClassLoader().loadClass(GEN_CLASS + className);
+// }
+// } catch (JexlException xignore) {
+// // ignore
+// }
+// r = (Integer) jexl.invokeMethod(javac, "compile", (Object) new String[]{source});
+// if (r.intValue() >= 0) {
+// return getClassLoader().loadClass(GEN_CLASS + className);
+// }
+// return null;
+// }
Class<?> compile() throws Exception {
String source = packageDir.getPath() + "/" + sourceName;
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
- DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<JavaFileObject>();
- StandardJavaFileManager fileManager = compiler.getStandardFileManager(diagnostics, null, null);
+ DiagnosticCollector<JavaFileObject> diagnosticsCollector = new DiagnosticCollector<JavaFileObject>();
+ StandardJavaFileManager fileManager = compiler.getStandardFileManager(diagnosticsCollector, null, null);
Iterable<? extends JavaFileObject> compilationUnits = fileManager
.getJavaFileObjectsFromStrings(Arrays.asList(source));
- JavaCompiler.CompilationTask task = compiler.getTask(null, fileManager, diagnostics, null,
+
+ List<String> options = new ArrayList<String>();
+ options.add("-classpath");
+ // only add hbase classes to classpath. This is a little bit tricky: assume
+ // the classpath is {hbaseSrc}/target/classes.
+ String currentDir = new File(".").getAbsolutePath();
+ String classpath = currentDir + File.separator + "target" + File.separator + "classes"
+ //+ System.getProperty("path.separator") + System.getProperty("java.class.path")
+ + System.getProperty("path.separator") + System.getProperty("surefire.test.class.path");
+
+ options.add(classpath);
+ //LOG.debug("Setting classpath to: " + classpath);
+
+ JavaCompiler.CompilationTask task = compiler.getTask(null, fileManager, diagnosticsCollector, options,
null, compilationUnits);
boolean success = task.call();
fileManager.close();
if (success) {
- return getClassLoader().loadClass(GEN_CLASS + className);
+ return getClassLoader().loadClass(GEN_CLASS + className);
} else {
+ List<Diagnostic<? extends JavaFileObject>> diagnostics = diagnosticsCollector.getDiagnostics();
+ for (Diagnostic<? extends JavaFileObject> diagnostic : diagnostics) {
+ // read error dertails from the diagnostic object
+ System.out.println(diagnostic.getMessage(null));
+
+ }
return null;
}
}
@@ -181,4 +232,5 @@ public class ClassCreator {
Method thisMethod = clazz.getDeclaredMethod("getValue", params);
return thisMethod.invoke(iClass, paramsObj);
}
+
}
diff --git a/src/test/java/org/apache/commons/jexl3/ClassCreatorTest.java b/src/test/java/org/apache/commons/jexl3/ClassCreatorTest.java
index abde7a6..7a5e768 100644
--- a/src/test/java/org/apache/commons/jexl3/ClassCreatorTest.java
+++ b/src/test/java/org/apache/commons/jexl3/ClassCreatorTest.java
@@ -22,7 +22,9 @@ import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -105,6 +107,7 @@ public class ClassCreatorTest extends JexlTestCase {
public void testOne() throws Exception {
// abort test if class creator can not run
if (!ClassCreator.canRun) {
+ logger.warn("unable to create classes");
return;
}
ClassCreator cctor = new ClassCreator(jexl, base);
@@ -113,7 +116,154 @@ public class ClassCreatorTest extends JexlTestCase {
Assert.assertEquals("foo1", foo1.getSimpleName());
cctor.clear();
}
-
+
+ @Test
+ public void testFunctorOne() throws Exception {
+ JexlContext ctxt = new MapContext();
+ ctxt.set("value", 1000);
+
+ // create a class foo1 with a ctor whose body gets a value
+ // from the context to initialize its value
+ ClassCreator cctor = new ClassCreator(jexl, base);
+ cctor.setSeed(1);
+ cctor.setCtorBody("value = (Integer) ctxt.get(\"value\") + 10;");
+ Class<?> foo1 = cctor.createClass(true);
+ Assert.assertTrue(foo1.getClassLoader() == cctor.getClassLoader());
+ Assert.assertEquals("foo1", foo1.getSimpleName());
+ Object result = cctor.newInstance(foo1, ctxt);
+ Assert.assertEquals(foo1, result.getClass());
+ jexl.setClassLoader(cctor.getClassLoader());
+ cctor.clear();
+
+ // check we can invoke that ctor using its name or class
+ JexlScript script = jexl.createScript("(c)->{ new(c).value; }");
+ result = script.execute(ctxt, foo1);
+ Assert.assertEquals(1010, result);
+ result = script.execute(ctxt, foo1.getName());
+ Assert.assertEquals(1010, result);
+
+ // re-create foo1 with a different body!
+ cctor.setSeed(1);
+ cctor.setCtorBody("value = (Integer) ctxt.get(\"value\") + 99;");
+ Class<?> foo11 = cctor.createClass(true);
+ Assert.assertEquals("foo1", foo1.getSimpleName());
+ Assert.assertTrue(foo11 != foo1);
+ foo1 = foo11;
+ result = cctor.newInstance(foo1, ctxt);
+ Assert.assertEquals(foo1, result.getClass());
+ // drum rolll....
+ jexl.setClassLoader(foo1.getClassLoader());
+ result = script.execute(ctxt, foo1.getName());
+ // tada!
+ Assert.assertEquals(1099, result);
+ result = script.execute(ctxt, foo1);
+ Assert.assertEquals(1099, result);
+ }
+
+ public static class NsTest implements JexlContext.NamespaceFunctor {
+ private String className;
+
+ public NsTest(String cls) {
+ className = cls;
+ }
+ @Override
+ public Object createFunctor(JexlContext context) {
+ JexlEngine jexl = JexlEngine.getThreadEngine();
+ return jexl.newInstance(className, context);
+ }
+
+ }
+
+ @Test
+ public void testFunctor2Name() throws Exception {
+ functorTwo(ClassCreator.GEN_CLASS + "foo2");
+ }
+
+ @Test
+ public void testFunctor2Class() throws Exception {
+ functorTwo(new NsTest(ClassCreator.GEN_CLASS + "foo2"));
+ }
+
+ void functorTwo(Object nstest) throws Exception {
+ // create jexl2 with a 'test' namespace
+ Map<String, Object> ns = new HashMap<String, Object>();
+ ns.put("test", nstest);
+ JexlEngine jexl2 = new JexlBuilder().namespaces(ns).create();
+ JexlContext ctxt = new MapContext();
+ ctxt.set("value", 1000);
+
+ // inject 'foo2' as test namespace functor class
+ ClassCreator cctor = new ClassCreator(jexl, base);
+ cctor.setSeed(2);
+ cctor.setCtorBody("value = (Integer) ctxt.get(\"value\") + 10;");
+ Class<?> foo1 = cctor.createClass(true);
+ Assert.assertTrue(foo1.getClassLoader() == cctor.getClassLoader());
+ Assert.assertEquals("foo2", foo1.getSimpleName());
+ Object result = cctor.newInstance(foo1, ctxt);
+ Assert.assertEquals(foo1, result.getClass());
+ jexl2.setClassLoader(cctor.getClassLoader());
+ cctor.clear();
+
+ // check the namespace functor behavior
+ JexlScript script = jexl2.createScript("test:getValue()");
+ result = script.execute(ctxt, foo1.getName());
+ Assert.assertEquals(1010, result);
+
+ // change the body
+ cctor.setSeed(2);
+ cctor.setCtorBody("value = (Integer) ctxt.get(\"value\") + 99;");
+ Class<?> foo11 = cctor.createClass(true);
+ Assert.assertEquals("foo2", foo1.getSimpleName());
+ Assert.assertTrue(foo11 != foo1);
+ foo1 = foo11;
+ result = cctor .newInstance(foo1, ctxt);
+ Assert.assertEquals(foo1, result.getClass());
+ // drum rolll....
+ jexl2.setClassLoader(foo1.getClassLoader());
+ result = script.execute(ctxt, foo1.getName());
+ // tada!
+ Assert.assertEquals(1099, result);
+ }
+
+ @Test
+ public void testFunctorThree() throws Exception {
+ JexlContext ctxt = new MapContext();
+ ctxt.set("value", 1000);
+
+ ClassCreator cctor = new ClassCreator(jexl, base);
+ cctor.setSeed(2);
+ cctor.setCtorBody("value = (Integer) ctxt.get(\"value\") + 10;");
+ Class<?> foo1 = cctor.createClass(true);
+ Assert.assertTrue(foo1.getClassLoader() == cctor.getClassLoader());
+ Assert.assertEquals("foo2", foo1.getSimpleName());
+ Object result = cctor.newInstance(foo1, ctxt);
+ Assert.assertEquals(foo1, result.getClass());
+ jexl.setClassLoader(cctor.getClassLoader());
+ cctor.clear();
+
+ Map<String, Object> ns = new HashMap<String, Object>();
+ ns.put("test", foo1);
+ JexlEngine jexl2 = new JexlBuilder().namespaces(ns).create();
+
+ JexlScript script = jexl2.createScript("test:getValue()");
+ result = script.execute(ctxt, foo1.getName());
+ Assert.assertEquals(1010, result);
+
+ cctor.setSeed(2);
+ cctor.setCtorBody("value = (Integer) ctxt.get(\"value\") + 99;");
+ Class<?> foo11 = cctor.createClass(true);
+ Assert.assertEquals("foo2", foo1.getSimpleName());
+ Assert.assertTrue(foo11 != foo1);
+ foo1 = foo11;
+ result = cctor.newInstance(foo1, ctxt);
+ Assert.assertEquals(foo1, result.getClass());
+ // drum rolll....
+ jexl2.setClassLoader(foo1.getClassLoader());
+ result = script.execute(ctxt, foo1.getName());
+ // tada!
+ Assert.assertEquals(1099, result);
+ }
+
@Test
public void testMany() throws Exception {
// abort test if class creator can not run
--
To stop receiving notification emails like this one, please contact
henrib@apache.org.