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    *   http://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.assertNotNull;
22  import static org.junit.jupiter.api.Assertions.assertNull;
23  import static org.junit.jupiter.api.Assertions.assertThrows;
24  import static org.junit.jupiter.api.Assertions.assertTrue;
25  
26  import java.io.Reader;
27  import java.io.StringReader;
28  import java.util.Arrays;
29  import java.util.List;
30  import java.util.Map;
31  
32  import javax.script.CompiledScript;
33  import javax.script.ScriptContext;
34  import javax.script.ScriptEngine;
35  import javax.script.ScriptEngineManager;
36  import javax.script.ScriptException;
37  
38  import org.apache.commons.jexl3.JexlBuilder;
39  import org.apache.commons.jexl3.JexlException;
40  import org.apache.commons.jexl3.introspection.JexlPermissions;
41  import org.junit.jupiter.api.AfterEach;
42  import org.junit.jupiter.api.Test;
43  
44  public class JexlScriptEngineTest {
45      public static class Errors {
46          public int illegal() {
47              throw new IllegalArgumentException("jexl");
48          }
49          public int npe() {
50              throw new NullPointerException("jexl");
51          }
52      }
53      private static final List<String> NAMES = Arrays.asList("JEXL", "Jexl", "jexl",
54                                                              "JEXL2", "Jexl2", "jexl2",
55                                                              "JEXL3", "Jexl3", "jexl3");
56      private static final List<String> EXTENSIONS = Arrays.asList("jexl", "jexl2", "jexl3");
57  
58      private static final List<String> MIMES = Arrays.asList("application/x-jexl",
59                                                              "application/x-jexl2",
60                                                              "application/x-jexl3");
61  
62      @AfterEach
63      public void tearDown() {
64          JexlBuilder.setDefaultPermissions(null);
65          JexlScriptEngine.setInstance(null);
66          JexlScriptEngine.setPermissions(null);
67      }
68  
69      @Test
70      public void testCompile() throws Exception {
71          final ScriptEngineManager manager = new ScriptEngineManager();
72          final JexlScriptEngine engine = (JexlScriptEngine) manager.getEngineByName("JEXL");
73          final ScriptContext ctxt = engine.getContext();
74          final String str = null;
75          final Reader reader = null;
76          assertThrows(NullPointerException.class, () -> engine.compile(str));
77          assertThrows(NullPointerException.class, () -> engine.compile(reader));
78          final CompiledScript script0 = engine.compile(new StringReader("3 + 4"));
79          assertEquals(engine, script0.getEngine());
80          Object result = script0.eval();
81          assertEquals(7, result);
82          result = script0.eval();
83          assertEquals(7, result);
84          result = engine.eval(new StringReader("38 + 4"));
85          assertEquals(42, result);
86          result = engine.eval("38 + 4");
87          assertEquals(42, result);
88          // next test
89          final CompiledScript script1 = engine.compile("3 + 4");
90          assertEquals(engine, script1.getEngine());
91          Object result1 = script1.eval();
92          assertEquals(7, result1);
93          result1 = script1.eval();
94          assertEquals(7, result1);
95          // next test
96          ctxt.setAttribute("x", 20, ScriptContext.ENGINE_SCOPE);
97          ctxt.setAttribute("y", 22, ScriptContext.ENGINE_SCOPE);
98          final CompiledScript script2 = engine.compile("x + y");
99          Object result2 = script2.eval();
100         assertEquals(42, result2);
101         ctxt.setAttribute("x", -20, ScriptContext.ENGINE_SCOPE);
102         ctxt.setAttribute("y", -22, ScriptContext.ENGINE_SCOPE);
103         result2 = script2.eval();
104         assertEquals(-42, result2);
105     }
106 
107     @Test
108     public void testDirectNew() throws Exception {
109         final ScriptEngine engine = new JexlScriptEngine();
110         final Integer initialValue = 123;
111         assertEquals(initialValue,engine.eval("123"));
112     }
113 
114     @Test
115     public void testDottedNames() throws Exception {
116         final ScriptEngineManager manager = new ScriptEngineManager();
117         assertNotNull(manager, "Manager should not be null");
118         final ScriptEngine engine = manager.getEngineByName("JEXL");
119         assertNotNull(engine, "Engine should not be null (JEXL)");
120         engine.eval("this.is.a.test=null");
121         assertNull(engine.get("this.is.a.test"));
122         assertEquals(Boolean.TRUE, engine.eval("empty(this.is.a.test)"));
123         final Object mymap = engine.eval("testmap={ 'key1' : 'value1', 'key2' : 'value2' }");
124         assertTrue(mymap instanceof Map<?, ?>);
125         assertEquals(2,((Map<?, ?>)mymap).size());
126     }
127 
128     @Test
129     public void testErrors() throws Exception {
130         final ScriptEngineManager manager = new ScriptEngineManager();
131         final JexlScriptEngine engine = (JexlScriptEngine) manager.getEngineByName("JEXL");
132         final ScriptContext ctxt = engine.getContext();
133         engine.put("errors", new Errors());
134         assertTrue(assertThrows(ScriptException.class, () -> engine.eval("errors.npe()")).getCause() instanceof NullPointerException);
135         assertTrue(assertThrows(ScriptException.class, () -> engine.eval("errors.illegal()")).getCause() instanceof IllegalArgumentException);
136         final CompiledScript script0 = engine.compile("errors.npe()");
137         assertTrue(assertThrows(ScriptException.class, () -> script0.eval()).getCause() instanceof NullPointerException);
138         final CompiledScript script1 = engine.compile("errors.illegal()");
139         assertTrue(assertThrows(ScriptException.class, () -> script1.eval()).getCause() instanceof IllegalArgumentException);
140     }
141 
142     @Test
143     public void testNulls() throws Exception {
144         final ScriptEngineManager manager = new ScriptEngineManager();
145         assertNotNull(manager, "Manager should not be null");
146         final ScriptEngine engine = manager.getEngineByName("jexl3");
147         assertNotNull(engine, "Engine should not be null (name)");
148         assertNotNull(engine.getFactory());
149         assertThrows(NullPointerException.class, () -> engine.eval((String) null));
150         assertThrows(NullPointerException.class, () -> engine.eval((Reader) null));
151         final ScriptContext ctxt = null;
152         assertThrows(NullPointerException.class, () -> engine.eval((String) null, ctxt));
153         assertThrows(NullPointerException.class, () -> engine.eval((Reader) null, ctxt));
154     }
155 
156     @Test
157     public void testScopes() throws Exception {
158         final ScriptEngineManager manager = new ScriptEngineManager();
159         assertNotNull(manager, "Manager should not be null");
160         final ScriptEngine engine = manager.getEngineByName("jexl3");
161         assertNotNull(engine, "Engine should not be null (name)");
162         manager.put("global", 1);
163         engine.put("local", 10);
164         manager.put("both", 7);
165         engine.put("both", 7);
166         engine.eval("local=local+1");
167         engine.eval("global=global+1");
168         engine.eval("both=both+1"); // should update engine value only
169         engine.eval("newvar=42;");
170         assertEquals(2,manager.get("global"));
171         assertEquals(11,engine.get("local"));
172         assertEquals(7,manager.get("both"));
173         assertEquals(8,engine.get("both"));
174         assertEquals(42,engine.get("newvar"));
175         assertNull(manager.get("newvar"));
176     }
177 
178     @Test
179     public void testScriptEngineFactory() throws Exception {
180         final JexlScriptEngineFactory factory = new JexlScriptEngineFactory();
181         assertEquals("JEXL Engine", factory.getParameter(ScriptEngine.ENGINE));
182         assertEquals("3.4", factory.getParameter(ScriptEngine.ENGINE_VERSION));
183         assertEquals("JEXL", factory.getParameter(ScriptEngine.LANGUAGE));
184         assertEquals("3.4", factory.getParameter(ScriptEngine.LANGUAGE_VERSION));
185         assertNull(factory.getParameter("THREADING"));
186         assertEquals(NAMES, factory.getParameter(ScriptEngine.NAME));
187         assertEquals(EXTENSIONS, factory.getExtensions());
188         assertEquals(MIMES, factory.getMimeTypes());
189 
190         assertEquals("42;", factory.getProgram("42"));
191         assertEquals("str.substring(3,4)", factory.getMethodCallSyntax("str", "substring", "3", "4"));
192     }
193 
194     @Test
195     public void testScripting() throws Exception {
196         final ScriptEngineManager manager = new ScriptEngineManager();
197         assertNotNull(manager, "Manager should not be null");
198         final ScriptEngine engine = manager.getEngineByName("jexl3");
199         final Integer initialValue = 123;
200         assertEquals(initialValue, engine.eval("123"));
201         assertEquals(initialValue, engine.eval("0;123")); // multiple statements
202         final ScriptException xscript = assertThrows(ScriptException.class,
203                 () -> engine.eval("sys=context.class.forName(\"java.lang.System\");now=sys.currentTimeMillis();"));
204         final JexlException.Method xjexl = (JexlException.Method) xscript.getCause();
205         assertEquals("forName", xjexl.getMethod());
206         engine.put("value", initialValue);
207         assertEquals(initialValue, engine.get("value"));
208         final Integer newValue = 124;
209         assertEquals(newValue, engine.eval("old=value;value=value+1"));
210         assertEquals(initialValue, engine.get("old"));
211         assertEquals(newValue, engine.get("value"));
212         assertEquals(engine.getContext(), engine.get(JexlScriptEngine.CONTEXT_KEY));
213         // Check behavior of JEXL object
214         assertEquals(engine.getContext().getReader(), engine.eval("JEXL.in"));
215         assertEquals(engine.getContext().getWriter(), engine.eval("JEXL.out"));
216         assertEquals(engine.getContext().getErrorWriter(), engine.eval("JEXL.err"));
217         assertEquals(System.class, engine.eval("JEXL.System"));
218     }
219 
220     @Test
221     public void testScriptingGetBy() throws Exception {
222         final ScriptEngineManager manager = new ScriptEngineManager();
223         assertNotNull(manager, "Manager should not be null");
224         for (final String name : NAMES) {
225             final ScriptEngine engine = manager.getEngineByName(name);
226             assertNotNull(engine, "Engine should not be null (name)");
227         }
228         for (final String extension : EXTENSIONS) {
229             final ScriptEngine engine = manager.getEngineByExtension(extension);
230             assertNotNull(engine, "Engine should not be null (extension)");
231         }
232         for (final String mime : MIMES) {
233             final ScriptEngine engine = manager.getEngineByMimeType(mime);
234             assertNotNull(engine, "Engine should not be null (mime)");
235         }
236     }
237     @Test
238     public void testScriptingInstance0() throws Exception {
239         JexlScriptEngine.setPermissions(JexlPermissions.UNRESTRICTED);
240         final ScriptEngineManager manager = new ScriptEngineManager();
241         final ScriptEngine engine = manager.getEngineByName("jexl3");
242         final Long time2 = (Long) engine.eval(
243                 "sys=context.class.forName(\"java.lang.System\");"
244                         + "now=sys.currentTimeMillis();");
245         assertTrue(time2 <= System.currentTimeMillis());
246     }
247 
248     @Test
249     public void testScriptingPermissions1() throws Exception {
250         JexlBuilder.setDefaultPermissions(JexlPermissions.UNRESTRICTED);
251         JexlScriptEngine.setPermissions(null);
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 }