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  package org.apache.commons.jexl3;
18  
19  import java.math.BigDecimal;
20  import java.math.BigInteger;
21  import java.util.Collections;
22  import java.util.Map;
23  import java.util.Set;
24  import java.util.TreeMap;
25  import java.util.concurrent.ConcurrentHashMap;
26  import java.util.concurrent.ConcurrentMap;
27  import java.util.concurrent.atomic.AtomicInteger;
28  
29  import org.junit.Assert;
30  import org.junit.Test;
31  
32  /**
33   * Tests for pragmas
34   */
35  public class PragmaTest extends JexlTestCase {
36      /**
37       * Create a new test case.
38       */
39      public PragmaTest() {
40          super("PragmaTest");
41      }
42  
43      /**
44       * Test creating a script from a string.
45       */
46      @Test
47      @SuppressWarnings("AssertEqualsBetweenInconvertibleTypes")
48      public void testPragmas() {
49          final JexlScript script = JEXL.createScript("#pragma one 1\n#pragma the.very.hard 'truth'\n2;");
50          Assert.assertNotNull(script);
51          final Map<String, Object> pragmas = script.getPragmas();
52          Assert.assertEquals(2, pragmas.size());
53          Assert.assertEquals(1, pragmas.get("one"));
54          Assert.assertEquals("truth", pragmas.get("the.very.hard"));
55      }
56  
57      @Test
58      @SuppressWarnings("AssertEqualsBetweenInconvertibleTypes")
59      public void testJxltPragmas() {
60          final JxltEngine engine = new JexlBuilder().create().createJxltEngine();
61          final JxltEngine.Template tscript = engine.createTemplate("$$ #pragma one 1\n$$ #pragma the.very.hard 'truth'\n2;");
62          Assert.assertNotNull(tscript);
63          final Map<String, Object> pragmas = tscript.getPragmas();
64          Assert.assertEquals(2, pragmas.size());
65          Assert.assertEquals(1, pragmas.get("one"));
66          Assert.assertEquals("truth", pragmas.get("the.very.hard"));
67      }
68  
69      public static class SafeContext extends JexlEvalContext {
70          // @Override
71          public void processPragmas(final Map<String, Object> pragmas) {
72              if (pragmas != null && !pragmas.isEmpty()) {
73                  final JexlOptions options = getEngineOptions();
74                  for (final Map.Entry<String, Object> pragma : pragmas.entrySet()) {
75                      final String key = pragma.getKey();
76                      final Object value = pragma.getValue();
77                      if ("jexl.safe".equals(key) && value instanceof Boolean) {
78                          options.setSafe((Boolean) value);
79                      } else if ("jexl.strict".equals(key) && value instanceof Boolean) {
80                          options.setStrict((Boolean) value);
81                      } else if ("jexl.silent".equals(key) && value instanceof Boolean) {
82                          options.setSilent((Boolean) value);
83                      }
84                  }
85              }
86          }
87  
88          /**
89           * Sleeps, called through scripts.
90           * @param ms time to sleep in ms
91           */
92          public void sleep(final long ms) {
93              try {
94                  Thread.sleep(ms);
95              } catch (final InterruptedException e) {
96                  // ignore
97              }
98          }
99      }
100 
101     @Test
102     @SuppressWarnings("AssertEqualsBetweenInconvertibleTypes")
103     public void testSafePragma() {
104         SafeContext jc = new SafeContext();
105         jc.set("foo", null);
106         final JexlScript script = JEXL.createScript("#pragma jexl.safe true\nfoo.bar;");
107         Assert.assertNotNull(script);
108         jc.processPragmas(script.getPragmas());
109         Object result = script.execute(jc);
110         Assert.assertNull(result);
111         jc = new SafeContext();
112         jc.set("foo", null);
113         try {
114             script.execute(jc);
115             Assert.fail("should have thrown");
116         } catch (final JexlException xvar) {
117             // ok, expected
118         }
119     }
120 
121     public static class ModuleContext extends MapContext {
122         protected final Map<String, JexlScript> sources = new TreeMap<>();
123 
124         ModuleContext() {  }
125         public Object script(String name) {
126             return sources.get(name);
127         }
128 
129         void script(String name, JexlScript script) { sources.put(name, script); }
130     }
131 
132     public static class CachingModuleContext extends ModuleContext implements JexlContext.ModuleProcessor {
133         private final ConcurrentMap<String, Object> modules = new ConcurrentHashMap<>();
134         private final AtomicInteger count = new AtomicInteger(0);
135 
136         public int getCountCompute() {
137             return count.get();
138         }
139 
140         CachingModuleContext() {
141         }
142 
143         @Override
144         public Object processModule(JexlEngine engine, JexlInfo info, String name, final String body) {
145             if (body.isEmpty()) {
146                 modules.remove(name);
147                 return null;
148             }
149             return modules.computeIfAbsent(name, (n) -> {
150                 Object module = engine.createExpression(info, body).evaluate(this);
151                 if (module instanceof JexlScript) {
152                     module = ((JexlScript) module).execute(this);
153                 }
154                 count.incrementAndGet();
155                 return module;
156             });
157         }
158     }
159 
160     @Test public void testPragmaModuleNoCache() {
161         ModuleContext ctxt = new ModuleContext();
162         runPragmaModule(ctxt, null);
163     }
164     @Test public void testPragmaModuleCache() {
165         CachingModuleContext ctxt = new CachingModuleContext();
166         runPragmaModule(ctxt, ctxt);
167     }
168     void runPragmaModule(ModuleContext ctxt, CachingModuleContext cmCtxt) {
169         ctxt.script("module0", JEXL.createScript("function f42(x) { 42 + x; } function f43(x) { 43 + x; }; { 'f42' : f42, 'f43' : f43 }"));
170         ConcurrentMap<String, Object> modules = new ConcurrentHashMap<>();
171         JexlScript script ;
172         Object result ;
173         script = JEXL.createScript("#pragma jexl.module.m0 \"script('module0')\"\n m0:f42(10);");
174         result = script.execute(ctxt);
175         Assert.assertEquals(52, result);
176         if (cmCtxt != null) {
177             Assert.assertEquals(1, cmCtxt.getCountCompute());
178         }
179         result = script.execute(ctxt);
180         Assert.assertEquals(52, result);
181         if (cmCtxt != null) {
182             Assert.assertEquals(1, cmCtxt.getCountCompute());
183         }
184         script = JEXL.createScript("#pragma jexl.module.m0 \"script('module0')\"\n m0:f43(10);");
185         result = script.execute(ctxt);
186         Assert.assertEquals(53, result);
187         if (cmCtxt != null) {
188             Assert.assertEquals(1, cmCtxt.getCountCompute());
189         }
190         try {
191             script = JEXL.createScript("#pragma jexl.module.m0 ''\n#pragma jexl.module.m0 \"fubar('module0')\"\n m0:f43(10);");
192             result = script.execute(ctxt);
193             Assert.fail("fubar sshoud fail");
194         } catch(JexlException.Method xmethod) {
195             Assert.assertEquals("fubar", xmethod.getMethod());
196         }
197     }
198 
199 
200     public static class StaticSleeper {
201         // precludes instantiation
202         private StaticSleeper() {}
203 
204         public static void sleep(final long ms) {
205             try {
206                 Thread.sleep(ms);
207             } catch (final InterruptedException e) {
208                 // ignore
209             }
210         }
211     }
212 
213     public static class Sleeper {
214         public void sleep(final long ms) {
215             try {
216                 Thread.sleep(ms);
217             } catch (final InterruptedException e) {
218                 // ignore
219             }
220         }
221     }
222 
223     @Test
224     public void testImportPragmaValueSet() {
225         String src =
226                 "#pragma jexl.import java.util\n"+
227                 "#pragma jexl.import java.io\n"+
228                 "#pragma jexl.import java.net\n"+
229                 "42";
230         final JexlScript script = JEXL.createScript(src);
231         Map<String, Object> pragmas = script.getPragmas();
232         Object importz = pragmas.get("jexl.import");
233         Assert.assertTrue(importz instanceof Set<?>);
234         Set<String> importzz = (Set<String>) importz;
235         Assert.assertTrue(importzz.contains("java.util"));
236         Assert.assertTrue(importzz.contains("java.io"));
237         Assert.assertTrue(importzz.contains("java.net"));
238         Assert.assertEquals(3, importzz.size());
239         String parsed = script.getParsedText();
240         Assert.assertEquals(src, parsed);
241     }
242     @Test
243     public void testPragmaOptions1() {
244         final String str = "i; #pragma jexl.options '-strict'\n";
245         final JexlEngine jexl = new JexlBuilder()
246                 .features(new JexlFeatures().pragmaAnywhere(false))
247                 .strict(true).create();
248         final JexlContext ctxt = new MapContext();
249         try {
250             final JexlScript e = jexl.createScript(str);
251             Assert.fail("i should not be resolved");
252         } catch (final JexlException xany) {
253             Assert.assertNotNull(xany);
254         }
255     }
256     @Test
257     public void testImportPragmaDisabled() {
258         String src =
259                 "#pragma jexl.import java.util\n"+
260                         "#pragma jexl.import java.io\n"+
261                         "#pragma jexl.import java.net\n"+
262                         "42";
263         JexlFeatures features = new JexlFeatures();
264         features.importPragma(false);
265         final JexlEngine jexl = new JexlBuilder().features(features).create();
266         try {
267             final JexlScript script = jexl.createScript(src);
268         } catch(JexlException.Parsing xparse) {
269             Assert.assertTrue(xparse.getMessage().contains("import pragma"));
270         }
271     }
272     @Test
273     public void testNamespacePragmaDisabled() {
274         JexlFeatures features = new JexlFeatures();
275         features.namespacePragma(false);
276         final JexlEngine jexl = new JexlBuilder().features(features).create();
277         try {
278             final JexlScript src = jexl.createScript(
279                     "#pragma jexl.namespace.sleeper " + StaticSleeper.class.getName() + "\n"
280                             + "sleeper:sleep(100);"
281                             + "42");
282             Assert.fail("should have thrown syntax exception");
283         } catch(JexlException.Parsing xparse) {
284             Assert.assertTrue(xparse.getMessage().contains("namespace pragma"));
285         }
286     }
287     @Test
288     @SuppressWarnings("AssertEqualsBetweenInconvertibleTypes")
289     public void testStaticNamespacePragma() {
290         final JexlContext jc = new SafeContext();
291         final JexlScript script = JEXL.createScript(
292                 "#pragma jexl.namespace.sleeper " + StaticSleeper.class.getName() + "\n"
293                 + "sleeper:sleep(100);"
294                 + "42");
295         final Object result = script.execute(jc);
296         Assert.assertEquals(42, result);
297     }
298 
299     @Test
300     @SuppressWarnings("AssertEqualsBetweenInconvertibleTypes")
301     public void testStatictNamespacePragmaCtl() {
302         final Map<String, Object> ns = Collections.singletonMap("sleeper", StaticSleeper.class.getName());
303         final JexlEngine jexl = new JexlBuilder().namespaces(ns).create();
304         final JexlContext jc = new SafeContext();
305         final JexlScript script = jexl.createScript(
306                 "sleeper:sleep(100);"
307                 + "42");
308         final Object result = script.execute(jc);
309         Assert.assertEquals(42, result);
310     }
311 
312     @Test
313     @SuppressWarnings("AssertEqualsBetweenInconvertibleTypes")
314     public void testNamespacePragma() {
315         final JexlContext jc = new SafeContext();
316         final String src =
317                 "#pragma jexl.namespace.sleeper " + Sleeper.class.getName() + "\n"
318                         + "sleeper:sleep(100);\n"
319                         + "42;\n";
320         final JexlScript script = JEXL.createScript(src);
321         final Object result = script.execute(jc);
322         Assert.assertEquals(42, result);
323         String parsed = script.getParsedText();
324         Assert.assertEquals(src, parsed);
325     }
326 
327     @Test
328     @SuppressWarnings("AssertEqualsBetweenInconvertibleTypes")
329     public void testNamespacePragmaCtl() {
330         final Map<String, Object> ns = Collections.singletonMap("sleeper", Sleeper.class.getName());
331         final JexlEngine jexl = new JexlBuilder().namespaces(ns).create();
332         final JexlContext jc = new SafeContext();
333         final JexlScript script = jexl.createScript(
334                 "sleeper:sleep(100);"
335                 + "42");
336         final Object result = script.execute(jc);
337         Assert.assertEquals(42, result);
338     }
339 
340     @Test public void test354() {
341         Map<String, Number> values = new TreeMap<>();
342         values.put("1", 1);
343         values.put("+1", 1);
344         values.put("-1", -1);
345         values.put("1l", 1L);
346         values.put("+1l", 1L);
347         values.put("-1l", -1L);
348         values.put("10h", BigInteger.valueOf(10));
349         values.put("-11h", BigInteger.valueOf(-11));
350         values.put("+12h", BigInteger.valueOf(12));
351         values.put("0xa", 0xa);
352         values.put("+0xa", 0xa);
353         values.put("-0xa", -0xa);
354         values.put("0xacl", 0xacL);
355         values.put("+0xadl", 0xadL);
356         values.put("-0xafl", -0xafL);
357         values.put("1d", 1d);
358         values.put("-1d", -1d);
359         values.put("+1d", 1d);
360         values.put("1f", 1f);
361         values.put("-1f", -1f);
362         values.put("+1f", 1f);
363         values.put("1B", new BigDecimal(1));
364         values.put("-1B", new BigDecimal(-1));
365         values.put("+1B", new BigDecimal(1));
366         values.put("-42424242424242424242424242424242", new BigInteger("-42424242424242424242424242424242"));
367         values.put("+42424242424242424242424242424242", new BigInteger("+42424242424242424242424242424242"));
368         values.put("42424242424242424242424242424242", new BigInteger("42424242424242424242424242424242"));
369         JexlEngine jexl = new JexlBuilder().safe(true).create();
370         for(Map.Entry<String, Number> e : values.entrySet()) {
371             String text = "#pragma number " + e.getKey();
372             JexlScript script = jexl.createScript(text);
373             Assert.assertNotNull(script);
374             Map<String, Object> pragmas = script.getPragmas();
375             Assert.assertNotNull(pragmas);
376             Assert.assertEquals(e.getKey(), e.getValue(), pragmas.get("number"));
377         }
378     }
379 }