1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.jexl3;
18
19 import static org.junit.jupiter.api.Assertions.assertEquals;
20 import static org.junit.jupiter.api.Assertions.assertNotNull;
21 import static org.junit.jupiter.api.Assertions.assertNotSame;
22 import static org.junit.jupiter.api.Assertions.assertNull;
23 import static org.junit.jupiter.api.Assertions.assertSame;
24
25 import java.io.File;
26 import java.lang.ref.Reference;
27 import java.lang.ref.ReferenceQueue;
28 import java.lang.ref.SoftReference;
29 import java.lang.ref.WeakReference;
30 import java.util.ArrayList;
31 import java.util.HashMap;
32 import java.util.List;
33 import java.util.Map;
34
35 import org.apache.commons.lang3.SystemProperties;
36 import org.apache.commons.logging.Log;
37 import org.apache.commons.logging.LogFactory;
38 import org.junit.jupiter.api.AfterEach;
39 import org.junit.jupiter.api.BeforeEach;
40 import org.junit.jupiter.api.Test;
41
42
43
44
45 @SuppressWarnings({"UnnecessaryBoxing", "AssertEqualsBetweenInconvertibleTypes"})
46 class ClassCreatorTest extends JexlTestCase {
47 public static class BigObject {
48 @SuppressWarnings("unused")
49 private final byte[] space = new byte[MEGA];
50 private final int id;
51
52 public BigObject(final int id) {
53 this.id = id;
54 }
55
56 public int getId() {
57 return id;
58 }
59 }
60
61 static final class ClassReference extends WeakReference<Class<?>> {
62 ClassReference(final Class<?> clazz, final ReferenceQueue<Object> queue) {
63 super(clazz, queue);
64 }
65 }
66 public static class ContextualCtor {
67 int value = -1;
68
69 public ContextualCtor(final JexlContext ctxt) {
70 value = (Integer) ctxt.get("value");
71 }
72
73 public ContextualCtor(final JexlContext ctxt, final int v) {
74 value = (Integer) ctxt.get("value") + v;
75 }
76
77 public int getValue() {
78 return value;
79 }
80 }
81
82 static final class InstanceReference extends SoftReference<Object> {
83 InstanceReference(final Object obj, final ReferenceQueue<Object> queue) {
84 super(obj, queue);
85 }
86 }
87
88 public static class NsTest implements JexlContext.NamespaceFunctor {
89 private final String className;
90
91 public NsTest(final String cls) {
92 className = cls;
93 }
94 @Override
95 public Object createFunctor(final JexlContext context) {
96 final JexlEngine jexl = JexlEngine.getThreadEngine();
97 return jexl.newInstance(className, context);
98 }
99
100 }
101
102 public static class TwoCtors {
103 int value;
104
105 public TwoCtors(final int v) {
106 this.value = v;
107 }
108
109 public TwoCtors(final Number x) {
110 this.value = -x.intValue();
111 }
112
113 public int getValue() {
114 return value;
115 }
116 }
117
118 static final Log logger = LogFactory.getLog(JexlTestCase.class);
119
120 static final int LOOPS = 8;
121
122
123 static final int MEGA = 1024 * 1024;
124
125 private File base;
126
127 private JexlEngine jexl;
128
129 public ClassCreatorTest() {
130 super("ClassCreatorTest");
131 }
132
133 private void deleteDirectory(final File dir) {
134 if (dir.isDirectory()) {
135 for (final File file : dir.listFiles()) {
136 if (file.isFile()) {
137 file.delete();
138 }
139 }
140 }
141 dir.delete();
142 }
143
144 void functorTwo(final Object nstest) throws Exception {
145
146 final Map<String, Object> ns = new HashMap<>();
147 ns.put("test", nstest);
148 final JexlEngine jexl2 = new JexlBuilder().namespaces(ns).create();
149 final JexlContext ctxt = new MapContext();
150 ctxt.set("value", 1000);
151
152
153 final ClassCreator cctor = new ClassCreator(jexl, base);
154 cctor.setSeed(2);
155 cctor.setCtorBody("value = (Integer) ctxt.get(\"value\") + 10;");
156 Class<?> foo1 = cctor.createClass(true);
157 assertSame(foo1.getClassLoader(), cctor.getClassLoader());
158 assertEquals("foo2", foo1.getSimpleName());
159 Object result = cctor.newInstance(foo1, ctxt);
160 assertEquals(foo1, result.getClass());
161 jexl2.setClassLoader(cctor.getClassLoader());
162 cctor.clear();
163
164
165 final JexlScript script = jexl2.createScript("test:getValue()");
166 result = script.execute(ctxt, foo1.getName());
167 assertEquals(1010, result);
168
169
170 cctor.setSeed(2);
171 cctor.setCtorBody("value = (Integer) ctxt.get(\"value\") + 99;");
172 final Class<?> foo11 = cctor.createClass(true);
173 assertEquals("foo2", foo1.getSimpleName());
174 assertNotSame(foo11, foo1);
175 foo1 = foo11;
176 result = cctor .newInstance(foo1, ctxt);
177 assertEquals(foo1, result.getClass());
178
179 jexl2.setClassLoader(foo1.getClassLoader());
180 result = script.execute(ctxt, foo1.getName());
181
182 assertEquals(1099, result);
183 }
184
185 @BeforeEach
186 @Override
187 public void setUp() throws Exception {
188 base = new File(SystemProperties.getJavaIoTmpdir(), "jexl" + System.currentTimeMillis());
189 jexl = JEXL;
190
191 }
192
193 @AfterEach
194 @Override
195 public void tearDown() {
196 deleteDirectory(base);
197 }
198
199 @Test
200 void testBasicCtor() {
201 final JexlScript s = jexl.createScript("(c, v)->{ var ct2 = new(c, v); ct2.value; }");
202 Object r = s.execute(null, TwoCtors.class, 10);
203 assertEquals(10, r);
204 r = s.execute(null, TwoCtors.class, 5 + 5);
205 assertEquals(10, r);
206 r = s.execute(null, TwoCtors.class, 10d);
207 assertEquals(-10, r);
208 r = s.execute(null, TwoCtors.class, 100f);
209 assertEquals(-100, r);
210 }
211
212 @Test
213 void testContextualCtor() {
214 final MapContext ctxt = new MapContext();
215 ctxt.set("value", 42);
216 JexlScript s = jexl.createScript("(c)->{ new(c).value }");
217 Object r = s.execute(ctxt, ContextualCtor.class);
218 assertEquals(42, r);
219 s = jexl.createScript("(c, v)->{ new(c, v).value }");
220 r = s.execute(ctxt, ContextualCtor.class, 100);
221 assertEquals(142, r);
222 }
223
224 @Test
225 void testFunctor2Class() throws Exception {
226 functorTwo(new NsTest(ClassCreator.GEN_CLASS + "foo2"));
227 }
228
229 @Test
230 void testFunctor2Name() throws Exception {
231 functorTwo(ClassCreator.GEN_CLASS + "foo2");
232 }
233
234 @Test
235 void testFunctorOne() throws Exception {
236 final JexlContext ctxt = new MapContext();
237 ctxt.set("value", 1000);
238
239
240
241 final ClassCreator cctor = new ClassCreator(jexl, base);
242 cctor.setSeed(1);
243 cctor.setCtorBody("value = (Integer) ctxt.get(\"value\") + 10;");
244 Class<?> foo1 = cctor.createClass(true);
245 assertSame(foo1.getClassLoader(), cctor.getClassLoader());
246 assertEquals("foo1", foo1.getSimpleName());
247 Object result = cctor.newInstance(foo1, ctxt);
248 assertEquals(foo1, result.getClass());
249 jexl.setClassLoader(cctor.getClassLoader());
250 cctor.clear();
251
252
253 final JexlScript script = jexl.createScript("(c)->{ new(c).value; }");
254 result = script.execute(ctxt, foo1);
255 assertEquals(1010, result);
256 result = script.execute(ctxt, foo1.getName());
257 assertEquals(1010, result);
258
259
260 cctor.setSeed(1);
261 cctor.setCtorBody("value = (Integer) ctxt.get(\"value\") + 99;");
262 final Class<?> foo11 = cctor.createClass(true);
263 assertEquals("foo1", foo1.getSimpleName());
264 assertNotSame(foo11, foo1);
265 foo1 = foo11;
266 result = cctor.newInstance(foo1, ctxt);
267 assertEquals(foo1, result.getClass());
268
269 jexl.setClassLoader(foo1.getClassLoader());
270 result = script.execute(ctxt, foo1.getName());
271
272 assertEquals(1099, result);
273 result = script.execute(ctxt, foo1);
274 assertEquals(1099, result);
275 }
276
277 @Test
278 void testFunctorThree() throws Exception {
279 final JexlContext ctxt = new MapContext();
280 ctxt.set("value", 1000);
281
282 final ClassCreator cctor = new ClassCreator(jexl, base);
283 cctor.setSeed(2);
284 cctor.setCtorBody("value = (Integer) ctxt.get(\"value\") + 10;");
285 Class<?> foo1 = cctor.createClass(true);
286 assertSame(foo1.getClassLoader(), cctor.getClassLoader());
287 assertEquals("foo2", foo1.getSimpleName());
288 Object result = cctor.newInstance(foo1, ctxt);
289 assertEquals(foo1, result.getClass());
290 jexl.setClassLoader(cctor.getClassLoader());
291 cctor.clear();
292
293 final Map<String, Object> ns = new HashMap<>();
294 ns.put("test", foo1);
295 final JexlEngine jexl2 = new JexlBuilder().namespaces(ns).create();
296
297 final JexlScript script = jexl2.createScript("test:getValue()");
298 result = script.execute(ctxt, foo1.getName());
299 assertEquals(1010, result);
300
301 cctor.setSeed(2);
302 cctor.setCtorBody("value = (Integer) ctxt.get(\"value\") + 99;");
303 final Class<?> foo11 = cctor.createClass(true);
304 assertEquals("foo2", foo1.getSimpleName());
305 assertNotSame(foo11, foo1);
306 foo1 = foo11;
307 result = cctor.newInstance(foo1, ctxt);
308 assertEquals(foo1, result.getClass());
309
310 jexl2.setClassLoader(foo1.getClassLoader());
311 result = script.execute(ctxt, foo1.getName());
312
313 assertEquals(1099, result);
314 }
315
316 @Test
317 void testMany() throws Exception {
318
319 if (!ClassCreator.canRun) {
320 return;
321 }
322 int pass = 0;
323 int gced = -1;
324 final ReferenceQueue<Object> queue = new ReferenceQueue<>();
325 final List<Reference<?>> stuff = new ArrayList<>();
326
327
328 final JexlExpression expr = jexl.createExpression("foo.value");
329 final JexlExpression newx = jexl.createExpression("foo = new(clazz)");
330 final JexlEvalContext context = new JexlEvalContext();
331 final JexlOptions options = context.getEngineOptions();
332 options.setStrict(false);
333 options.setSilent(true);
334
335 final ClassCreator cctor = new ClassCreator(jexl, base);
336 for (int i = 0; i < LOOPS && gced < 0; ++i) {
337 cctor.setSeed(i);
338 Class<?> clazz;
339 if (pass == 0) {
340 clazz = cctor.createClass();
341 } else {
342 clazz = cctor.getClassInstance();
343 if (clazz == null) {
344 assertEquals(i, gced);
345 break;
346 }
347 }
348
349
350
351
352
353 context.set("clazz", cctor.getClassName());
354 context.set("foo", null);
355 Object z = newx.evaluate(context);
356 assertNull(z);
357
358 context.set("clazz", clazz);
359 z = newx.evaluate(context);
360 assertNotNull(z, clazz + ": class " + i + " could not be instantiated on pass " + pass);
361 assertEquals(Integer.valueOf(i), expr.evaluate(context));
362
363 jexl.setClassLoader(cctor.getClassLoader());
364 z = newx.evaluate(context);
365 assertEquals(z.getClass(), clazz);
366 assertEquals(Integer.valueOf(i), expr.evaluate(context));
367 cctor.clear();
368 jexl.setClassLoader(null);
369
370
371 if (pass == 0) {
372
373 stuff.add(new ClassReference(clazz, queue));
374
375 stuff.add(new InstanceReference(clazz.getConstructor().newInstance(), queue));
376
377
378
379 for (int b = 0; b < 1024 && Runtime.getRuntime().freeMemory() > MEGA; ++b) {
380 final BigObject big = new BigObject(b);
381 stuff.add(new InstanceReference(big, queue));
382 }
383
384 System.gc();
385
386 boolean qr = false;
387 while (queue.poll() != null) {
388 final Reference<?> ref = queue.remove(1);
389 if (ref instanceof ClassReference) {
390 gced = i;
391 qr = true;
392 }
393 }
394 if (qr) {
395
396 pass = 1;
397 i = 0;
398 }
399 }
400 }
401
402 if (gced < 0) {
403 logger.warn("unable to force GC");
404
405 }
406 }
407
408 @Test
409 void testOne() throws Exception {
410
411 if (!ClassCreator.canRun) {
412 logger.warn("unable to create classes");
413 return;
414 }
415 final ClassCreator cctor = new ClassCreator(jexl, base);
416 cctor.setSeed(1);
417 final Class<?> foo1 = cctor.createClass();
418 assertEquals("foo1", foo1.getSimpleName());
419 cctor.clear();
420 }
421 @Test
422 void test432() throws Exception {
423 final ClassCreator cctor = new ClassCreator(jexl, base);
424 cctor.setSeed(2);
425 cctor.setCtorBody("value = (Integer) ctxt.get(\"value\") + 10;");
426 Class<?> foo1 = cctor.createClass(true);
427 assertSame(foo1.getClassLoader(), cctor.getClassLoader());
428 assertEquals("foo2", foo1.getSimpleName());
429 final Map<String, Object> ns = new HashMap<>();
430 ns.put("test", foo1.getName());
431
432 final JexlEngine jexl2 = new JexlBuilder().namespaces(ns).cache(16).create();
433 jexl2.setClassLoader(cctor.getClassLoader());
434 cctor.clear();
435 final JexlContext ctxt = new MapContext();
436 ctxt.set("value", 1000);
437 final JexlScript script = jexl2.createScript("test:getValue()");
438 Object result = script.execute(ctxt);
439 assertEquals(1010, result); cctor.setSeed(2);
440 cctor.setCtorBody("value = (Integer) ctxt.get(\"value\") + 99;");
441 final Class<?> foo11 = cctor.createClass(true);
442 assertEquals("foo2", foo1.getSimpleName());
443 assertNotSame(foo11, foo1);
444 foo1 = foo11;
445
446 jexl2.setClassLoader(foo1.getClassLoader());
447 result = script.execute(ctxt);
448
449 assertEquals(1099, result);
450 }
451 }