1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.jexl3.internal.introspection;
18
19 import java.lang.ref.Reference;
20 import java.lang.ref.SoftReference;
21 import java.lang.reflect.Field;
22 import java.lang.reflect.Method;
23 import java.util.Collections;
24 import java.util.EnumSet;
25 import java.util.Enumeration;
26 import java.util.Iterator;
27 import java.util.List;
28 import java.util.Map;
29 import java.util.Set;
30 import java.util.concurrent.ConcurrentHashMap;
31 import java.util.concurrent.atomic.AtomicInteger;
32
33 import org.apache.commons.jexl3.JexlArithmetic;
34 import org.apache.commons.jexl3.JexlEngine;
35 import org.apache.commons.jexl3.JexlOperator;
36 import org.apache.commons.jexl3.internal.IntegerRange;
37 import org.apache.commons.jexl3.internal.LongRange;
38 import org.apache.commons.jexl3.internal.Operator;
39 import org.apache.commons.jexl3.introspection.JexlMethod;
40 import org.apache.commons.jexl3.introspection.JexlPermissions;
41 import org.apache.commons.jexl3.introspection.JexlPropertyGet;
42 import org.apache.commons.jexl3.introspection.JexlPropertySet;
43 import org.apache.commons.jexl3.introspection.JexlUberspect;
44 import org.apache.commons.logging.Log;
45 import org.apache.commons.logging.LogFactory;
46
47
48
49
50
51
52
53
54
55 public class Uberspect implements JexlUberspect {
56
57
58 public static final Object TRY_FAILED = JexlEngine.TRY_FAILED;
59
60
61 protected final Log logger;
62
63
64 private final JexlUberspect.ResolverStrategy strategy;
65
66
67 private final JexlPermissions permissions;
68
69
70 private final AtomicInteger version;
71
72
73 private volatile Reference<Introspector> ref;
74
75
76 private volatile Reference<ClassLoader> loader;
77
78
79
80
81
82
83
84 private final Map<Class<? extends JexlArithmetic>, Set<JexlOperator>> operatorMap;
85
86
87
88
89
90
91
92 public Uberspect(final Log runtimeLogger, final JexlUberspect.ResolverStrategy sty) {
93 this(runtimeLogger, sty, null);
94 }
95
96
97
98
99
100
101
102
103 public Uberspect(final Log runtimeLogger, final JexlUberspect.ResolverStrategy sty, final JexlPermissions perms) {
104 final ClassLoader cl = getClass().getClassLoader();
105 logger = runtimeLogger == null ? LogFactory.getLog(JexlEngine.class) : runtimeLogger;
106 strategy = sty == null ? JexlUberspect.JEXL_STRATEGY : sty;
107 permissions = perms == null ? JexlPermissions.RESTRICTED : perms;
108 ref = new SoftReference<>(null);
109 loader = new SoftReference<>(cl);
110 operatorMap = new ConcurrentHashMap<>();
111 version = new AtomicInteger();
112 }
113
114
115
116
117
118
119
120
121 protected final Introspector base() {
122 Introspector intro = ref.get();
123 if (intro == null) {
124
125 synchronized (this) {
126 intro = ref.get();
127 if (intro == null) {
128 intro = new Introspector(logger, loader.get(), permissions);
129 ref = new SoftReference<>(intro);
130 loader = new SoftReference<>(intro.getLoader());
131 version.incrementAndGet();
132 }
133 }
134 }
135 return intro;
136 }
137
138
139
140
141
142
143
144
145 Set<JexlOperator> getOverloads(final JexlArithmetic arithmetic) {
146 final Class<? extends JexlArithmetic> aclass = arithmetic.getClass();
147 return operatorMap.computeIfAbsent(aclass, k -> {
148 final Set<JexlOperator> newOps = EnumSet.noneOf(JexlOperator.class);
149
150 if (!JexlArithmetic.class.equals(aclass)) {
151 for (final JexlOperator op : JexlOperator.values()) {
152 final Method[] methods = getMethods(arithmetic.getClass(), op.getMethodName());
153 if (methods != null) {
154 for (final Method method : methods) {
155 final Class<?>[] parms = method.getParameterTypes();
156 if (parms.length != op.getArity()) {
157 continue;
158 }
159
160
161
162 if (!JexlArithmetic.class.equals(method.getDeclaringClass())) {
163 try {
164 JexlArithmetic.class.getMethod(method.getName(), method.getParameterTypes());
165 } catch (final NoSuchMethodException xmethod) {
166
167 newOps.add(op);
168 }
169 }
170 }
171 }
172 }
173 }
174 return newOps;
175 });
176 }
177
178 @Override
179 public JexlArithmetic.Uberspect getArithmetic(final JexlArithmetic arithmetic) {
180 final Set<JexlOperator> operators = arithmetic == null ? Collections.emptySet() : getOverloads(arithmetic);
181 return operators.isEmpty()? null : new Operator(this, arithmetic, operators);
182 }
183
184 @Override
185 public Operator getOperator(final JexlArithmetic arithmetic) {
186 final Set<JexlOperator> operators = arithmetic == null ? Collections.emptySet() : getOverloads(arithmetic);
187 return new Operator(this, arithmetic, operators);
188 }
189
190
191
192
193
194
195
196 @Override
197 public final Class<?> getClassByName(final String className) {
198 return base().getClassByName(className);
199 }
200
201 @Override
202 public ClassLoader getClassLoader() {
203 synchronized (this) {
204 return loader.get();
205 }
206 }
207
208 @Override
209 public JexlMethod getConstructor(final Object ctorHandle, final Object... args) {
210 return ConstructorMethod.discover(base(), ctorHandle, args);
211 }
212
213
214
215
216
217
218
219
220
221
222 public final Field getField(final Class<?> c, final String key) {
223 return base().getField(c, key);
224 }
225
226
227
228
229
230
231
232 public final String[] getFieldNames(final Class<?> c) {
233 return base().getFieldNames(c);
234 }
235
236 @Override
237 @SuppressWarnings("unchecked")
238 public Iterator<?> getIterator(final Object obj) {
239
240
241 if (!(obj instanceof IntegerRange) && !(obj instanceof LongRange)
242 && !obj.getClass().isArray() && !permissions.allow(obj.getClass())) {
243 return null;
244 }
245 if (obj instanceof Iterator<?>) {
246 return (Iterator<?>) obj;
247 }
248 if (obj.getClass().isArray()) {
249 return new ArrayIterator(obj);
250 }
251 if (obj instanceof Map<?, ?>) {
252 return ((Map<?, ?>) obj).values().iterator();
253 }
254 if (obj instanceof Enumeration<?>) {
255 return new EnumerationIterator<>((Enumeration<Object>) obj);
256 }
257 if (obj instanceof Iterable<?>) {
258 return ((Iterable<?>) obj).iterator();
259 }
260 try {
261
262
263
264 final JexlMethod it = getMethod(obj, "iterator", (Object[]) null);
265 if (it != null && Iterator.class.isAssignableFrom(it.getReturnType())) {
266 return (Iterator<Object>) it.invoke(obj, (Object[]) null);
267 }
268 } catch (final Exception xany) {
269 if (logger != null && logger.isDebugEnabled()) {
270 logger.info("unable to solve iterator()", xany);
271 }
272 }
273 return null;
274 }
275
276
277
278
279
280
281
282
283
284
285
286 public final Method getMethod(final Class<?> c, final MethodKey key) {
287 return base().getMethod(c, key);
288 }
289
290
291
292
293
294
295
296
297
298
299
300
301
302 public final Method getMethod(final Class<?> c, final String name, final Object[] params) {
303 return base().getMethod(c, new MethodKey(name, params));
304 }
305
306 @Override
307 public JexlMethod getMethod(final Object obj, final String method, final Object... args) {
308 return MethodExecutor.discover(base(), obj, method, args);
309 }
310
311
312
313
314
315
316
317 public final String[] getMethodNames(final Class<?> c) {
318 return base().getMethodNames(c);
319 }
320
321
322
323
324
325
326
327
328 public final Method[] getMethods(final Class<?> c, final String methodName) {
329 return base().getMethods(c, methodName);
330 }
331
332 @Override
333 public JexlPropertyGet getPropertyGet(
334 final List<PropertyResolver> resolvers, final Object obj, final Object identifier
335 ) {
336 final Class<?> clazz = obj.getClass();
337 final String property = AbstractExecutor.castString(identifier);
338 final Introspector is = base();
339 final List<PropertyResolver> r = resolvers == null ? strategy.apply(null, obj) : resolvers;
340 JexlPropertyGet executor = null;
341 for (final PropertyResolver resolver : r) {
342 if (resolver instanceof JexlResolver) {
343 switch ((JexlResolver) resolver) {
344 case PROPERTY:
345
346 executor = PropertyGetExecutor.discover(is, clazz, property);
347 if (executor == null) {
348 executor = BooleanGetExecutor.discover(is, clazz, property);
349 }
350 break;
351 case MAP:
352
353 executor = MapGetExecutor.discover(is, clazz, identifier);
354 break;
355 case LIST:
356
357 final Integer index = AbstractExecutor.castInteger(identifier);
358 if (index != null) {
359 executor = ListGetExecutor.discover(is, clazz, index);
360 }
361 break;
362 case DUCK:
363
364 executor = DuckGetExecutor.discover(is, clazz, identifier);
365 if (executor == null && property != null && property != identifier) {
366
367 executor = DuckGetExecutor.discover(is, clazz, property);
368 }
369 break;
370 case FIELD:
371
372 executor = FieldGetExecutor.discover(is, clazz, property);
373
374 if (obj instanceof Class<?>) {
375 executor = FieldGetExecutor.discover(is, (Class<?>) obj, property);
376 }
377 break;
378 case CONTAINER:
379
380 executor = IndexedType.discover(is, obj, property);
381 break;
382 default:
383 continue;
384 }
385 } else {
386 executor = resolver.getPropertyGet(this, obj, identifier);
387 }
388 if (executor != null) {
389 return executor;
390 }
391 }
392 return null;
393 }
394
395 @Override
396 public JexlPropertyGet getPropertyGet(final Object obj, final Object identifier) {
397 return getPropertyGet(null, obj, identifier);
398 }
399
400 @Override
401 public JexlPropertySet getPropertySet(
402 final List<PropertyResolver> resolvers, final Object obj, final Object identifier, final Object arg
403 ) {
404 final Class<?> clazz = obj.getClass();
405 final String property = AbstractExecutor.castString(identifier);
406 final Introspector is = base();
407 final List<PropertyResolver> actual = resolvers == null ? strategy.apply(null, obj) : resolvers;
408 JexlPropertySet executor = null;
409 for (final PropertyResolver resolver : actual) {
410 if (resolver instanceof JexlResolver) {
411 switch ((JexlResolver) resolver) {
412 case PROPERTY:
413
414 executor = PropertySetExecutor.discover(is, clazz, property, arg);
415 break;
416 case MAP:
417
418 executor = MapSetExecutor.discover(is, clazz, identifier, arg);
419 break;
420 case LIST:
421
422
423 final Integer index = AbstractExecutor.castInteger(identifier);
424 if (index != null) {
425 executor = ListSetExecutor.discover(is, clazz, identifier, arg);
426 }
427 break;
428 case DUCK:
429
430 executor = DuckSetExecutor.discover(is, clazz, identifier, arg);
431 if (executor == null && property != null && property != identifier) {
432 executor = DuckSetExecutor.discover(is, clazz, property, arg);
433 }
434 break;
435 case FIELD:
436
437 executor = FieldSetExecutor.discover(is, clazz, property, arg);
438 break;
439 case CONTAINER:
440 default:
441 continue;
442 }
443 } else {
444 executor = resolver.getPropertySet(this, obj, identifier, arg);
445 }
446 if (executor != null) {
447 return executor;
448 }
449 }
450 return null;
451 }
452
453 @Override
454 public JexlPropertySet getPropertySet(final Object obj, final Object identifier, final Object arg) {
455 return getPropertySet(null, obj, identifier, arg);
456 }
457
458 @Override
459 public List<PropertyResolver> getResolvers(final JexlOperator op, final Object obj) {
460 return strategy.apply(op, obj);
461 }
462
463 @Override
464 public int getVersion() {
465 return version.intValue();
466 }
467
468 @Override
469 public void setClassLoader(final ClassLoader loader) {
470 final ClassLoader classLoader = loader == null ? JexlUberspect.class.getClassLoader() : loader;
471 synchronized (this) {
472 Introspector intro = ref.get();
473 if (intro != null) {
474 intro.setLoader(classLoader);
475 } else {
476 intro = new Introspector(logger, classLoader, permissions);
477 ref = new SoftReference<>(intro);
478 }
479 this.loader = new SoftReference<>(intro.getLoader());
480 operatorMap.clear();
481 version.incrementAndGet();
482 }
483 }
484 }