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 * https://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
18 package org.apache.commons.beanutils2;
19
20 import java.lang.ref.Reference;
21 import java.lang.ref.WeakReference;
22 import java.lang.reflect.InvocationTargetException;
23 import java.lang.reflect.Method;
24 import java.lang.reflect.Modifier;
25 import java.util.Arrays;
26 import java.util.Collections;
27 import java.util.Map;
28 import java.util.Objects;
29 import java.util.WeakHashMap;
30
31 import org.apache.commons.lang3.SystemProperties;
32 import org.apache.commons.logging.Log;
33 import org.apache.commons.logging.LogFactory;
34
35 /**
36 * <p>
37 * Utility reflection methods focused on methods in general rather than properties in particular.
38 * </p>
39 *
40 * <h2>Known Limitations</h2>
41 * <h3>Accessing Public Methods In A Default Access Superclass</h3>
42 * <p>
43 * There is an issue when invoking public methods contained in a default access superclass. Reflection locates these methods fine and correctly assigns them as
44 * public. However, an {@code IllegalAccessException} is thrown if the method is invoked.
45 * </p>
46 *
47 * <p>
48 * {@code MethodUtils} contains a workaround for this situation. It will attempt to call {@code setAccessible} on this method. If this call succeeds, then the
49 * method can be invoked as normal. This call will only succeed when the application has sufficient security privileges. If this call fails then a warning will
50 * be logged and the method may fail.
51 * </p>
52 */
53 public final class MethodUtils {
54
55 /**
56 * Represents the key to looking up a Method by reflection.
57 */
58 private static final class MethodDescriptor {
59 private final Class<?> cls;
60 private final String methodName;
61 private final Class<?>[] paramTypes;
62 private final boolean exact;
63 private final int hashCode;
64
65 /**
66 * The sole constructor.
67 *
68 * @param cls the class to reflect, must not be null
69 * @param methodName the method name to obtain
70 * @param paramTypes the array of classes representing the parameter types
71 * @param exact whether the match has to be exact.
72 */
73 public MethodDescriptor(final Class<?> cls, final String methodName, final Class<?>[] paramTypes, final boolean exact) {
74 this.cls = Objects.requireNonNull(cls, "cls");
75 this.methodName = Objects.requireNonNull(methodName, "methodName");
76 this.paramTypes = paramTypes != null ? paramTypes : BeanUtils.EMPTY_CLASS_ARRAY;
77 this.exact = exact;
78 this.hashCode = methodName.length();
79 }
80
81 /**
82 * Checks for equality.
83 *
84 * @param obj object to be tested for equality
85 * @return true, if the object describes the same Method.
86 */
87 @Override
88 public boolean equals(final Object obj) {
89 if (!(obj instanceof MethodDescriptor)) {
90 return false;
91 }
92 final MethodDescriptor md = (MethodDescriptor) obj;
93
94 return exact == md.exact && methodName.equals(md.methodName) && cls.equals(md.cls) && Arrays.equals(paramTypes, md.paramTypes);
95 }
96
97 /**
98 * Returns the string length of method name. I.e. if the hash codes are different, the objects are different. If the hash codes are the same, need to
99 * use the equals method to determine equality.
100 *
101 * @return the string length of method name.
102 */
103 @Override
104 public int hashCode() {
105 return hashCode;
106 }
107 }
108
109 private static final Log LOG = LogFactory.getLog(MethodUtils.class);
110
111 /**
112 * Only log warning about accessibility work around once.
113 * <p>
114 * Note that this is broken when this class is deployed via a shared classloader in a container, as the warning message will be emitted only once, not once
115 * per webapp. However making the warning appear once per webapp means having a map keyed by context classloader which introduces nasty memory-leak
116 * problems. As this warning is really optional we can ignore this problem; only one of the webapps will get the warning in its logs but that should be good
117 * enough.
118 */
119 private static boolean loggedAccessibleWarning;
120
121 /**
122 * Indicates whether methods should be cached for improved performance.
123 * <p>
124 * Note that when this class is deployed via a shared classloader in a container, this will affect all webapps. However making this configurable per webapp
125 * would mean having a map keyed by context classloader which may introduce memory-leak problems.
126 */
127 private static boolean CACHE_METHODS = true;
128
129 /**
130 * Stores a cache of MethodDescriptor -> Method in a WeakHashMap.
131 * <p>
132 * The keys into this map only ever exist as temporary variables within methods of this class, and are never exposed to users of this class. This means that
133 * the WeakHashMap is used only as a mechanism for limiting the size of the cache, that is, a way to tell the garbage collector that the contents of the
134 * cache can be completely garbage-collected whenever it needs the memory. Whether this is a good approach to this problem is doubtful; something like the
135 * commons-collections LRUMap may be more appropriate (though of course selecting an appropriate size is an issue).
136 * <p>
137 * This static variable is safe even when this code is deployed via a shared classloader because it is keyed via a MethodDescriptor object which has a Class
138 * as one of its members and that member is used in the MethodDescriptor.equals method. So two components that load the same class via different class
139 * loaders will generate non-equal MethodDescriptor objects and hence end up with different entries in the map.
140 */
141 private static final Map<MethodDescriptor, Reference<Method>> cache = Collections.synchronizedMap(new WeakHashMap<>());
142
143 /**
144 * Add a method to the cache.
145 *
146 * @param md The method descriptor
147 * @param method The method to cache
148 */
149 private static void cacheMethod(final MethodDescriptor md, final Method method) {
150 if (CACHE_METHODS && method != null) {
151 cache.put(md, new WeakReference<>(method));
152 }
153 }
154
155 /**
156 * Clear the method cache.
157 *
158 * @return the number of cached methods cleared
159 * @since 1.8.0
160 */
161 public static synchronized int clearCache() {
162 final int size = cache.size();
163 cache.clear();
164 return size;
165 }
166
167 /**
168 * <p>
169 * Return an accessible method (that is, one that can be invoked via reflection) that implements the specified Method. If no such method can be found,
170 * return {@code null}.
171 * </p>
172 *
173 * @param clazz The class of the object
174 * @param method The method that we wish to call
175 * @return The accessible method
176 * @since 1.8.0
177 */
178 public static Method getAccessibleMethod(Class<?> clazz, Method method) {
179 // Make sure we have a method to check
180 if (method == null) {
181 return null;
182 }
183
184 // If the requested method is not public we cannot call it
185 if (!Modifier.isPublic(method.getModifiers())) {
186 return null;
187 }
188
189 boolean sameClass = true;
190 if (clazz == null) {
191 clazz = method.getDeclaringClass();
192 } else {
193 if (!method.getDeclaringClass().isAssignableFrom(clazz)) {
194 throw new IllegalArgumentException(clazz.getName() + " is not assignable from " + method.getDeclaringClass().getName());
195 }
196 sameClass = clazz.equals(method.getDeclaringClass());
197 }
198
199 // If the class is public, we are done
200 if (Modifier.isPublic(clazz.getModifiers())) {
201 if (!sameClass && !Modifier.isPublic(method.getDeclaringClass().getModifiers())) {
202 setMethodAccessible(method); // Default access superclass workaround
203 }
204 return method;
205 }
206
207 final String methodName = method.getName();
208 final Class<?>[] parameterTypes = method.getParameterTypes();
209
210 // Check the implemented interfaces and subinterfaces
211 method = getAccessibleMethodFromInterfaceNest(clazz, methodName, parameterTypes);
212
213 // Check the superclass chain
214 if (method == null) {
215 method = getAccessibleMethodFromSuperclass(clazz, methodName, parameterTypes);
216 }
217
218 return method;
219 }
220
221 /**
222 * <p>
223 * Return an accessible method (that is, one that can be invoked via reflection) with given name and a single parameter. If no such method can be found,
224 * return {@code null}. Basically, a convenience wrapper that constructs a {@code Class} array for you.
225 * </p>
226 *
227 * @param clazz get method from this class
228 * @param methodName get method with this name
229 * @param parameterType taking this type of parameter
230 * @return The accessible method
231 */
232 public static Method getAccessibleMethod(final Class<?> clazz, final String methodName, final Class<?> parameterType) {
233 final Class<?>[] parameterTypes = { parameterType };
234 return getAccessibleMethod(clazz, methodName, parameterTypes);
235 }
236
237 /**
238 * <p>
239 * Return an accessible method (that is, one that can be invoked via reflection) with given name and parameters. If no such method can be found, return
240 * {@code null}. This is just a convenient wrapper for {@link #getAccessibleMethod(Method method)}.
241 * </p>
242 *
243 * @param clazz get method from this class
244 * @param methodName get method with this name
245 * @param parameterTypes with these parameters types
246 * @return The accessible method
247 */
248 public static Method getAccessibleMethod(final Class<?> clazz, final String methodName, final Class<?>[] parameterTypes) {
249 try {
250 final MethodDescriptor md = new MethodDescriptor(clazz, methodName, parameterTypes, true);
251 // Check the cache first
252 Method method = getCachedMethod(md);
253 if (method != null) {
254 return method;
255 }
256
257 method = getAccessibleMethod(clazz, clazz.getMethod(methodName, parameterTypes));
258 cacheMethod(md, method);
259 return method;
260 } catch (final NoSuchMethodException e) {
261 return null;
262 }
263 }
264
265 /**
266 * <p>
267 * Return an accessible method (that is, one that can be invoked via reflection) that implements the specified Method. If no such method can be found,
268 * return {@code null}.
269 * </p>
270 *
271 * @param method The method that we wish to call
272 * @return The accessible method
273 */
274 public static Method getAccessibleMethod(final Method method) {
275 // Make sure we have a method to check
276 if (method == null) {
277 return null;
278 }
279
280 return getAccessibleMethod(method.getDeclaringClass(), method);
281 }
282
283 /**
284 * <p>
285 * Return an accessible method (that is, one that can be invoked via reflection) that implements the specified method, by scanning through all implemented
286 * interfaces and subinterfaces. If no such method can be found, return {@code null}.
287 * </p>
288 *
289 * <p>
290 * There isn't any good reason why this method must be private. It is because there doesn't seem any reason why other classes should call this rather than
291 * the higher level methods.
292 * </p>
293 *
294 * @param clazz Parent class for the interfaces to be checked
295 * @param methodName Method name of the method we wish to call
296 * @param parameterTypes The parameter type signatures
297 */
298 private static Method getAccessibleMethodFromInterfaceNest(Class<?> clazz, final String methodName, final Class<?>[] parameterTypes) {
299 Method method = null;
300
301 // Search up the superclass chain
302 for (; clazz != null; clazz = clazz.getSuperclass()) {
303
304 // Check the implemented interfaces of the parent class
305 final Class<?>[] interfaces = clazz.getInterfaces();
306 for (final Class<?> anInterface : interfaces) {
307
308 // Is this interface public?
309 if (!Modifier.isPublic(anInterface.getModifiers())) {
310 continue;
311 }
312
313 // Does the method exist on this interface?
314 try {
315 method = anInterface.getDeclaredMethod(methodName, parameterTypes);
316 } catch (final NoSuchMethodException e) {
317 /*
318 * Swallow, if no method is found after the loop then this method returns null.
319 */
320 }
321 if (method != null) {
322 return method;
323 }
324
325 // Recursively check our parent interfaces
326 method = getAccessibleMethodFromInterfaceNest(anInterface, methodName, parameterTypes);
327 if (method != null) {
328 return method;
329 }
330
331 }
332
333 }
334
335 // We did not find anything
336 return null;
337 }
338
339 /**
340 * <p>
341 * Return an accessible method (that is, one that can be invoked via reflection) by scanning through the superclasses. If no such method can be found,
342 * return {@code null}.
343 * </p>
344 *
345 * @param clazz Class to be checked
346 * @param methodName Method name of the method we wish to call
347 * @param parameterTypes The parameter type signatures
348 */
349 private static Method getAccessibleMethodFromSuperclass(final Class<?> clazz, final String methodName, final Class<?>[] parameterTypes) {
350 Class<?> parentClazz = clazz.getSuperclass();
351 while (parentClazz != null) {
352 if (Modifier.isPublic(parentClazz.getModifiers())) {
353 try {
354 return parentClazz.getMethod(methodName, parameterTypes);
355 } catch (final NoSuchMethodException e) {
356 return null;
357 }
358 }
359 parentClazz = parentClazz.getSuperclass();
360 }
361 return null;
362 }
363
364 /**
365 * Gets the method from the cache, if present.
366 *
367 * @param md The method descriptor
368 * @return The cached method
369 */
370 private static Method getCachedMethod(final MethodDescriptor md) {
371 if (CACHE_METHODS) {
372 final Reference<Method> methodRef = cache.get(md);
373 if (methodRef != null) {
374 return methodRef.get();
375 }
376 }
377 return null;
378 }
379
380 /**
381 * <p>
382 * Find an accessible method that matches the given name and has compatible parameters. Compatible parameters mean that every method parameter is assignable
383 * from the given parameters. In other words, it finds a method with the given name that will take the parameters given.
384 * </p>
385 *
386 * <p>
387 * This method is slightly indeterministic since it loops through methods names and return the first matching method.
388 * </p>
389 *
390 * <p>
391 * This method is used by {@link #invokeMethod(Object object, String methodName, Object[] args, Class[] parameterTypes)}.
392 *
393 * <p>
394 * This method can match primitive parameter by passing in wrapper classes. For example, a {@code Boolean</code> will match a primitive <code>boolean}
395 * parameter.
396 *
397 * @param clazz find method in this class
398 * @param methodName find method with this name
399 * @param parameterTypes find method with compatible parameters
400 * @return The accessible method
401 */
402 public static Method getMatchingAccessibleMethod(final Class<?> clazz, final String methodName, final Class<?>[] parameterTypes) {
403 // trace logging
404 if (LOG.isTraceEnabled()) {
405 LOG.trace("Matching name=" + methodName + " on " + clazz);
406 }
407 final MethodDescriptor md = new MethodDescriptor(clazz, methodName, parameterTypes, false);
408
409 // see if we can find the method directly
410 // most of the time this works and it's much faster
411 try {
412 // Check the cache first
413 Method method = getCachedMethod(md);
414 if (method != null) {
415 return method;
416 }
417
418 method = clazz.getMethod(methodName, parameterTypes);
419 if (LOG.isTraceEnabled()) {
420 LOG.trace("Found straight match: " + method);
421 LOG.trace("isPublic:" + Modifier.isPublic(method.getModifiers()));
422 }
423
424 setMethodAccessible(method); // Default access superclass workaround
425
426 cacheMethod(md, method);
427 return method;
428
429 } catch (final NoSuchMethodException e) {
430 /* SWALLOW */ }
431
432 // search through all methods
433 final int paramSize = parameterTypes.length;
434 Method bestMatch = null;
435 final Method[] methods = clazz.getMethods();
436 float bestMatchCost = Float.MAX_VALUE;
437 float myCost = Float.MAX_VALUE;
438 for (final Method method2 : methods) {
439 if (method2.getName().equals(methodName)) {
440 // log some trace information
441 if (LOG.isTraceEnabled()) {
442 LOG.trace("Found matching name:");
443 LOG.trace(method2);
444 }
445
446 // compare parameters
447 final Class<?>[] methodsParams = method2.getParameterTypes();
448 final int methodParamSize = methodsParams.length;
449 if (methodParamSize == paramSize) {
450 boolean match = true;
451 for (int n = 0; n < methodParamSize; n++) {
452 if (LOG.isTraceEnabled()) {
453 LOG.trace("Param=" + parameterTypes[n].getName());
454 LOG.trace("Method=" + methodsParams[n].getName());
455 }
456 if (!isAssignmentCompatible(methodsParams[n], parameterTypes[n])) {
457 if (LOG.isTraceEnabled()) {
458 LOG.trace(methodsParams[n] + " is not assignable from " + parameterTypes[n]);
459 }
460 match = false;
461 break;
462 }
463 }
464
465 if (match) {
466 // get accessible version of method
467 final Method method = getAccessibleMethod(clazz, method2);
468 if (method != null) {
469 if (LOG.isTraceEnabled()) {
470 LOG.trace(method + " accessible version of " + method2);
471 }
472 setMethodAccessible(method); // Default access superclass workaround
473 myCost = getTotalTransformationCost(parameterTypes, method.getParameterTypes());
474 if (myCost < bestMatchCost) {
475 bestMatch = method;
476 bestMatchCost = myCost;
477 }
478 }
479
480 LOG.trace("Couldn't find accessible method.");
481 }
482 }
483 }
484 }
485 if (bestMatch != null) {
486 cacheMethod(md, bestMatch);
487 } else {
488 // didn't find a match
489 LOG.trace("No match found.");
490 }
491
492 return bestMatch;
493 }
494
495 /**
496 * Gets the number of steps required needed to turn the source class into the destination class. This represents the number of steps in the object hierarchy
497 * graph.
498 *
499 * @param srcClass The source class
500 * @param destClass The destination class
501 * @return The cost of transforming an object
502 */
503 private static float getObjectTransformationCost(Class<?> srcClass, final Class<?> destClass) {
504 float cost = 0.0f;
505 while (srcClass != null && !destClass.equals(srcClass)) {
506 if (destClass.isPrimitive()) {
507 final Class<?> destClassWrapperClazz = getPrimitiveWrapper(destClass);
508 if (destClassWrapperClazz != null && destClassWrapperClazz.equals(srcClass)) {
509 cost += 0.25f;
510 break;
511 }
512 }
513 if (destClass.isInterface() && isAssignmentCompatible(destClass, srcClass)) {
514 // slight penalty for interface match.
515 // we still want an exact match to override an interface match, but
516 // an interface match should override anything where we have to get a
517 // superclass.
518 cost += 0.25f;
519 break;
520 }
521 cost++;
522 srcClass = srcClass.getSuperclass();
523 }
524
525 /*
526 * If the destination class is null, we've traveled all the way up to an Object match. We'll penalize this by adding 1.5 to the cost.
527 */
528 if (srcClass == null) {
529 cost += 1.5f;
530 }
531
532 return cost;
533 }
534
535 /**
536 * Gets the class for the primitive type corresponding to the primitive wrapper class given. For example, an instance of
537 * {@code Boolean.class</code> returns a <code>boolean.class}.
538 *
539 * @param wrapperType the
540 * @return the primitive type class corresponding to the given wrapper class, null if no match is found
541 */
542 public static Class<?> getPrimitiveType(final Class<?> wrapperType) {
543 // does anyone know a better strategy than comparing names?
544 if (Boolean.class.equals(wrapperType)) {
545 return boolean.class;
546 }
547 if (Float.class.equals(wrapperType)) {
548 return float.class;
549 }
550 if (Long.class.equals(wrapperType)) {
551 return long.class;
552 }
553 if (Integer.class.equals(wrapperType)) {
554 return int.class;
555 }
556 if (Short.class.equals(wrapperType)) {
557 return short.class;
558 }
559 if (Byte.class.equals(wrapperType)) {
560 return byte.class;
561 }
562 if (Double.class.equals(wrapperType)) {
563 return double.class;
564 }
565 if (Character.class.equals(wrapperType)) {
566 return char.class;
567 }
568 if (LOG.isDebugEnabled()) {
569 LOG.debug("Not a known primitive wrapper class: " + wrapperType);
570 }
571 return null;
572 }
573
574 /**
575 * Gets the wrapper object class for the given primitive type class. For example, passing {@code boolean.class</code> returns <code>Boolean.class}
576 *
577 * @param primitiveType the primitive type class for which a match is to be found
578 * @return the wrapper type associated with the given primitive or null if no match is found
579 */
580 public static Class<?> getPrimitiveWrapper(final Class<?> primitiveType) {
581 // does anyone know a better strategy than comparing names?
582 if (boolean.class.equals(primitiveType)) {
583 return Boolean.class;
584 }
585 if (float.class.equals(primitiveType)) {
586 return Float.class;
587 }
588 if (long.class.equals(primitiveType)) {
589 return Long.class;
590 }
591 if (int.class.equals(primitiveType)) {
592 return Integer.class;
593 }
594 if (short.class.equals(primitiveType)) {
595 return Short.class;
596 }
597 if (byte.class.equals(primitiveType)) {
598 return Byte.class;
599 }
600 if (double.class.equals(primitiveType)) {
601 return Double.class;
602 }
603 if (char.class.equals(primitiveType)) {
604 return Character.class;
605 }
606 return null;
607 }
608
609 /**
610 * Returns the sum of the object transformation cost for each class in the source argument list.
611 *
612 * @param srcArgs The source arguments
613 * @param destArgs The destination arguments
614 * @return The total transformation cost
615 */
616 private static float getTotalTransformationCost(final Class<?>[] srcArgs, final Class<?>[] destArgs) {
617 float totalCost = 0.0f;
618 for (int i = 0; i < srcArgs.length; i++) {
619 Class<?> srcClass, destClass;
620 srcClass = srcArgs[i];
621 destClass = destArgs[i];
622 totalCost += getObjectTransformationCost(srcClass, destClass);
623 }
624
625 return totalCost;
626 }
627
628 /**
629 * <p>
630 * Invoke a method whose parameter type matches exactly the object type.
631 * </p>
632 *
633 * <p>
634 * This is a convenient wrapper for {@link #invokeExactMethod(Object object,String methodName,Object [] args)}.
635 * </p>
636 *
637 * @param object invoke method on this object
638 * @param methodName get method with this name
639 * @param arg use this argument. May be null (this will result in calling the parameterless method with name {@code methodName}).
640 * @return The value returned by the invoked method
641 * @throws NoSuchMethodException if there is no such accessible method
642 * @throws InvocationTargetException wraps an exception thrown by the method invoked
643 * @throws IllegalAccessException if the requested method is not accessible via reflection
644 */
645 public static Object invokeExactMethod(final Object object, final String methodName, final Object arg)
646 throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
647 final Object[] args = toArray(arg);
648 return invokeExactMethod(object, methodName, args);
649 }
650
651 /**
652 * <p>
653 * Invoke a method whose parameter types match exactly the object types.
654 * </p>
655 *
656 * <p>
657 * This uses reflection to invoke the method obtained from a call to {@code getAccessibleMethod()}.
658 * </p>
659 *
660 * @param object invoke method on this object
661 * @param methodName get method with this name
662 * @param args use these arguments - treat null as empty array (passing null will result in calling the parameterless method with name
663 * {@code methodName}).
664 * @return The value returned by the invoked method
665 * @throws NoSuchMethodException if there is no such accessible method
666 * @throws InvocationTargetException wraps an exception thrown by the method invoked
667 * @throws IllegalAccessException if the requested method is not accessible via reflection
668 */
669 public static Object invokeExactMethod(final Object object, final String methodName, Object[] args)
670 throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
671 if (args == null) {
672 args = BeanUtils.EMPTY_OBJECT_ARRAY;
673 }
674 final int arguments = args.length;
675 final Class<?>[] parameterTypes = new Class[arguments];
676 for (int i = 0; i < arguments; i++) {
677 parameterTypes[i] = args[i].getClass();
678 }
679 return invokeExactMethod(object, methodName, args, parameterTypes);
680 }
681
682 /**
683 * <p>
684 * Invoke a method whose parameter types match exactly the parameter types given.
685 * </p>
686 *
687 * <p>
688 * This uses reflection to invoke the method obtained from a call to {@code getAccessibleMethod()}.
689 * </p>
690 *
691 * @param object invoke method on this object
692 * @param methodName get method with this name
693 * @param args use these arguments - treat null as empty array (passing null will result in calling the parameterless method with name
694 * {@code methodName}).
695 * @param parameterTypes match these parameters - treat null as empty array
696 * @return The value returned by the invoked method
697 * @throws NoSuchMethodException if there is no such accessible method
698 * @throws InvocationTargetException wraps an exception thrown by the method invoked
699 * @throws IllegalAccessException if the requested method is not accessible via reflection
700 */
701 public static Object invokeExactMethod(final Object object, final String methodName, Object[] args, Class<?>[] parameterTypes)
702 throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
703 if (args == null) {
704 args = BeanUtils.EMPTY_OBJECT_ARRAY;
705 }
706
707 if (parameterTypes == null) {
708 parameterTypes = BeanUtils.EMPTY_CLASS_ARRAY;
709 }
710
711 final Method method = getAccessibleMethod(object.getClass(), methodName, parameterTypes);
712 if (method == null) {
713 throw new NoSuchMethodException("No such accessible method: " + methodName + "() on object: " + object.getClass().getName());
714 }
715 return method.invoke(object, args);
716 }
717
718 /**
719 * <p>
720 * Invoke a static method whose parameter type matches exactly the object type.
721 * </p>
722 *
723 * <p>
724 * This is a convenient wrapper for {@link #invokeExactStaticMethod(Class objectClass,String methodName,Object [] args)}.
725 * </p>
726 *
727 * @param objectClass invoke static method on this class
728 * @param methodName get method with this name
729 * @param arg use this argument. May be null (this will result in calling the parameterless method with name {@code methodName}).
730 * @return The value returned by the invoked method
731 * @throws NoSuchMethodException if there is no such accessible method
732 * @throws InvocationTargetException wraps an exception thrown by the method invoked
733 * @throws IllegalAccessException if the requested method is not accessible via reflection
734 * @since 1.8.0
735 */
736 public static Object invokeExactStaticMethod(final Class<?> objectClass, final String methodName, final Object arg)
737 throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
738 final Object[] args = toArray(arg);
739 return invokeExactStaticMethod(objectClass, methodName, args);
740 }
741
742 /**
743 * <p>
744 * Invoke a static method whose parameter types match exactly the object types.
745 * </p>
746 *
747 * <p>
748 * This uses reflection to invoke the method obtained from a call to {@link #getAccessibleMethod(Class, String, Class[])}.
749 * </p>
750 *
751 * @param objectClass invoke static method on this class
752 * @param methodName get method with this name
753 * @param args use these arguments - treat null as empty array (passing null will result in calling the parameterless method with name
754 * {@code methodName}).
755 * @return The value returned by the invoked method
756 * @throws NoSuchMethodException if there is no such accessible method
757 * @throws InvocationTargetException wraps an exception thrown by the method invoked
758 * @throws IllegalAccessException if the requested method is not accessible via reflection
759 * @since 1.8.0
760 */
761 public static Object invokeExactStaticMethod(final Class<?> objectClass, final String methodName, Object[] args)
762 throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
763 if (args == null) {
764 args = BeanUtils.EMPTY_OBJECT_ARRAY;
765 }
766 final int arguments = args.length;
767 final Class<?>[] parameterTypes = new Class[arguments];
768 for (int i = 0; i < arguments; i++) {
769 parameterTypes[i] = args[i].getClass();
770 }
771 return invokeExactStaticMethod(objectClass, methodName, args, parameterTypes);
772 }
773
774 /**
775 * <p>
776 * Invoke a static method whose parameter types match exactly the parameter types given.
777 * </p>
778 *
779 * <p>
780 * This uses reflection to invoke the method obtained from a call to {@link #getAccessibleMethod(Class, String, Class[])}.
781 * </p>
782 *
783 * @param objectClass invoke static method on this class
784 * @param methodName get method with this name
785 * @param args use these arguments - treat null as empty array (passing null will result in calling the parameterless method with name
786 * {@code methodName}).
787 * @param parameterTypes match these parameters - treat null as empty array
788 * @return The value returned by the invoked method
789 * @throws NoSuchMethodException if there is no such accessible method
790 * @throws InvocationTargetException wraps an exception thrown by the method invoked
791 * @throws IllegalAccessException if the requested method is not accessible via reflection
792 * @since 1.8.0
793 */
794 public static Object invokeExactStaticMethod(final Class<?> objectClass, final String methodName, Object[] args, Class<?>[] parameterTypes)
795 throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
796 if (args == null) {
797 args = BeanUtils.EMPTY_OBJECT_ARRAY;
798 }
799
800 if (parameterTypes == null) {
801 parameterTypes = BeanUtils.EMPTY_CLASS_ARRAY;
802 }
803
804 final Method method = getAccessibleMethod(objectClass, methodName, parameterTypes);
805 if (method == null) {
806 throw new NoSuchMethodException("No such accessible method: " + methodName + "() on class: " + objectClass.getName());
807 }
808 return method.invoke(null, args);
809 }
810
811 /**
812 * <p>
813 * Invoke a named method whose parameter type matches the object type.
814 * </p>
815 *
816 * <p>
817 * The behavior of this method is less deterministic than {@code invokeExactMethod()}. It loops through all methods with names that match and then executes
818 * the first it finds with compatible parameters.
819 * </p>
820 *
821 * <p>
822 * This method supports calls to methods taking primitive parameters via passing in wrapping classes. So, for example, a {@code Boolean} class would match a
823 * {@code boolean} primitive.
824 * </p>
825 *
826 * <p>
827 * This is a convenient wrapper for {@link #invokeMethod(Object object,String methodName,Object [] args)}.
828 * </p>
829 *
830 * @param object invoke method on this object
831 * @param methodName get method with this name
832 * @param arg use this argument. May be null (this will result in calling the parameterless method with name {@code methodName}).
833 * @return The value returned by the invoked method
834 * @throws NoSuchMethodException if there is no such accessible method
835 * @throws InvocationTargetException wraps an exception thrown by the method invoked
836 * @throws IllegalAccessException if the requested method is not accessible via reflection
837 */
838 public static Object invokeMethod(final Object object, final String methodName, final Object arg)
839 throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
840 final Object[] args = toArray(arg);
841 return invokeMethod(object, methodName, args);
842 }
843
844 /**
845 * <p>
846 * Invoke a named method whose parameter type matches the object type.
847 * </p>
848 *
849 * <p>
850 * The behavior of this method is less deterministic than {@link #invokeExactMethod(Object object,String methodName,Object [] args)}. It loops through all
851 * methods with names that match and then executes the first it finds with compatible parameters.
852 * </p>
853 *
854 * <p>
855 * This method supports calls to methods taking primitive parameters via passing in wrapping classes. So, for example, a {@code Boolean} class would match a
856 * {@code boolean} primitive.
857 * </p>
858 *
859 * <p>
860 * This is a convenient wrapper for {@link #invokeMethod(Object object, String methodName, Object[] args, Class[] parameterTypes)}.
861 * </p>
862 *
863 * @param object invoke method on this object
864 * @param methodName get method with this name
865 * @param args use these arguments - treat null as empty array (passing null will result in calling the parameterless method with name
866 * {@code methodName}).
867 * @return The value returned by the invoked method
868 * @throws NoSuchMethodException if there is no such accessible method
869 * @throws InvocationTargetException wraps an exception thrown by the method invoked
870 * @throws IllegalAccessException if the requested method is not accessible via reflection
871 */
872 public static Object invokeMethod(final Object object, final String methodName, Object[] args)
873 throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
874 if (args == null) {
875 args = BeanUtils.EMPTY_OBJECT_ARRAY;
876 }
877 final int arguments = args.length;
878 final Class<?>[] parameterTypes = new Class[arguments];
879 for (int i = 0; i < arguments; i++) {
880 parameterTypes[i] = args[i].getClass();
881 }
882 return invokeMethod(object, methodName, args, parameterTypes);
883 }
884
885 /**
886 * <p>
887 * Invoke a named method whose parameter type matches the object type.
888 * </p>
889 *
890 * <p>
891 * The behavior of this method is less deterministic than
892 * {@link #invokeExactMethod(Object object, String methodName, Object[] args, Class[] parameterTypes)}. It loops through all methods with names that match
893 * and then executes the first it finds with compatible parameters.
894 * </p>
895 *
896 * <p>
897 * This method supports calls to methods taking primitive parameters via passing in wrapping classes. So, for example, a {@code Boolean} class would match a
898 * {@code boolean} primitive.
899 * </p>
900 *
901 *
902 * @param object invoke method on this object
903 * @param methodName get method with this name
904 * @param args use these arguments - treat null as empty array (passing null will result in calling the parameterless method with name
905 * {@code methodName}).
906 * @param parameterTypes match these parameters - treat null as empty array
907 * @return The value returned by the invoked method
908 * @throws NoSuchMethodException if there is no such accessible method
909 * @throws InvocationTargetException wraps an exception thrown by the method invoked
910 * @throws IllegalAccessException if the requested method is not accessible via reflection
911 */
912 public static Object invokeMethod(final Object object, final String methodName, Object[] args, Class<?>[] parameterTypes)
913 throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
914 if (parameterTypes == null) {
915 parameterTypes = BeanUtils.EMPTY_CLASS_ARRAY;
916 }
917 if (args == null) {
918 args = BeanUtils.EMPTY_OBJECT_ARRAY;
919 }
920
921 final Method method = getMatchingAccessibleMethod(object.getClass(), methodName, parameterTypes);
922 if (method == null) {
923 throw new NoSuchMethodException("No such accessible method: " + methodName + "() on object: " + object.getClass().getName());
924 }
925 return method.invoke(object, args);
926 }
927
928 /**
929 * <p>
930 * Invoke a named static method whose parameter type matches the object type.
931 * </p>
932 *
933 * <p>
934 * The behavior of this method is less deterministic than {@link #invokeExactMethod(Object, String, Object[], Class[])}. It loops through all methods with
935 * names that match and then executes the first it finds with compatible parameters.
936 * </p>
937 *
938 * <p>
939 * This method supports calls to methods taking primitive parameters via passing in wrapping classes. So, for example, a {@code Boolean} class would match a
940 * {@code boolean} primitive.
941 * </p>
942 *
943 * <p>
944 * This is a convenient wrapper for {@link #invokeStaticMethod(Class objectClass,String methodName,Object [] args)}.
945 * </p>
946 *
947 * @param objectClass invoke static method on this class
948 * @param methodName get method with this name
949 * @param arg use this argument. May be null (this will result in calling the parameterless method with name {@code methodName}).
950 * @return The value returned by the invoked method
951 * @throws NoSuchMethodException if there is no such accessible method
952 * @throws InvocationTargetException wraps an exception thrown by the method invoked
953 * @throws IllegalAccessException if the requested method is not accessible via reflection
954 * @since 1.8.0
955 */
956 public static Object invokeStaticMethod(final Class<?> objectClass, final String methodName, final Object arg)
957 throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
958 final Object[] args = toArray(arg);
959 return invokeStaticMethod(objectClass, methodName, args);
960 }
961
962 /**
963 * <p>
964 * Invoke a named static method whose parameter type matches the object type.
965 * </p>
966 *
967 * <p>
968 * The behavior of this method is less deterministic than {@link #invokeExactMethod(Object object,String methodName,Object [] args)}. It loops through all
969 * methods with names that match and then executes the first it finds with compatible parameters.
970 * </p>
971 *
972 * <p>
973 * This method supports calls to methods taking primitive parameters via passing in wrapping classes. So, for example, a {@code Boolean} class would match a
974 * {@code boolean} primitive.
975 * </p>
976 *
977 * <p>
978 * This is a convenient wrapper for {@link #invokeStaticMethod(Class objectClass, String methodName, Object[] args, Class[] parameterTypes)}.
979 * </p>
980 *
981 * @param objectClass invoke static method on this class
982 * @param methodName get method with this name
983 * @param args use these arguments - treat null as empty array (passing null will result in calling the parameterless method with name
984 * {@code methodName}).
985 * @return The value returned by the invoked method
986 * @throws NoSuchMethodException if there is no such accessible method
987 * @throws InvocationTargetException wraps an exception thrown by the method invoked
988 * @throws IllegalAccessException if the requested method is not accessible via reflection
989 * @since 1.8.0
990 */
991 public static Object invokeStaticMethod(final Class<?> objectClass, final String methodName, Object[] args)
992 throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
993 if (args == null) {
994 args = BeanUtils.EMPTY_OBJECT_ARRAY;
995 }
996 final int arguments = args.length;
997 final Class<?>[] parameterTypes = new Class[arguments];
998 for (int i = 0; i < arguments; i++) {
999 parameterTypes[i] = args[i].getClass();
1000 }
1001 return invokeStaticMethod(objectClass, methodName, args, parameterTypes);
1002 }
1003
1004 /**
1005 * <p>
1006 * Invoke a named static method whose parameter type matches the object type.
1007 * </p>
1008 *
1009 * <p>
1010 * The behavior of this method is less deterministic than
1011 * {@link #invokeExactStaticMethod(Class objectClass, String methodName, Object[] args, Class[] parameterTypes)}. It loops through all methods with names
1012 * that match and then executes the first it finds with compatible parameters.
1013 * </p>
1014 *
1015 * <p>
1016 * This method supports calls to methods taking primitive parameters via passing in wrapping classes. So, for example, a {@code Boolean} class would match a
1017 * {@code boolean} primitive.
1018 * </p>
1019 *
1020 *
1021 * @param objectClass invoke static method on this class
1022 * @param methodName get method with this name
1023 * @param args use these arguments - treat null as empty array (passing null will result in calling the parameterless method with name
1024 * {@code methodName}).
1025 * @param parameterTypes match these parameters - treat null as empty array
1026 * @return The value returned by the invoked method
1027 * @throws NoSuchMethodException if there is no such accessible method
1028 * @throws InvocationTargetException wraps an exception thrown by the method invoked
1029 * @throws IllegalAccessException if the requested method is not accessible via reflection
1030 * @since 1.8.0
1031 */
1032 public static Object invokeStaticMethod(final Class<?> objectClass, final String methodName, Object[] args, Class<?>[] parameterTypes)
1033 throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
1034
1035 if (parameterTypes == null) {
1036 parameterTypes = BeanUtils.EMPTY_CLASS_ARRAY;
1037 }
1038 if (args == null) {
1039 args = BeanUtils.EMPTY_OBJECT_ARRAY;
1040 }
1041
1042 final Method method = getMatchingAccessibleMethod(objectClass, methodName, parameterTypes);
1043 if (method == null) {
1044 throw new NoSuchMethodException("No such accessible method: " + methodName + "() on class: " + objectClass.getName());
1045 }
1046 return method.invoke(null, args);
1047 }
1048
1049 /**
1050 * <p>
1051 * Determine whether a type can be used as a parameter in a method invocation. This method handles primitive conversions correctly.
1052 * </p>
1053 *
1054 * <p>
1055 * In order words, it will match a {@code Boolean</code> to a <code>boolean},
1056 * a {@code Long</code> to a <code>long},
1057 * a {@code Float</code> to a <code>float},
1058 * a {@code Integer</code> to a <code>int},
1059 * and a {@code Double</code> to a <code>double}.
1060 * Now logic widening matches are allowed.
1061 * For example, a {@code Long</code> will not match a <code>int}.
1062 *
1063 * @param parameterType the type of parameter accepted by the method
1064 * @param parameterization the type of parameter being tested
1065 * @return true if the assignment is compatible.
1066 */
1067 public static boolean isAssignmentCompatible(final Class<?> parameterType, final Class<?> parameterization) {
1068 // try plain assignment
1069 if (parameterType.isAssignableFrom(parameterization)) {
1070 return true;
1071 }
1072
1073 if (parameterType.isPrimitive()) {
1074 // this method does *not* do widening - you must specify exactly
1075 // is this the right behavior?
1076 final Class<?> parameterWrapperClazz = getPrimitiveWrapper(parameterType);
1077 if (parameterWrapperClazz != null) {
1078 return parameterWrapperClazz.equals(parameterization);
1079 }
1080 }
1081
1082 return false;
1083 }
1084
1085 /**
1086 * Sets whether methods should be cached for greater performance or not, default is {@code true}.
1087 *
1088 * @param cacheMethods {@code true} if methods should be cached for greater performance, otherwise {@code false}
1089 * @since 1.8.0
1090 */
1091 public static synchronized void setCacheMethods(final boolean cacheMethods) {
1092 CACHE_METHODS = cacheMethods;
1093 if (!CACHE_METHODS) {
1094 clearCache();
1095 }
1096 }
1097
1098 /**
1099 * Try to make the method accessible
1100 *
1101 * @param method The source arguments
1102 */
1103 private static void setMethodAccessible(final Method method) {
1104 try {
1105 //
1106 // XXX Default access superclass workaround
1107 //
1108 // When a public class has a default access superclass
1109 // with public methods, these methods are accessible.
1110 // Calling them from compiled code works fine.
1111 //
1112 // Unfortunately, using reflection to invoke these methods
1113 // seems to (wrongly) to prevent access even when the method
1114 // modifier is public.
1115 //
1116 // The following workaround solves the problem but will only
1117 // work from sufficiently privileges code.
1118 //
1119 // Better workarounds would be gratefully accepted.
1120 //
1121 if (!method.isAccessible()) {
1122 method.setAccessible(true);
1123 }
1124
1125 } catch (final SecurityException se) {
1126 // log but continue just in case the method.invoke works anyway
1127 if (!loggedAccessibleWarning) {
1128 boolean vulnerableJVM = false;
1129 try {
1130 final String specVersion = SystemProperties.getJavaSpecificationVersion();
1131 if (specVersion.charAt(0) == '1'
1132 && (specVersion.charAt(2) == '0' || specVersion.charAt(2) == '1' || specVersion.charAt(2) == '2' || specVersion.charAt(2) == '3')) {
1133
1134 vulnerableJVM = true;
1135 }
1136 } catch (final SecurityException e) {
1137 // don't know - so display warning
1138 vulnerableJVM = true;
1139 }
1140 if (vulnerableJVM) {
1141 LOG.warn("Current Security Manager restricts use of workarounds for reflection bugs " + " in pre-1.4 JVMs.");
1142 }
1143 loggedAccessibleWarning = true;
1144 }
1145 LOG.debug("Cannot setAccessible on method. Therefore cannot use jvm access bug workaround.", se);
1146 }
1147 }
1148
1149 private static Object[] toArray(final Object arg) {
1150 Object[] args = null;
1151 if (arg != null) {
1152 args = new Object[] { arg };
1153 }
1154 return args;
1155 }
1156
1157 /**
1158 * Find a non primitive representation for given primitive class.
1159 *
1160 * @param clazz the class to find a representation for, not null
1161 * @return the original class if it not a primitive. Otherwise the wrapper class. Not null
1162 */
1163 public static Class<?> toNonPrimitiveClass(final Class<?> clazz) {
1164 if (clazz.isPrimitive()) {
1165 final Class<?> primitiveClazz = MethodUtils.getPrimitiveWrapper(clazz);
1166 // the above method returns
1167 if (primitiveClazz != null) {
1168 return primitiveClazz;
1169 }
1170 }
1171 return clazz;
1172 }
1173
1174 private MethodUtils() {
1175 // empty
1176 }
1177 }