This is an automated email from the ASF dual-hosted git repository.

henrib pushed a commit to branch JEXL-350
in repository https://gitbox.apache.org/repos/asf/commons-bsf.git

commit 03b69f1681f82d0916682b18d74a420a628370a3
Author: Henrib <hbies...@gmail.com>
AuthorDate: Wed Apr 16 19:15:21 2025 +0200

    - Updated JEXL dependency to JEXL 3.5.0;
    - added tests;
---
 pom.xml                                            |   6 +-
 .../org/apache/bsf/engines/jexl/JEXLEngine.java    | 165 +++++++++++++++++----
 .../org/apache/bsf/engines/JavascriptTest.java     |  26 ++--
 src/test/java/org/apache/bsf/engines/JexlTest.java |  52 +++++++
 4 files changed, 206 insertions(+), 43 deletions(-)

diff --git a/pom.xml b/pom.xml
index df23e99..38ba820 100644
--- a/pom.xml
+++ b/pom.xml
@@ -201,9 +201,9 @@
       <scope>test</scope>
     </dependency>
     <dependency>
-      <groupId>commons-jexl</groupId>
-      <artifactId>commons-jexl</artifactId>
-      <version>1.1</version>
+      <groupId>org.apache.commons</groupId>
+      <artifactId>commons-jexl3</artifactId>
+      <version>3.5.0</version>
     </dependency>
     <dependency>
       <groupId>rhino</groupId>
diff --git a/src/main/java/org/apache/bsf/engines/jexl/JEXLEngine.java 
b/src/main/java/org/apache/bsf/engines/jexl/JEXLEngine.java
index c9671db..5b07b6c 100644
--- a/src/main/java/org/apache/bsf/engines/jexl/JEXLEngine.java
+++ b/src/main/java/org/apache/bsf/engines/jexl/JEXLEngine.java
@@ -16,20 +16,32 @@
  */
 package org.apache.bsf.engines.jexl;
 
+import java.io.BufferedReader;
 import java.io.File;
-import java.lang.reflect.Method;
+import java.io.IOException;
+import java.io.InputStreamReader;
 import java.net.URL;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.util.Map;
+import java.util.Objects;
 import java.util.Vector;
+import java.util.concurrent.ConcurrentHashMap;
 
 import org.apache.bsf.BSFDeclaredBean;
 import org.apache.bsf.BSFEngine;
 import org.apache.bsf.BSFException;
 import org.apache.bsf.BSFManager;
 import org.apache.bsf.util.BSFEngineImpl;
-import org.apache.commons.jexl.JexlContext;
-import org.apache.commons.jexl.JexlHelper;
-import org.apache.commons.jexl.Script;
-import org.apache.commons.jexl.ScriptFactory;
+import org.apache.bsf.util.BSFFunctions;
+import org.apache.commons.jexl3.JexlContext;
+import org.apache.commons.jexl3.JexlEngine;
+import org.apache.commons.jexl3.JexlException;
+import org.apache.commons.jexl3.JexlExpression;
+import org.apache.commons.jexl3.JexlInfo;
+import org.apache.commons.jexl3.JexlScript;
+import org.apache.commons.jexl3.JexlBuilder;
+import org.apache.commons.jexl3.introspection.JexlPermissions;
 
 /**
  * {@link BSFEngine} for Commons JEXL. Requires Commons JEXL version 1.1 or 
later.
@@ -37,10 +49,47 @@ import org.apache.commons.jexl.ScriptFactory;
  * @see <a href="http://commons.apache.org/jexl/";>Commons JEXL</a>
  */
 public class JEXLEngine extends BSFEngineImpl {
-
+    private static JexlPermissions BSF_PERMISSIONS = 
JexlPermissions.RESTRICTED;
+    /** The engine. */
+    private JexlEngine engine;
+    /** The declared bean */
+    private Map<String, Object> vars;
     /** The backing JexlContext for this engine. */
     private JexlContext jc;
 
+    /**
+     * Sets the JEXL engine permissions.
+     * @param permissions the permissions
+     */
+    public static void setPermissions(JexlPermissions permissions) {
+        BSF_PERMISSIONS = permissions;
+    }
+
+    public static class BSFContext implements JexlContext {
+        private final Map<String, Object> map;
+
+        BSFContext(Map<String, Object> vars) {
+            this.map = vars;
+        }
+
+        public void clear() {
+            this.map.clear();
+        }
+
+        public Object get(String name) {
+            return this.map.get(name);
+        }
+
+        public boolean has(String name) {
+            return this.map.containsKey(name);
+        }
+
+        public void set(String name, Object value) {
+                this.map.put(name, value);
+            }
+    }
+
+
     /**
      * Initialize the JEXL engine by creating a JexlContext and populating it 
with the declared beans.
      *
@@ -51,11 +100,17 @@ public class JEXLEngine extends BSFEngineImpl {
      */
     public void initialize(final BSFManager mgr, final String lang, final 
Vector declaredBeans) throws BSFException {
         super.initialize(mgr, lang, declaredBeans);
-        jc = JexlHelper.createContext();
+        vars = new ConcurrentHashMap<>();
+        jc = new BSFContext(vars);
         for (int i = 0; i < declaredBeans.size(); i++) {
             final BSFDeclaredBean bean = (BSFDeclaredBean) 
declaredBeans.elementAt(i);
-            jc.getVars().put(bean.name, bean.bean);
+            vars.put(bean.name, bean.bean);
         }
+        vars.put("java.lang.System.out", java.lang.System.out);
+        vars.put("java.lang.System.in", java.lang.System.in);
+        vars.put("java.lang.System.err", java.lang.System.err);
+        vars.put("bsf", new BSFFunctions(mgr, this));
+        engine = new 
JexlBuilder().cache(32).permissions(BSF_PERMISSIONS).create();
     }
 
     /**
@@ -63,8 +118,9 @@ public class JEXLEngine extends BSFEngineImpl {
      */
     public void terminate() {
         if (jc != null) {
-            jc.getVars().clear();
+            vars.clear();
             jc = null;
+            engine = null;
         }
     }
 
@@ -75,7 +131,7 @@ public class JEXLEngine extends BSFEngineImpl {
      * @throws BSFException For any exception that occurs while trying to 
declare the bean.
      */
     public void declareBean(final BSFDeclaredBean bean) throws BSFException {
-        jc.getVars().put(bean.name, bean.bean);
+        vars.put(bean.name, bean.bean);
     }
 
     /**
@@ -85,7 +141,7 @@ public class JEXLEngine extends BSFEngineImpl {
      * @throws BSFException For any exception that occurs while trying to 
undeclare the bean.
      */
     public void undeclareBean(final BSFDeclaredBean bean) throws BSFException {
-        jc.getVars().remove(bean.name);
+        vars.remove(bean.name);
     }
 
     /**
@@ -101,23 +157,27 @@ public class JEXLEngine extends BSFEngineImpl {
         if (expr == null) {
             return null;
         }
+        final JexlInfo info = new JexlInfo(
+                fileName != null ? fileName : expr.toString(),
+                Math.max(lineNo, 1),
+                Math.max(colNo, 1));
         try {
-            Script jExpr = null;
+            JexlExpression jExpr;
             if (expr instanceof File) {
-                jExpr = ScriptFactory.createScript((File) expr);
+                jExpr = engine.createExpression(info, readSource(info, (File) 
expr));
             } else if (expr instanceof URL) {
-                jExpr = ScriptFactory.createScript((URL) expr);
+                jExpr = engine.createExpression(info, readSource(info, (URL) 
expr));
             } else {
-                jExpr = ScriptFactory.createScript((String) expr);
+                jExpr = engine.createExpression(info, (String) expr);
             }
-            return jExpr.execute(jc);
+            return jExpr.evaluate(jc);
         } catch (final Exception e) {
             throw new BSFException(BSFException.REASON_EXECUTION_ERROR, 
"Exception from Commons JEXL:\n" + e.getMessage(), e);
         }
     }
 
     /**
-     * Executes the script as a JEXL {@link Script}.
+     * Executes the script as a JEXL {@link JexlScript}.
      *
      * @param fileName The file name, if it is available.
      * @param lineNo   The line number, if it is available.
@@ -129,14 +189,18 @@ public class JEXLEngine extends BSFEngineImpl {
         if (script == null) {
             return;
         }
+        final JexlInfo info = new JexlInfo(
+                fileName != null ? fileName : script.toString(),
+                Math.max(lineNo, 1),
+                Math.max(colNo, 1));
         try {
-            Script jExpr = null;
+            JexlScript jExpr;
             if (script instanceof File) {
-                jExpr = ScriptFactory.createScript((File) script);
+                jExpr = engine.createScript(info, readSource(info, (File) 
script));
             } else if (script instanceof URL) {
-                jExpr = ScriptFactory.createScript((URL) script);
+                jExpr = engine.createScript(info, readSource(info, (URL) 
script));
             } else {
-                jExpr = ScriptFactory.createScript((String) script);
+                jExpr = engine.createScript(info, (String) script);
             }
             jExpr.execute(jc);
         } catch (final Exception e) {
@@ -166,17 +230,64 @@ public class JEXLEngine extends BSFEngineImpl {
      * @return The result of the call.
      * @throws BSFException For any exception that occurs while making the 
call.
      */
-    public Object call(final Object object, final String name, final Object[] 
args) throws BSFException {
+    public Object call(Object object, final String name, final Object[] args) 
throws BSFException {
         try {
-            final Class[] types = new Class[args.length];
-            for (int i = 0; i < args.length; i++) {
-                types[i] = args[i].getClass();
+            if (object == null) {
+                object = vars.get(name);
             }
-            final Method m = object.getClass().getMethod(name, types);
-            return m.invoke(object, args);
+            if (object instanceof JexlScript) {
+               return ((JexlScript) object).execute(jc, args);
+            }
+            return engine.invokeMethod(object, name, args);
         } catch (final Exception e) {
             throw new BSFException(BSFException.REASON_EXECUTION_ERROR, 
"Exception from JEXLEngine:\n" + e.getMessage(), e);
         }
     }
 
+    /**
+     * Reads a JEXL source from a File.
+     *
+     * @param info the script source info
+     * @param file the script file
+     * @return the source
+     */
+    protected String readSource(JexlInfo info, final File file) {
+        Objects.requireNonNull(file, "file");
+        try (BufferedReader reader = Files.newBufferedReader(file.toPath(), 
StandardCharsets.UTF_8)) {
+            return toString(reader);
+        } catch (final IOException xio) {
+            throw new JexlException(info, "could not read source File", xio);
+        }
+    }
+
+    /**
+     * Reads a JEXL source from an URL.
+     *
+     * @param info the script source info
+     * @param url the script url
+     * @return the source
+     */
+    protected String readSource(JexlInfo info, final URL url) {
+        Objects.requireNonNull(url, "url");
+        try (BufferedReader reader = new BufferedReader(new 
InputStreamReader(url.openStream(), StandardCharsets.UTF_8))) {
+            return toString(reader);
+        } catch (final IOException xio) {
+            throw new JexlException(info, "could not read source URL", xio);
+        }
+    }
+    /**
+     * Creates a string from a reader.
+     *
+     * @param reader to be read.
+     * @return the contents of the reader as a String.
+     * @throws IOException on any error reading the reader.
+     */
+    protected static String toString(final BufferedReader reader) throws 
IOException {
+        final StringBuilder buffer = new StringBuilder();
+        String line;
+        while ((line = reader.readLine()) != null) {
+            buffer.append(line).append('\n');
+        }
+        return buffer.toString();
+    }
 }
diff --git a/src/test/java/org/apache/bsf/engines/JavascriptTest.java 
b/src/test/java/org/apache/bsf/engines/JavascriptTest.java
index 374b9e4..a093ad3 100644
--- a/src/test/java/org/apache/bsf/engines/JavascriptTest.java
+++ b/src/test/java/org/apache/bsf/engines/JavascriptTest.java
@@ -25,7 +25,7 @@ import org.apache.bsf.BSFException;
  * Test class for the Rhino language engine.
  */
 public class JavascriptTest extends BSFEngineTestCase {
-    private BSFEngine javascriptEngine;
+    protected BSFEngine engine;
 
     public JavascriptTest(final String name) {
         super(name);
@@ -35,7 +35,7 @@ public class JavascriptTest extends BSFEngineTestCase {
         super.setUp();
 
         try {
-            javascriptEngine = bsfManager.loadScriptingEngine("javascript");
+            engine = bsfManager.loadScriptingEngine("javascript");
         } catch (final Exception e) {
             fail(failMessage("Failure attempting to load javascript engine", 
e));
         }
@@ -43,7 +43,7 @@ public class JavascriptTest extends BSFEngineTestCase {
 
     public void testExec() {
         try {
-            javascriptEngine.exec("Test.js", 0, 0, "java.lang.System.out.print 
" + "(\"PASSED\");");
+            engine.exec("Test.js", 0, 0, "java.lang.System.out.print " + 
"(\"PASSED\");");
         } catch (final Exception e) {
             fail(failMessage("exec() test failed", e));
         }
@@ -55,7 +55,7 @@ public class JavascriptTest extends BSFEngineTestCase {
         Double retval = null;
 
         try {
-            retval = Double.valueOf((javascriptEngine.eval("Test.js", 0, 0, "1 
+ 1;").toString()));
+            retval = Double.valueOf((engine.eval("Test.js", 0, 0, "1 + 
1").toString()));
         } catch (final Exception e) {
             fail(failMessage("eval() test failed", e));
         }
@@ -68,8 +68,8 @@ public class JavascriptTest extends BSFEngineTestCase {
         Double retval = null;
 
         try {
-            javascriptEngine.exec("Test.js", 0, 0, "function addOne (f) {\n 
return f + 1;\n}");
-            retval = Double.valueOf((javascriptEngine.call(null, "addOne", 
args).toString()));
+            engine.exec("Test.js", 0, 0, "function addOne (f) {\n return f + 
1;\n}");
+            retval = Double.valueOf((engine.call(null, "addOne", 
args).toString()));
         } catch (final Exception e) {
             fail(failMessage("call() test failed", e));
         }
@@ -79,7 +79,7 @@ public class JavascriptTest extends BSFEngineTestCase {
 
     public void testIexec() {
         try {
-            javascriptEngine.iexec("Test.js", 0, 0, 
"java.lang.System.out.print " + "(\"PASSED\");");
+            engine.iexec("Test.js", 0, 0, "java.lang.System.out.print " + 
"(\"PASSED\")");
         } catch (final Exception e) {
             fail(failMessage("iexec() test failed", e));
         }
@@ -91,7 +91,7 @@ public class JavascriptTest extends BSFEngineTestCase {
         Double retval = null;
 
         try {
-            retval = Double.valueOf((bsfManager.eval("javascript", "Test.js", 
0, 0, "1 + 1;")).toString());
+            retval = Double.valueOf((bsfManager.eval("javascript", "Test.js", 
0, 0, "1 + 1")).toString());
         } catch (final Exception e) {
             fail(failMessage("BSFManager eval() test failed", e));
         }
@@ -103,7 +103,7 @@ public class JavascriptTest extends BSFEngineTestCase {
         Object retval = null;
 
         try {
-            retval = javascriptEngine.eval("Test.js", 0, 0, 
"bsf.lookupBean(\"foo\");");
+            retval = engine.eval("Test.js", 0, 0, "bsf.lookupBean(\"foo\")");
         } catch (final Exception e) {
             fail(failMessage("Test of BSFManager availability failed", e));
         }
@@ -117,7 +117,7 @@ public class JavascriptTest extends BSFEngineTestCase {
 
         try {
             bsfManager.registerBean("foo", foo);
-            bar = (Double) javascriptEngine.eval("Test.js", 0, 0, 
"bsf.lookupBean(\"foo\");");
+            bar = (Double) engine.eval("Test.js", 0, 0, 
"bsf.lookupBean(\"foo\")");
         } catch (final Exception e) {
             fail(failMessage("registerBean() test failed", e));
         }
@@ -132,7 +132,7 @@ public class JavascriptTest extends BSFEngineTestCase {
         try {
             bsfManager.registerBean("foo", foo);
             bsfManager.unregisterBean("foo");
-            bar = (Double) javascriptEngine.eval("Test.js", 0, 0, 
"bsf.lookupBean(\"foo\");");
+            bar = (Double) engine.eval("Test.js", 0, 0, 
"bsf.lookupBean(\"foo\")");
         } catch (final Exception e) {
             fail(failMessage("unregisterBean() test failed", e));
         }
@@ -146,7 +146,7 @@ public class JavascriptTest extends BSFEngineTestCase {
 
         try {
             bsfManager.declareBean("foo", foo, Double.class);
-            bar = (Double) javascriptEngine.eval("Test.js", 0, 0, "foo + 1;");
+            bar = (Double) engine.eval("Test.js", 0, 0, "foo + 1");
         } catch (final Exception e) {
             fail(failMessage("declareBean() test failed", e));
         }
@@ -161,7 +161,7 @@ public class JavascriptTest extends BSFEngineTestCase {
         try {
             bsfManager.declareBean("foo", foo, Double.class);
             bsfManager.undeclareBean("foo");
-            bar = (Double) javascriptEngine.eval("Test.js", 0, 0, "foo + 1");
+            bar = (Double) engine.eval("Test.js", 0, 0, "foo + 1");
         } catch (final BSFException bsfE) {
             // Do nothing. This is the expected case.
         } catch (final Exception e) {
diff --git a/src/test/java/org/apache/bsf/engines/JexlTest.java 
b/src/test/java/org/apache/bsf/engines/JexlTest.java
new file mode 100644
index 0000000..bd4636f
--- /dev/null
+++ b/src/test/java/org/apache/bsf/engines/JexlTest.java
@@ -0,0 +1,52 @@
+/*
+ * 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.bsf.engines;
+
+import org.apache.bsf.engines.jexl.JEXLEngine;
+import org.apache.commons.jexl3.introspection.JexlPermissions;
+
+public class JexlTest extends JavascriptTest {
+  public JexlTest(String name) {
+    super(name);
+  }
+
+  public void setUp() {
+  super.setUp();
+  try {
+    JEXLEngine.setPermissions(JexlPermissions.UNRESTRICTED);
+    engine = bsfManager.loadScriptingEngine("jexl");
+  } catch (final Exception e) {
+    fail(failMessage("Failure attempting to load jexl engine", e));
+  }
+}
+
+
+  public void testCall() {
+    final Object[] args = { Double.valueOf(1) };
+    Double retval = null;
+
+    try {
+      engine.exec("Test.js", 0, 0, "addOne  = (f) -> {\n return f + 1;\n}");
+      retval = Double.valueOf((engine.call(null, "addOne", args).toString()));
+    } catch (final Exception e) {
+      fail(failMessage("call() test failed", e));
+    }
+
+    assertEquals(Double.valueOf(2), retval);
+  }
+}

Reply via email to