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 org.apache.commons.jexl3.internal.Engine;
20  import org.junit.Assert;
21  import org.junit.Test;
22  
23  /**
24   * Checks various exception handling cases.
25   */
26  @SuppressWarnings({"UnnecessaryBoxing", "AssertEqualsBetweenInconvertibleTypes"})
27  public class ExceptionTest extends JexlTestCase {
28      /** create a named test */
29      public ExceptionTest() {
30          super("ExceptionTest");
31      }
32  
33      public static class ThrowNPE {
34          boolean doThrow = false;
35          public String npe() {
36              throw new NullPointerException("ThrowNPE");
37          }
38  
39          public void setFail(final boolean f) {
40              doThrow = f;
41              if (f) {
42                  throw new NullPointerException("ThrowNPE/set");
43              }
44          }
45  
46          public boolean getFail() {
47              if (doThrow) {
48                  throw new NullPointerException("ThrowNPE/get");
49              }
50              return doThrow;
51          }
52      }
53  
54      @Test
55      public void testWrappedEx() throws Exception {
56          final JexlEngine jexl = new Engine();
57          final JexlExpression e = jexl.createExpression("npe()");
58          final JexlContext jc = new ObjectContext<>(jexl, new ThrowNPE());
59          try {
60              e.evaluate(jc);
61              Assert.fail("Should have thrown NPE");
62          } catch (final JexlException xany) {
63              final Throwable xth = xany.getCause();
64              Assert.assertEquals(NullPointerException.class, xth.getClass());
65          }
66      }
67  
68      @Test
69      public void testWrappedExmore() throws Exception {
70          final JexlEngine jexl = new Engine();
71          final ThrowNPE npe = new ThrowNPE();
72          try {
73              final Object r = jexl.getProperty(npe, "foo");
74              Assert.fail("Should have thrown JexlException.Property");
75          } catch (final JexlException.Property xany) {
76              final Throwable xth = xany.getCause();
77              Assert.assertNull(xth);
78          }
79          try {
80              jexl.setProperty(npe, "foo", 42);
81              Assert.fail("Should have thrown JexlException.Property");
82          } catch (final JexlException.Property xany) {
83              final Throwable xth = xany.getCause();
84              Assert.assertNull(xth);
85          }
86  
87          final boolean b = (Boolean) jexl.getProperty(npe, "fail");
88          Assert.assertFalse(b);
89          try {
90              jexl.setProperty(npe, "fail", false);
91              jexl.setProperty(npe, "fail", true);
92              Assert.fail("Should have thrown JexlException.Property");
93          } catch (final JexlException.Property xany) {
94              final Throwable xth = xany.getCause();
95              Assert.assertEquals(NullPointerException.class, xth.getClass());
96          }
97          try {
98              jexl.getProperty(npe, "fail");
99              Assert.fail("Should have thrown JexlException.Property");
100         } catch (final JexlException.Property xany) {
101             final Throwable xth = xany.getCause();
102             Assert.assertEquals(NullPointerException.class, xth.getClass());
103         }
104 
105         try {
106             jexl.invokeMethod(npe, "foo", 42);
107             Assert.fail("Should have thrown JexlException.Method");
108         } catch (final JexlException.Method xany) {
109             final Throwable xth = xany.getCause();
110             Assert.assertNull(xth);
111         }
112         try {
113             jexl.invokeMethod(npe, "npe");
114             Assert.fail("Should have thrown NullPointerException");
115         } catch (final JexlException.Method xany) {
116             final Throwable xth = xany.getCause();
117             Assert.assertEquals(NullPointerException.class, xth.getClass());
118         }
119     }
120 
121     // Unknown vars and properties versus null operands
122     // JEXL-73
123     @Test
124     public void testEx() throws Exception {
125         final JexlEngine jexl = createEngine(false);
126         final JexlExpression e = jexl.createExpression("c.e * 6");
127         final JexlEvalContext ctxt = new JexlEvalContext();
128         final JexlOptions options = ctxt.getEngineOptions();
129         // ensure errors will throw
130         options.setSilent(false);
131         // make unknown vars throw
132         options.setStrict(true);
133         // empty cotext
134         try {
135             /* Object o = */ e.evaluate(ctxt);
136             Assert.fail("c not defined as variable should throw");
137         } catch (final JexlException.Variable xjexl) {
138             final String msg = xjexl.getMessage();
139             Assert.assertTrue(msg.indexOf("variable 'c.e'") > 0);
140         }
141 
142         // disallow null operands
143         options.setStrictArithmetic(true);
144         ctxt.set("c.e", null);
145         try {
146             /* Object o = */ e.evaluate(ctxt);
147             Assert.fail("c.e as null operand should throw");
148         } catch (final JexlException.Variable xjexl) {
149             final String msg = xjexl.getMessage();
150             Assert.assertTrue(msg.indexOf("variable 'c.e'") > 0);
151         }
152 
153         // allow null operands
154         options.setStrictArithmetic(false);
155         try {
156             /* Object o = */ e.evaluate(ctxt);
157 
158         } catch (final JexlException xjexl) {
159             Assert.fail("c.e in expr should not throw");
160         }
161 
162         // ensure c.e is not a defined property
163         ctxt.set("c", "{ 'a' : 3, 'b' : 5}");
164         ctxt.set("e", Integer.valueOf(2));
165         try {
166             /* Object o = */ e.evaluate(ctxt);
167             Assert.fail("c.e not accessible as property should throw");
168         } catch (final JexlException.Property xjexl) {
169             final String msg = xjexl.getMessage();
170             Assert.assertTrue(msg.indexOf("property 'e") > 0);
171         }
172     }
173 
174     // null local vars and strict arithmetic effects
175     @Test
176     public void testExVar() throws Exception {
177         final JexlEngine jexl = createEngine(false);
178         final JexlScript e = jexl.createScript("(x)->{ x * 6 }");
179         final JexlEvalContext ctxt = new JexlEvalContext();
180         final JexlOptions options = ctxt.getEngineOptions();
181         // ensure errors will throw
182         options.setSilent(false);
183         // make unknown vars throw
184         options.setStrict(true);
185         options.setStrictArithmetic(true);
186         // empty cotext
187         try {
188             /* Object o = */ e.execute(ctxt);
189             Assert.fail("x is null, should throw");
190         } catch (final JexlException xjexl) {
191             final String msg = xjexl.getMessage();
192             Assert.assertTrue(msg.indexOf("null") > 0);
193         }
194 
195         // allow null operands
196         options.setStrictArithmetic(false);
197         try {
198             final Object o = e.execute(ctxt, (Object) null);
199         } catch (final JexlException.Variable xjexl) {
200             Assert.fail("arithmetic allows null operands, should not throw");
201         }
202     }
203 
204     // Unknown vars and properties versus null operands
205     @Test
206     public void testExMethod() throws Exception {
207         final JexlEngine jexl = createEngine(false);
208         final JexlExpression e = jexl.createExpression("c.e.foo()");
209         final JexlEvalContext ctxt = new JexlEvalContext();
210         final JexlOptions options = ctxt.getEngineOptions();
211         // ensure errors will throw
212         options.setSilent(false);
213         // make unknown vars throw
214         options.setStrict(true);
215         // empty cotext
216         try {
217             /* Object o = */ e.evaluate(ctxt);
218             Assert.fail("c not declared as variable should throw");
219         } catch (final JexlException.Variable xjexl) {
220             final String msg = xjexl.getMessage();
221             Assert.assertTrue(msg.indexOf("variable 'c.e'") > 0);
222         }
223 
224         // disallow null operands
225         options.setStrictArithmetic(true);
226         ctxt.set("c.e", null);
227         try {
228             /* Object o = */ e.evaluate(ctxt);
229             Assert.fail("c.e as null operand should throw");
230         } catch (final JexlException xjexl) {
231             final String msg = xjexl.getMessage();
232             Assert.assertTrue(msg.indexOf("variable 'c.e'") > 0);
233         }
234     }
235 
236     @Test
237     public void test206() throws Exception {
238         String src = "null.1 = 2; return 42";
239         doTest206(src, false, false);
240         doTest206(src, false, true);
241         doTest206(src, true, false);
242         doTest206(src, true, true);
243         src = "x = null.1; return 42";
244         doTest206(src, false, false);
245         doTest206(src, false, true);
246         doTest206(src, true, false);
247         doTest206(src, true, true);
248         src = "x = y.1; return 42";
249         doTest206(src, false, false);
250         doTest206(src, false, true);
251         doTest206(src, true, false);
252         doTest206(src, true, true);
253     }
254     private void doTest206(final String src, final boolean strict, final boolean silent) throws Exception {
255         final CaptureLog l = new CaptureLog();
256         final JexlContext jc = new MapContext();
257         final JexlEngine jexl = new JexlBuilder().logger(l).strict(strict).silent(silent).create();
258         JexlScript e;
259         Object r = -1;
260         e = jexl.createScript(src);
261         try {
262             r = e.execute(jc);
263             if (strict && !silent) {
264                 Assert.fail("should have thrown an exception");
265             }
266         } catch(final JexlException xjexl) {
267             if (!strict || silent) {
268                 Assert.fail(src + ": should not have thrown an exception");
269             }
270         }
271         if (strict) {
272             if (silent && l.count("warn") == 0) {
273                 Assert.fail(src + ": should have generated a warning");
274             }
275         } else {
276             if (l.count("debug") == 0) {
277                 Assert.fail(src + ": should have generated a debug");
278             }
279             Assert.assertEquals(42, r);
280         }
281     }
282 }