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.reflect.Constructor;
20 import java.lang.reflect.Field;
21 import java.lang.reflect.Method;
22 import java.util.ArrayList;
23 import java.util.HashMap;
24 import java.util.Iterator;
25 import java.util.List;
26 import java.util.Map;
27 import java.util.concurrent.locks.ReadWriteLock;
28 import java.util.concurrent.locks.ReentrantReadWriteLock;
29
30 import org.apache.commons.jexl3.introspection.JexlPermissions;
31 import org.apache.commons.logging.Log;
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46 public final class Introspector {
47
48
49
50 private static final class CacheMiss {
51
52 @SuppressWarnings("unused")
53 public CacheMiss() {
54 }
55 }
56
57
58
59 private static final Constructor<?> CTOR_MISS = CacheMiss.class.getConstructors()[0];
60
61
62
63
64
65
66 private static boolean isLoadedBy(final ClassLoader loader, final Class<?> clazz) {
67 if (loader != null) {
68 ClassLoader cloader = clazz.getClassLoader();
69 while (cloader != null) {
70 if (cloader.equals(loader)) {
71 return true;
72 }
73 cloader = cloader.getParent();
74 }
75 }
76 return false;
77 }
78
79
80
81 private final Log logger;
82
83
84
85 private ClassLoader loader;
86
87
88
89 private final JexlPermissions permissions;
90
91
92
93 private final ReadWriteLock lock = new ReentrantReadWriteLock();
94
95
96
97 private final Map<Class<?>, ClassMap> classMethodMaps = new HashMap<>();
98
99
100
101 private final Map<MethodKey, Constructor<?>> constructorsMap = new HashMap<>();
102
103
104
105
106 private final Map<String, Class<?>> constructibleClasses = new HashMap<>();
107
108
109
110
111
112
113 public Introspector(final Log log, final ClassLoader cloader) {
114 this(log, cloader, null);
115 }
116
117
118
119
120
121
122
123 public Introspector(final Log log, final ClassLoader cloader, final JexlPermissions perms) {
124 this.logger = log;
125 this.loader = cloader;
126 this.permissions = perms == null ? JexlPermissions.RESTRICTED : perms;
127 }
128
129
130
131
132
133
134 public Class<?> getClassByName(final String className) {
135 try {
136 final Class<?> clazz = Class.forName(className, false, loader);
137 return permissions.allow(clazz)? clazz : null;
138 } catch (final ClassNotFoundException xignore) {
139 return null;
140 }
141 }
142
143
144
145
146
147
148
149
150 public Constructor<?> getConstructor(final Class<?> c, final MethodKey key) {
151 Constructor<?> ctor;
152 lock.readLock().lock();
153 try {
154 ctor = constructorsMap.get(key);
155 if (ctor != null) {
156
157 return CTOR_MISS.equals(ctor) ? null : ctor;
158 }
159 } finally {
160 lock.readLock().unlock();
161 }
162
163 lock.writeLock().lock();
164 try {
165
166 ctor = constructorsMap.get(key);
167 if (ctor != null) {
168
169 return CTOR_MISS.equals(ctor) ? null : ctor;
170 }
171 final String constructorName = key.getMethod();
172
173 Class<?> clazz = constructibleClasses.get(constructorName);
174 try {
175
176 if (clazz == null) {
177 if (c != null && c.getName().equals(key.getMethod())) {
178 clazz = c;
179 } else {
180 clazz = loader.loadClass(constructorName);
181 }
182
183 constructibleClasses.put(constructorName, clazz);
184 }
185 final List<Constructor<?>> l = new ArrayList<>();
186 for (final Constructor<?> ictor : clazz.getConstructors()) {
187 if (permissions.allow(ictor)) {
188 l.add(ictor);
189 }
190 }
191
192 ctor = key.getMostSpecificConstructor(l.toArray(new Constructor<?>[0]));
193 if (ctor != null) {
194 constructorsMap.put(key, ctor);
195 } else {
196 constructorsMap.put(key, CTOR_MISS);
197 }
198 } catch (final ClassNotFoundException xnotfound) {
199 if (logger != null && logger.isDebugEnabled()) {
200 logger.debug("unable to find class: "
201 + constructorName + "."
202 + key.debugString(), xnotfound);
203 }
204 } catch (final MethodKey.AmbiguousException xambiguous) {
205 if (logger != null && xambiguous.isSevere() && logger.isInfoEnabled()) {
206 logger.info("ambiguous constructor invocation: "
207 + constructorName + "."
208 + key.debugString(), xambiguous);
209 }
210 ctor = null;
211 }
212 return ctor;
213 } finally {
214 lock.writeLock().unlock();
215 }
216 }
217
218
219
220
221
222
223
224
225 public Constructor<?> getConstructor(final MethodKey key) {
226 return getConstructor(null, key);
227 }
228
229
230
231
232
233
234
235
236 public Field getField(final Class<?> c, final String key) {
237 return getMap(c).getField(key);
238 }
239
240
241
242
243
244
245 public String[] getFieldNames(final Class<?> c) {
246 if (c == null) {
247 return new String[0];
248 }
249 final ClassMap classMap = getMap(c);
250 return classMap.getFieldNames();
251 }
252
253
254
255
256
257 public ClassLoader getLoader() {
258 return loader;
259 }
260
261
262
263
264
265
266 private ClassMap getMap(final Class<?> c) {
267 ClassMap classMap;
268 lock.readLock().lock();
269 try {
270 classMap = classMethodMaps.get(c);
271 } finally {
272 lock.readLock().unlock();
273 }
274 if (classMap == null) {
275 lock.writeLock().lock();
276 try {
277
278 classMap = classMethodMaps.get(c);
279 if (classMap == null) {
280 classMap = permissions.allow(c)
281 ? new ClassMap(c, permissions, logger)
282 : ClassMap.empty();
283 classMethodMaps.put(c, classMap);
284 }
285 } finally {
286 lock.writeLock().unlock();
287 }
288
289 }
290 return classMap;
291 }
292
293
294
295
296
297
298
299
300
301 public Method getMethod(final Class<?> c, final MethodKey key) {
302 try {
303 return getMap(c).getMethod(key);
304 } catch (final MethodKey.AmbiguousException xambiguous) {
305
306 if (logger != null && xambiguous.isSevere() && logger.isInfoEnabled()) {
307 logger.info("ambiguous method invocation: "
308 + c.getName() + "."
309 + key.debugString(), xambiguous);
310 }
311 return null;
312 }
313 }
314
315
316
317
318
319
320
321
322
323 public Method getMethod(final Class<?> c, final String name, final Object... params) {
324 return getMethod(c, new MethodKey(name, params));
325 }
326
327
328
329
330
331
332 public String[] getMethodNames(final Class<?> c) {
333 if (c == null) {
334 return new String[0];
335 }
336 final ClassMap classMap = getMap(c);
337 return classMap.getMethodNames();
338 }
339
340
341
342
343
344
345
346 public Method[] getMethods(final Class<?> c, final String methodName) {
347 if (c == null) {
348 return null;
349 }
350 final ClassMap classMap = getMap(c);
351 return classMap.getMethods(methodName);
352 }
353
354
355
356
357
358
359 public void setLoader(final ClassLoader classLoader) {
360 final ClassLoader previous = loader;
361 final ClassLoader current = classLoader == null ? getClass().getClassLoader() : classLoader;
362 if (!current.equals(loader)) {
363 lock.writeLock().lock();
364 try {
365
366 final Iterator<Map.Entry<MethodKey, Constructor<?>>> mentries = constructorsMap.entrySet().iterator();
367 while (mentries.hasNext()) {
368 final Map.Entry<MethodKey, Constructor<?>> entry = mentries.next();
369 final Class<?> clazz = entry.getValue().getDeclaringClass();
370 if (isLoadedBy(previous, clazz)) {
371 mentries.remove();
372
373 constructibleClasses.remove(entry.getKey().getMethod());
374 }
375 }
376
377 final Iterator<Map.Entry<Class<?>, ClassMap>> centries = classMethodMaps.entrySet().iterator();
378 while (centries.hasNext()) {
379 final Map.Entry<Class<?>, ClassMap> entry = centries.next();
380 final Class<?> clazz = entry.getKey();
381 if (isLoadedBy(previous, clazz)) {
382 centries.remove();
383 }
384 }
385 loader = current;
386 } finally {
387 lock.writeLock().unlock();
388 }
389 }
390 }
391 }