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