View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *   https://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  package org.apache.commons.jexl3.scripting;
19  
20  import static org.junit.jupiter.api.Assertions.assertEquals;
21  import static org.junit.jupiter.api.Assertions.assertInstanceOf;
22  import static org.junit.jupiter.api.Assertions.assertNotNull;
23  import static org.junit.jupiter.api.Assertions.assertNull;
24  import static org.junit.jupiter.api.Assertions.assertThrows;
25  import static org.junit.jupiter.api.Assertions.assertTrue;
26  
27  import java.io.BufferedReader;
28  import java.io.BufferedWriter;
29  import java.io.ByteArrayOutputStream;
30  import java.io.FileWriter;
31  import java.io.PrintStream;
32  import java.io.PrintWriter;
33  import java.io.Reader;
34  import java.io.StringReader;
35  import java.io.StringWriter;
36  import java.nio.file.Files;
37  import java.nio.file.Path;
38  import java.util.Arrays;
39  import java.util.List;
40  import java.util.Map;
41  
42  import javax.script.CompiledScript;
43  import javax.script.ScriptContext;
44  import javax.script.ScriptEngine;
45  import javax.script.ScriptEngineManager;
46  import javax.script.ScriptException;
47  
48  import org.apache.commons.jexl3.JexlBuilder;
49  import org.apache.commons.jexl3.JexlException;
50  import org.apache.commons.jexl3.introspection.JexlPermissions;
51  import org.junit.jupiter.api.AfterEach;
52  import org.junit.jupiter.api.Assertions;
53  import org.junit.jupiter.api.Test;
54  
55  class JexlScriptEngineTest {
56      public static class Errors {
57          public int illegal() {
58              throw new IllegalArgumentException("jexl");
59          }
60          public int npe() {
61              throw new NullPointerException("jexl");
62          }
63      }
64      private static final List<String> NAMES = Arrays.asList("JEXL", "Jexl", "jexl",
65                                                              "JEXL2", "Jexl2", "jexl2",
66                                                              "JEXL3", "Jexl3", "jexl3");
67      private static final List<String> EXTENSIONS = Arrays.asList("jexl", "jexl2", "jexl3");
68  
69      private static final List<String> MIMES = Arrays.asList("application/x-jexl",
70                                                              "application/x-jexl2",
71                                                              "application/x-jexl3");
72  
73      private static final String LF = System.lineSeparator();
74  
75      @AfterEach
76      void tearDown() {
77          JexlBuilder.setDefaultPermissions(null);
78          JexlScriptEngine.setInstance(null);
79          JexlScriptEngine.setPermissions(null);
80      }
81  
82      @Test
83      void testCompile() throws Exception {
84          final ScriptEngineManager manager = new ScriptEngineManager();
85          final JexlScriptEngine engine = (JexlScriptEngine) manager.getEngineByName("JEXL");
86          final ScriptContext ctxt = engine.getContext();
87          final String str = null;
88          final Reader reader = null;
89          assertThrows(NullPointerException.class, () -> engine.compile(str));
90          assertThrows(NullPointerException.class, () -> engine.compile(reader));
91          final CompiledScript script0 = engine.compile(new StringReader("3 + 4"));
92          assertEquals(engine, script0.getEngine());
93          Object result = script0.eval();
94          assertEquals(7, result);
95          result = script0.eval();
96          assertEquals(7, result);
97          result = engine.eval(new StringReader("38 + 4"));
98          assertEquals(42, result);
99          result = engine.eval("38 + 4");
100         assertEquals(42, result);
101         // next test
102         final CompiledScript script1 = engine.compile("3 + 4");
103         assertEquals(engine, script1.getEngine());
104         Object result1 = script1.eval();
105         assertEquals(7, result1);
106         result1 = script1.eval();
107         assertEquals(7, result1);
108         // next test
109         ctxt.setAttribute("x", 20, ScriptContext.ENGINE_SCOPE);
110         ctxt.setAttribute("y", 22, ScriptContext.ENGINE_SCOPE);
111         final CompiledScript script2 = engine.compile("x + y");
112         Object result2 = script2.eval();
113         assertEquals(42, result2);
114         ctxt.setAttribute("x", -20, ScriptContext.ENGINE_SCOPE);
115         ctxt.setAttribute("y", -22, ScriptContext.ENGINE_SCOPE);
116         result2 = script2.eval();
117         assertEquals(-42, result2);
118     }
119 
120     @Test
121     void testDirectNew() throws Exception {
122         final ScriptEngine engine = new JexlScriptEngine();
123         final Integer initialValue = 123;
124         assertEquals(initialValue,engine.eval("123"));
125     }
126 
127     @Test
128     void testDottedNames() throws Exception {
129         final ScriptEngineManager manager = new ScriptEngineManager();
130         assertNotNull(manager, "Manager should not be null");
131         final ScriptEngine engine = manager.getEngineByName("JEXL");
132         assertNotNull(engine, "Engine should not be null (JEXL)");
133         engine.eval("this.is.a.test=null");
134         assertNull(engine.get("this.is.a.test"));
135         assertEquals(Boolean.TRUE, engine.eval("empty(this.is.a.test)"));
136         final Object mymap = engine.eval("testmap={ 'key1' : 'value1', 'key2' : 'value2' }");
137         assertInstanceOf(Map.class, mymap);
138         assertEquals(2,((Map<?, ?>)mymap).size());
139     }
140 
141     @Test
142     void testErrors() throws Exception {
143         final ScriptEngineManager manager = new ScriptEngineManager();
144         final JexlScriptEngine engine = (JexlScriptEngine) manager.getEngineByName("JEXL");
145         engine.put("errors", new Errors());
146         assertInstanceOf(NullPointerException.class, assertThrows(ScriptException.class, () -> engine.eval("errors.npe()")).getCause());
147         assertInstanceOf(IllegalArgumentException.class, assertThrows(ScriptException.class, () -> engine.eval("errors.illegal()")).getCause());
148         final CompiledScript script0 = engine.compile("errors.npe()");
149         assertInstanceOf(NullPointerException.class, assertThrows(ScriptException.class, script0::eval).getCause());
150         final CompiledScript script1 = engine.compile("errors.illegal()");
151         assertInstanceOf(IllegalArgumentException.class, assertThrows(ScriptException.class, script1::eval).getCause());
152     }
153 
154     @Test
155     void testNulls() {
156         final ScriptEngineManager manager = new ScriptEngineManager();
157         assertNotNull(manager, "Manager should not be null");
158         final ScriptEngine engine = manager.getEngineByName("jexl3");
159         assertNotNull(engine, "Engine should not be null (name)");
160         assertNotNull(engine.getFactory());
161         assertThrows(NullPointerException.class, () -> engine.eval((String) null));
162         assertThrows(NullPointerException.class, () -> engine.eval((Reader) null));
163         final ScriptContext ctxt = null;
164         assertThrows(NullPointerException.class, () -> engine.eval((String) null, ctxt));
165         assertThrows(NullPointerException.class, () -> engine.eval((Reader) null, ctxt));
166     }
167 
168     @Test
169     void testScopes() throws Exception {
170         final ScriptEngineManager manager = new ScriptEngineManager();
171         assertNotNull(manager, "Manager should not be null");
172         final ScriptEngine engine = manager.getEngineByName("jexl3");
173         assertNotNull(engine, "Engine should not be null (name)");
174         manager.put("global", 1);
175         engine.put("local", 10);
176         manager.put("both", 7);
177         engine.put("both", 7);
178         engine.eval("local=local+1");
179         engine.eval("global=global+1");
180         engine.eval("both=both+1"); // should update engine value only
181         engine.eval("newvar=42;");
182         assertEquals(2,manager.get("global"));
183         assertEquals(11,engine.get("local"));
184         assertEquals(7,manager.get("both"));
185         assertEquals(8,engine.get("both"));
186         assertEquals(42,engine.get("newvar"));
187         assertNull(manager.get("newvar"));
188     }
189 
190     @Test
191     void testScriptEngineFactory() {
192         final JexlScriptEngineFactory factory = new JexlScriptEngineFactory();
193         assertEquals("JEXL Engine", factory.getParameter(ScriptEngine.ENGINE));
194         assertEquals("3.6", factory.getParameter(ScriptEngine.ENGINE_VERSION));
195         assertEquals("JEXL", factory.getParameter(ScriptEngine.LANGUAGE));
196         assertEquals("3.6", factory.getParameter(ScriptEngine.LANGUAGE_VERSION));
197         assertNull(factory.getParameter("THREADING"));
198         assertEquals(NAMES, factory.getParameter(ScriptEngine.NAME));
199         assertEquals(EXTENSIONS, factory.getExtensions());
200         assertEquals(MIMES, factory.getMimeTypes());
201 
202         assertEquals("42;", factory.getProgram("42"));
203         assertEquals("str.substring(3,4)", factory.getMethodCallSyntax("str", "substring", "3", "4"));
204     }
205 
206     @Test
207     void testScripting() throws Exception {
208         final ScriptEngineManager manager = new ScriptEngineManager();
209         assertNotNull(manager, "Manager should not be null");
210         final ScriptEngine engine = manager.getEngineByName("jexl3");
211         final Integer initialValue = 123;
212         assertEquals(initialValue, engine.eval("123"));
213         assertEquals(initialValue, engine.eval("0;123")); // multiple statements
214         final ScriptException xscript = assertThrows(ScriptException.class,
215                 () -> engine.eval("sys=context.class.forName(\"java.lang.System\");now=sys.currentTimeMillis();"));
216         final JexlException.Method xjexl = (JexlException.Method) xscript.getCause();
217         assertEquals("forName", xjexl.getMethod());
218         engine.put("value", initialValue);
219         assertEquals(initialValue, engine.get("value"));
220         final Integer newValue = 124;
221         assertEquals(newValue, engine.eval("old=value;value=value+1"));
222         assertEquals(initialValue, engine.get("old"));
223         assertEquals(newValue, engine.get("value"));
224         assertEquals(engine.getContext(), engine.get(JexlScriptEngine.CONTEXT_KEY));
225         // Check behavior of JEXL object
226         assertEquals(engine.getContext().getReader(), engine.eval("JEXL.in"));
227         assertEquals(engine.getContext().getWriter(), engine.eval("JEXL.out"));
228         assertEquals(engine.getContext().getErrorWriter(), engine.eval("JEXL.err"));
229         assertEquals(System.class, engine.eval("JEXL.System"));
230     }
231 
232     @Test
233     void testScriptingGetBy() {
234         final ScriptEngineManager manager = new ScriptEngineManager();
235         assertNotNull(manager, "Manager should not be null");
236         for (final String name : NAMES) {
237             final ScriptEngine engine = manager.getEngineByName(name);
238             assertNotNull(engine, "Engine should not be null (name)");
239         }
240         for (final String extension : EXTENSIONS) {
241             final ScriptEngine engine = manager.getEngineByExtension(extension);
242             assertNotNull(engine, "Engine should not be null (extension)");
243         }
244         for (final String mime : MIMES) {
245             final ScriptEngine engine = manager.getEngineByMimeType(mime);
246             assertNotNull(engine, "Engine should not be null (mime)");
247         }
248     }
249     @Test
250     void testScriptingInstance0() throws Exception {
251         JexlScriptEngine.setPermissions(JexlPermissions.UNRESTRICTED);
252         final ScriptEngineManager manager = new ScriptEngineManager();
253         final ScriptEngine engine = manager.getEngineByName("jexl3");
254         final Long time2 = (Long) engine.eval(
255                 "sys=context.class.forName(\"java.lang.System\");"
256                         + "now=sys.currentTimeMillis();");
257         assertTrue(time2 <= System.currentTimeMillis());
258     }
259 
260     @Test
261     void testScriptingPermissions1() throws Exception {
262         JexlBuilder.setDefaultPermissions(JexlPermissions.UNRESTRICTED);
263         JexlScriptEngine.setPermissions(null);
264         final ScriptEngineManager manager = new ScriptEngineManager();
265         final ScriptEngine engine = manager.getEngineByName("jexl3");
266         final Long time2 = (Long) engine.eval(
267                 "sys=context.class.forName(\"java.lang.System\");"
268                         + "now=sys.currentTimeMillis();");
269         assertTrue(time2 <= System.currentTimeMillis());
270     }
271 
272     @Test
273     void testMain0() throws Exception {
274         final StringWriter strw = new StringWriter();
275         final StringReader strr = new StringReader("a=20\nb=22\na+b\n//q!\n");
276         Main.run(new BufferedReader(strr), new PrintWriter(strw), null);
277         final String ctl = "> >> 20" + LF +
278                 "> >> 22" + LF +
279                 "> >> 42" + LF +
280                 "> ";
281         Assertions.assertEquals(ctl, strw.toString());
282     }
283 
284     @Test
285     void testMain1() throws Exception {
286         final StringWriter strw = new StringWriter();
287         final StringReader strr = new StringReader("args[0]+args[1]");
288         Main.run(new BufferedReader(strr), new PrintWriter(strw), new Object[]{20, 22});
289         final String ctl = ">>: 42" + LF;
290         Assertions.assertEquals(ctl, strw.toString());
291     }
292 
293     @Test
294     void testMain2() throws Exception {
295         final ByteArrayOutputStream outContent = new ByteArrayOutputStream();
296         final PrintStream originalOut = System.out;
297         Path file = null;
298         try {
299             System.setOut(new PrintStream(outContent));
300             file = Files.createTempFile("test-jsr233", ".jexl");
301             final BufferedWriter writer = new BufferedWriter(new FileWriter(file.toFile()));
302             writer.write("a=20;\nb=22;\na+b\n");
303             writer.close();
304             final String ctl = ">>: 42" + LF;
305             Main.main(new String[]{file.toString()});
306             Assertions.assertEquals(ctl, outContent.toString());
307         } finally {
308             System.setOut(originalOut);
309             if (file != null) {
310                 Files.delete(file);
311             }
312         }
313     }
314 
315 }