View Javadoc
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    *      http://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 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 final 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 }