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 &amp; 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.