MethodUtils.java

  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. package org.apache.commons.beanutils2;

  18. import java.lang.ref.Reference;
  19. import java.lang.ref.WeakReference;
  20. import java.lang.reflect.InvocationTargetException;
  21. import java.lang.reflect.Method;
  22. import java.lang.reflect.Modifier;
  23. import java.util.Arrays;
  24. import java.util.Collections;
  25. import java.util.Map;
  26. import java.util.Objects;
  27. import java.util.WeakHashMap;

  28. import org.apache.commons.lang3.SystemProperties;
  29. import org.apache.commons.logging.Log;
  30. import org.apache.commons.logging.LogFactory;

  31. /**
  32.  * <p>
  33.  * Utility reflection methods focused on methods in general rather than properties in particular.
  34.  * </p>
  35.  *
  36.  * <h2>Known Limitations</h2>
  37.  * <h3>Accessing Public Methods In A Default Access Superclass</h3>
  38.  * <p>
  39.  * There is an issue when invoking public methods contained in a default access superclass. Reflection locates these methods fine and correctly assigns them as
  40.  * public. However, an {@code IllegalAccessException} is thrown if the method is invoked.
  41.  * </p>
  42.  *
  43.  * <p>
  44.  * {@code MethodUtils} contains a workaround for this situation. It will attempt to call {@code setAccessible} on this method. If this call succeeds, then the
  45.  * 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
  46.  * be logged and the method may fail.
  47.  * </p>
  48.  */
  49. public final class MethodUtils {

  50.     /**
  51.      * Represents the key to looking up a Method by reflection.
  52.      */
  53.     private static final class MethodDescriptor {
  54.         private final Class<?> cls;
  55.         private final String methodName;
  56.         private final Class<?>[] paramTypes;
  57.         private final boolean exact;
  58.         private final int hashCode;

  59.         /**
  60.          * The sole constructor.
  61.          *
  62.          * @param cls        the class to reflect, must not be null
  63.          * @param methodName the method name to obtain
  64.          * @param paramTypes the array of classes representing the parameter types
  65.          * @param exact      whether the match has to be exact.
  66.          */
  67.         public MethodDescriptor(final Class<?> cls, final String methodName, final Class<?>[] paramTypes, final boolean exact) {
  68.             this.cls = Objects.requireNonNull(cls, "cls");
  69.             this.methodName = Objects.requireNonNull(methodName, "methodName");
  70.             this.paramTypes = paramTypes != null ? paramTypes : BeanUtils.EMPTY_CLASS_ARRAY;
  71.             this.exact = exact;
  72.             this.hashCode = methodName.length();
  73.         }

  74.         /**
  75.          * Checks for equality.
  76.          *
  77.          * @param obj object to be tested for equality
  78.          * @return true, if the object describes the same Method.
  79.          */
  80.         @Override
  81.         public boolean equals(final Object obj) {
  82.             if (!(obj instanceof MethodDescriptor)) {
  83.                 return false;
  84.             }
  85.             final MethodDescriptor md = (MethodDescriptor) obj;

  86.             return exact == md.exact && methodName.equals(md.methodName) && cls.equals(md.cls) && Arrays.equals(paramTypes, md.paramTypes);
  87.         }

  88.         /**
  89.          * 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
  90.          * use the equals method to determine equality.
  91.          *
  92.          * @return the string length of method name.
  93.          */
  94.         @Override
  95.         public int hashCode() {
  96.             return hashCode;
  97.         }
  98.     }

  99.     private static final Log LOG = LogFactory.getLog(MethodUtils.class);

  100.     /**
  101.      * Only log warning about accessibility work around once.
  102.      * <p>
  103.      * 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
  104.      * per webapp. However making the warning appear once per webapp means having a map keyed by context classloader which introduces nasty memory-leak
  105.      * 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
  106.      * enough.
  107.      */
  108.     private static boolean loggedAccessibleWarning;

  109.     /**
  110.      * Indicates whether methods should be cached for improved performance.
  111.      * <p>
  112.      * 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
  113.      * would mean having a map keyed by context classloader which may introduce memory-leak problems.
  114.      */
  115.     private static boolean CACHE_METHODS = true;

  116.     /**
  117.      * Stores a cache of MethodDescriptor -> Method in a WeakHashMap.
  118.      * <p>
  119.      * 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
  120.      * 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
  121.      * 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
  122.      * commons-collections LRUMap may be more appropriate (though of course selecting an appropriate size is an issue).
  123.      * <p>
  124.      * 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
  125.      * 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
  126.      * loaders will generate non-equal MethodDescriptor objects and hence end up with different entries in the map.
  127.      */
  128.     private static final Map<MethodDescriptor, Reference<Method>> cache = Collections.synchronizedMap(new WeakHashMap<>());

  129.     /**
  130.      * Add a method to the cache.
  131.      *
  132.      * @param md     The method descriptor
  133.      * @param method The method to cache
  134.      */
  135.     private static void cacheMethod(final MethodDescriptor md, final Method method) {
  136.         if (CACHE_METHODS && method != null) {
  137.             cache.put(md, new WeakReference<>(method));
  138.         }
  139.     }

  140.     /**
  141.      * Clear the method cache.
  142.      *
  143.      * @return the number of cached methods cleared
  144.      * @since 1.8.0
  145.      */
  146.     public static synchronized int clearCache() {
  147.         final int size = cache.size();
  148.         cache.clear();
  149.         return size;
  150.     }

  151.     /**
  152.      * <p>
  153.      * 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,
  154.      * return {@code null}.
  155.      * </p>
  156.      *
  157.      * @param clazz  The class of the object
  158.      * @param method The method that we wish to call
  159.      * @return The accessible method
  160.      * @since 1.8.0
  161.      */
  162.     public static Method getAccessibleMethod(Class<?> clazz, Method method) {
  163.         // Make sure we have a method to check
  164.         if (method == null) {
  165.             return null;
  166.         }

  167.         // If the requested method is not public we cannot call it
  168.         if (!Modifier.isPublic(method.getModifiers())) {
  169.             return null;
  170.         }

  171.         boolean sameClass = true;
  172.         if (clazz == null) {
  173.             clazz = method.getDeclaringClass();
  174.         } else {
  175.             if (!method.getDeclaringClass().isAssignableFrom(clazz)) {
  176.                 throw new IllegalArgumentException(clazz.getName() + " is not assignable from " + method.getDeclaringClass().getName());
  177.             }
  178.             sameClass = clazz.equals(method.getDeclaringClass());
  179.         }

  180.         // If the class is public, we are done
  181.         if (Modifier.isPublic(clazz.getModifiers())) {
  182.             if (!sameClass && !Modifier.isPublic(method.getDeclaringClass().getModifiers())) {
  183.                 setMethodAccessible(method); // Default access superclass workaround
  184.             }
  185.             return method;
  186.         }

  187.         final String methodName = method.getName();
  188.         final Class<?>[] parameterTypes = method.getParameterTypes();

  189.         // Check the implemented interfaces and subinterfaces
  190.         method = getAccessibleMethodFromInterfaceNest(clazz, methodName, parameterTypes);

  191.         // Check the superclass chain
  192.         if (method == null) {
  193.             method = getAccessibleMethodFromSuperclass(clazz, methodName, parameterTypes);
  194.         }

  195.         return method;
  196.     }

  197.     /**
  198.      * <p>
  199.      * 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,
  200.      * return {@code null}. Basically, a convenience wrapper that constructs a {@code Class} array for you.
  201.      * </p>
  202.      *
  203.      * @param clazz         get method from this class
  204.      * @param methodName    get method with this name
  205.      * @param parameterType taking this type of parameter
  206.      * @return The accessible method
  207.      */
  208.     public static Method getAccessibleMethod(final Class<?> clazz, final String methodName, final Class<?> parameterType) {
  209.         final Class<?>[] parameterTypes = { parameterType };
  210.         return getAccessibleMethod(clazz, methodName, parameterTypes);
  211.     }

  212.     /**
  213.      * <p>
  214.      * 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
  215.      * {@code null}. This is just a convenient wrapper for {@link #getAccessibleMethod(Method method)}.
  216.      * </p>
  217.      *
  218.      * @param clazz          get method from this class
  219.      * @param methodName     get method with this name
  220.      * @param parameterTypes with these parameters types
  221.      * @return The accessible method
  222.      */
  223.     public static Method getAccessibleMethod(final Class<?> clazz, final String methodName, final Class<?>[] parameterTypes) {
  224.         try {
  225.             final MethodDescriptor md = new MethodDescriptor(clazz, methodName, parameterTypes, true);
  226.             // Check the cache first
  227.             Method method = getCachedMethod(md);
  228.             if (method != null) {
  229.                 return method;
  230.             }

  231.             method = getAccessibleMethod(clazz, clazz.getMethod(methodName, parameterTypes));
  232.             cacheMethod(md, method);
  233.             return method;
  234.         } catch (final NoSuchMethodException e) {
  235.             return null;
  236.         }
  237.     }

  238.     /**
  239.      * <p>
  240.      * 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,
  241.      * return {@code null}.
  242.      * </p>
  243.      *
  244.      * @param method The method that we wish to call
  245.      * @return The accessible method
  246.      */
  247.     public static Method getAccessibleMethod(final Method method) {
  248.         // Make sure we have a method to check
  249.         if (method == null) {
  250.             return null;
  251.         }

  252.         return getAccessibleMethod(method.getDeclaringClass(), method);
  253.     }

  254.     /**
  255.      * <p>
  256.      * Return an accessible method (that is, one that can be invoked via reflection) that implements the specified method, by scanning through all implemented
  257.      * interfaces and subinterfaces. If no such method can be found, return {@code null}.
  258.      * </p>
  259.      *
  260.      * <p>
  261.      * 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
  262.      * the higher level methods.
  263.      * </p>
  264.      *
  265.      * @param clazz          Parent class for the interfaces to be checked
  266.      * @param methodName     Method name of the method we wish to call
  267.      * @param parameterTypes The parameter type signatures
  268.      */
  269.     private static Method getAccessibleMethodFromInterfaceNest(Class<?> clazz, final String methodName, final Class<?>[] parameterTypes) {
  270.         Method method = null;

  271.         // Search up the superclass chain
  272.         for (; clazz != null; clazz = clazz.getSuperclass()) {

  273.             // Check the implemented interfaces of the parent class
  274.             final Class<?>[] interfaces = clazz.getInterfaces();
  275.             for (final Class<?> anInterface : interfaces) {

  276.                 // Is this interface public?
  277.                 if (!Modifier.isPublic(anInterface.getModifiers())) {
  278.                     continue;
  279.                 }

  280.                 // Does the method exist on this interface?
  281.                 try {
  282.                     method = anInterface.getDeclaredMethod(methodName, parameterTypes);
  283.                 } catch (final NoSuchMethodException e) {
  284.                     /*
  285.                      * Swallow, if no method is found after the loop then this method returns null.
  286.                      */
  287.                 }
  288.                 if (method != null) {
  289.                     return method;
  290.                 }

  291.                 // Recursively check our parent interfaces
  292.                 method = getAccessibleMethodFromInterfaceNest(anInterface, methodName, parameterTypes);
  293.                 if (method != null) {
  294.                     return method;
  295.                 }

  296.             }

  297.         }

  298.         // We did not find anything
  299.         return null;
  300.     }

  301.     /**
  302.      * <p>
  303.      * 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,
  304.      * return {@code null}.
  305.      * </p>
  306.      *
  307.      * @param clazz          Class to be checked
  308.      * @param methodName     Method name of the method we wish to call
  309.      * @param parameterTypes The parameter type signatures
  310.      */
  311.     private static Method getAccessibleMethodFromSuperclass(final Class<?> clazz, final String methodName, final Class<?>[] parameterTypes) {
  312.         Class<?> parentClazz = clazz.getSuperclass();
  313.         while (parentClazz != null) {
  314.             if (Modifier.isPublic(parentClazz.getModifiers())) {
  315.                 try {
  316.                     return parentClazz.getMethod(methodName, parameterTypes);
  317.                 } catch (final NoSuchMethodException e) {
  318.                     return null;
  319.                 }
  320.             }
  321.             parentClazz = parentClazz.getSuperclass();
  322.         }
  323.         return null;
  324.     }

  325.     /**
  326.      * Gets the method from the cache, if present.
  327.      *
  328.      * @param md The method descriptor
  329.      * @return The cached method
  330.      */
  331.     private static Method getCachedMethod(final MethodDescriptor md) {
  332.         if (CACHE_METHODS) {
  333.             final Reference<Method> methodRef = cache.get(md);
  334.             if (methodRef != null) {
  335.                 return methodRef.get();
  336.             }
  337.         }
  338.         return null;
  339.     }

  340.     /**
  341.      * <p>
  342.      * Find an accessible method that matches the given name and has compatible parameters. Compatible parameters mean that every method parameter is assignable
  343.      * from the given parameters. In other words, it finds a method with the given name that will take the parameters given.
  344.      * </p>
  345.      *
  346.      * <p>
  347.      * This method is slightly indeterministic since it loops through methods names and return the first matching method.
  348.      * </p>
  349.      *
  350.      * <p>
  351.      * This method is used by {@link #invokeMethod(Object object, String methodName, Object[] args, Class[] parameterTypes)}.
  352.      *
  353.      * <p>
  354.      * This method can match primitive parameter by passing in wrapper classes. For example, a {@code Boolean</code> will match a primitive <code>boolean}
  355.      * parameter.
  356.      *
  357.      * @param clazz          find method in this class
  358.      * @param methodName     find method with this name
  359.      * @param parameterTypes find method with compatible parameters
  360.      * @return The accessible method
  361.      */
  362.     public static Method getMatchingAccessibleMethod(final Class<?> clazz, final String methodName, final Class<?>[] parameterTypes) {
  363.         // trace logging
  364.         if (LOG.isTraceEnabled()) {
  365.             LOG.trace("Matching name=" + methodName + " on " + clazz);
  366.         }
  367.         final MethodDescriptor md = new MethodDescriptor(clazz, methodName, parameterTypes, false);

  368.         // see if we can find the method directly
  369.         // most of the time this works and it's much faster
  370.         try {
  371.             // Check the cache first
  372.             Method method = getCachedMethod(md);
  373.             if (method != null) {
  374.                 return method;
  375.             }

  376.             method = clazz.getMethod(methodName, parameterTypes);
  377.             if (LOG.isTraceEnabled()) {
  378.                 LOG.trace("Found straight match: " + method);
  379.                 LOG.trace("isPublic:" + Modifier.isPublic(method.getModifiers()));
  380.             }

  381.             setMethodAccessible(method); // Default access superclass workaround

  382.             cacheMethod(md, method);
  383.             return method;

  384.         } catch (final NoSuchMethodException e) {
  385.             /* SWALLOW */ }

  386.         // search through all methods
  387.         final int paramSize = parameterTypes.length;
  388.         Method bestMatch = null;
  389.         final Method[] methods = clazz.getMethods();
  390.         float bestMatchCost = Float.MAX_VALUE;
  391.         float myCost = Float.MAX_VALUE;
  392.         for (final Method method2 : methods) {
  393.             if (method2.getName().equals(methodName)) {
  394.                 // log some trace information
  395.                 if (LOG.isTraceEnabled()) {
  396.                     LOG.trace("Found matching name:");
  397.                     LOG.trace(method2);
  398.                 }

  399.                 // compare parameters
  400.                 final Class<?>[] methodsParams = method2.getParameterTypes();
  401.                 final int methodParamSize = methodsParams.length;
  402.                 if (methodParamSize == paramSize) {
  403.                     boolean match = true;
  404.                     for (int n = 0; n < methodParamSize; n++) {
  405.                         if (LOG.isTraceEnabled()) {
  406.                             LOG.trace("Param=" + parameterTypes[n].getName());
  407.                             LOG.trace("Method=" + methodsParams[n].getName());
  408.                         }
  409.                         if (!isAssignmentCompatible(methodsParams[n], parameterTypes[n])) {
  410.                             if (LOG.isTraceEnabled()) {
  411.                                 LOG.trace(methodsParams[n] + " is not assignable from " + parameterTypes[n]);
  412.                             }
  413.                             match = false;
  414.                             break;
  415.                         }
  416.                     }

  417.                     if (match) {
  418.                         // get accessible version of method
  419.                         final Method method = getAccessibleMethod(clazz, method2);
  420.                         if (method != null) {
  421.                             if (LOG.isTraceEnabled()) {
  422.                                 LOG.trace(method + " accessible version of " + method2);
  423.                             }
  424.                             setMethodAccessible(method); // Default access superclass workaround
  425.                             myCost = getTotalTransformationCost(parameterTypes, method.getParameterTypes());
  426.                             if (myCost < bestMatchCost) {
  427.                                 bestMatch = method;
  428.                                 bestMatchCost = myCost;
  429.                             }
  430.                         }

  431.                         LOG.trace("Couldn't find accessible method.");
  432.                     }
  433.                 }
  434.             }
  435.         }
  436.         if (bestMatch != null) {
  437.             cacheMethod(md, bestMatch);
  438.         } else {
  439.             // didn't find a match
  440.             LOG.trace("No match found.");
  441.         }

  442.         return bestMatch;
  443.     }

  444.     /**
  445.      * 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
  446.      * graph.
  447.      *
  448.      * @param srcClass  The source class
  449.      * @param destClass The destination class
  450.      * @return The cost of transforming an object
  451.      */
  452.     private static float getObjectTransformationCost(Class<?> srcClass, final Class<?> destClass) {
  453.         float cost = 0.0f;
  454.         while (srcClass != null && !destClass.equals(srcClass)) {
  455.             if (destClass.isPrimitive()) {
  456.                 final Class<?> destClassWrapperClazz = getPrimitiveWrapper(destClass);
  457.                 if (destClassWrapperClazz != null && destClassWrapperClazz.equals(srcClass)) {
  458.                     cost += 0.25f;
  459.                     break;
  460.                 }
  461.             }
  462.             if (destClass.isInterface() && isAssignmentCompatible(destClass, srcClass)) {
  463.                 // slight penalty for interface match.
  464.                 // we still want an exact match to override an interface match, but
  465.                 // an interface match should override anything where we have to get a
  466.                 // superclass.
  467.                 cost += 0.25f;
  468.                 break;
  469.             }
  470.             cost++;
  471.             srcClass = srcClass.getSuperclass();
  472.         }

  473.         /*
  474.          * 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.
  475.          */
  476.         if (srcClass == null) {
  477.             cost += 1.5f;
  478.         }

  479.         return cost;
  480.     }

  481.     /**
  482.      * Gets the class for the primitive type corresponding to the primitive wrapper class given. For example, an instance of
  483.      * {@code Boolean.class</code> returns a <code>boolean.class}.
  484.      *
  485.      * @param wrapperType the
  486.      * @return the primitive type class corresponding to the given wrapper class, null if no match is found
  487.      */
  488.     public static Class<?> getPrimitiveType(final Class<?> wrapperType) {
  489.         // does anyone know a better strategy than comparing names?
  490.         if (Boolean.class.equals(wrapperType)) {
  491.             return boolean.class;
  492.         }
  493.         if (Float.class.equals(wrapperType)) {
  494.             return float.class;
  495.         }
  496.         if (Long.class.equals(wrapperType)) {
  497.             return long.class;
  498.         }
  499.         if (Integer.class.equals(wrapperType)) {
  500.             return int.class;
  501.         }
  502.         if (Short.class.equals(wrapperType)) {
  503.             return short.class;
  504.         }
  505.         if (Byte.class.equals(wrapperType)) {
  506.             return byte.class;
  507.         }
  508.         if (Double.class.equals(wrapperType)) {
  509.             return double.class;
  510.         }
  511.         if (Character.class.equals(wrapperType)) {
  512.             return char.class;
  513.         }
  514.         if (LOG.isDebugEnabled()) {
  515.             LOG.debug("Not a known primitive wrapper class: " + wrapperType);
  516.         }
  517.         return null;
  518.     }

  519.     /**
  520.      * Gets the wrapper object class for the given primitive type class. For example, passing {@code boolean.class</code> returns <code>Boolean.class}
  521.      *
  522.      * @param primitiveType the primitive type class for which a match is to be found
  523.      * @return the wrapper type associated with the given primitive or null if no match is found
  524.      */
  525.     public static Class<?> getPrimitiveWrapper(final Class<?> primitiveType) {
  526.         // does anyone know a better strategy than comparing names?
  527.         if (boolean.class.equals(primitiveType)) {
  528.             return Boolean.class;
  529.         }
  530.         if (float.class.equals(primitiveType)) {
  531.             return Float.class;
  532.         }
  533.         if (long.class.equals(primitiveType)) {
  534.             return Long.class;
  535.         }
  536.         if (int.class.equals(primitiveType)) {
  537.             return Integer.class;
  538.         }
  539.         if (short.class.equals(primitiveType)) {
  540.             return Short.class;
  541.         }
  542.         if (byte.class.equals(primitiveType)) {
  543.             return Byte.class;
  544.         }
  545.         if (double.class.equals(primitiveType)) {
  546.             return Double.class;
  547.         }
  548.         if (char.class.equals(primitiveType)) {
  549.             return Character.class;
  550.         }
  551.         return null;
  552.     }

  553.     /**
  554.      * Returns the sum of the object transformation cost for each class in the source argument list.
  555.      *
  556.      * @param srcArgs  The source arguments
  557.      * @param destArgs The destination arguments
  558.      * @return The total transformation cost
  559.      */
  560.     private static float getTotalTransformationCost(final Class<?>[] srcArgs, final Class<?>[] destArgs) {
  561.         float totalCost = 0.0f;
  562.         for (int i = 0; i < srcArgs.length; i++) {
  563.             Class<?> srcClass, destClass;
  564.             srcClass = srcArgs[i];
  565.             destClass = destArgs[i];
  566.             totalCost += getObjectTransformationCost(srcClass, destClass);
  567.         }

  568.         return totalCost;
  569.     }

  570.     /**
  571.      * <p>
  572.      * Invoke a method whose parameter type matches exactly the object type.
  573.      * </p>
  574.      *
  575.      * <p>
  576.      * This is a convenient wrapper for {@link #invokeExactMethod(Object object,String methodName,Object [] args)}.
  577.      * </p>
  578.      *
  579.      * @param object     invoke method on this object
  580.      * @param methodName get method with this name
  581.      * @param arg        use this argument. May be null (this will result in calling the parameterless method with name {@code methodName}).
  582.      * @return The value returned by the invoked method
  583.      * @throws NoSuchMethodException     if there is no such accessible method
  584.      * @throws InvocationTargetException wraps an exception thrown by the method invoked
  585.      * @throws IllegalAccessException    if the requested method is not accessible via reflection
  586.      */
  587.     public static Object invokeExactMethod(final Object object, final String methodName, final Object arg)
  588.             throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
  589.         final Object[] args = toArray(arg);
  590.         return invokeExactMethod(object, methodName, args);
  591.     }

  592.     /**
  593.      * <p>
  594.      * Invoke a method whose parameter types match exactly the object types.
  595.      * </p>
  596.      *
  597.      * <p>
  598.      * This uses reflection to invoke the method obtained from a call to {@code getAccessibleMethod()}.
  599.      * </p>
  600.      *
  601.      * @param object     invoke method on this object
  602.      * @param methodName get method with this name
  603.      * @param args       use these arguments - treat null as empty array (passing null will result in calling the parameterless method with name
  604.      *                   {@code methodName}).
  605.      * @return The value returned by the invoked method
  606.      * @throws NoSuchMethodException     if there is no such accessible method
  607.      * @throws InvocationTargetException wraps an exception thrown by the method invoked
  608.      * @throws IllegalAccessException    if the requested method is not accessible via reflection
  609.      */
  610.     public static Object invokeExactMethod(final Object object, final String methodName, Object[] args)
  611.             throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
  612.         if (args == null) {
  613.             args = BeanUtils.EMPTY_OBJECT_ARRAY;
  614.         }
  615.         final int arguments = args.length;
  616.         final Class<?>[] parameterTypes = new Class[arguments];
  617.         for (int i = 0; i < arguments; i++) {
  618.             parameterTypes[i] = args[i].getClass();
  619.         }
  620.         return invokeExactMethod(object, methodName, args, parameterTypes);
  621.     }

  622.     /**
  623.      * <p>
  624.      * Invoke a method whose parameter types match exactly the parameter types given.
  625.      * </p>
  626.      *
  627.      * <p>
  628.      * This uses reflection to invoke the method obtained from a call to {@code getAccessibleMethod()}.
  629.      * </p>
  630.      *
  631.      * @param object         invoke method on this object
  632.      * @param methodName     get method with this name
  633.      * @param args           use these arguments - treat null as empty array (passing null will result in calling the parameterless method with name
  634.      *                       {@code methodName}).
  635.      * @param parameterTypes match these parameters - treat null as empty array
  636.      * @return The value returned by the invoked method
  637.      * @throws NoSuchMethodException     if there is no such accessible method
  638.      * @throws InvocationTargetException wraps an exception thrown by the method invoked
  639.      * @throws IllegalAccessException    if the requested method is not accessible via reflection
  640.      */
  641.     public static Object invokeExactMethod(final Object object, final String methodName, Object[] args, Class<?>[] parameterTypes)
  642.             throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
  643.         if (args == null) {
  644.             args = BeanUtils.EMPTY_OBJECT_ARRAY;
  645.         }

  646.         if (parameterTypes == null) {
  647.             parameterTypes = BeanUtils.EMPTY_CLASS_ARRAY;
  648.         }

  649.         final Method method = getAccessibleMethod(object.getClass(), methodName, parameterTypes);
  650.         if (method == null) {
  651.             throw new NoSuchMethodException("No such accessible method: " + methodName + "() on object: " + object.getClass().getName());
  652.         }
  653.         return method.invoke(object, args);
  654.     }

  655.     /**
  656.      * <p>
  657.      * Invoke a static method whose parameter type matches exactly the object type.
  658.      * </p>
  659.      *
  660.      * <p>
  661.      * This is a convenient wrapper for {@link #invokeExactStaticMethod(Class objectClass,String methodName,Object [] args)}.
  662.      * </p>
  663.      *
  664.      * @param objectClass invoke static method on this class
  665.      * @param methodName  get method with this name
  666.      * @param arg         use this argument. May be null (this will result in calling the parameterless method with name {@code methodName}).
  667.      * @return The value returned by the invoked method
  668.      * @throws NoSuchMethodException     if there is no such accessible method
  669.      * @throws InvocationTargetException wraps an exception thrown by the method invoked
  670.      * @throws IllegalAccessException    if the requested method is not accessible via reflection
  671.      * @since 1.8.0
  672.      */
  673.     public static Object invokeExactStaticMethod(final Class<?> objectClass, final String methodName, final Object arg)
  674.             throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
  675.         final Object[] args = toArray(arg);
  676.         return invokeExactStaticMethod(objectClass, methodName, args);
  677.     }

  678.     /**
  679.      * <p>
  680.      * Invoke a static method whose parameter types match exactly the object types.
  681.      * </p>
  682.      *
  683.      * <p>
  684.      * This uses reflection to invoke the method obtained from a call to {@link #getAccessibleMethod(Class, String, Class[])}.
  685.      * </p>
  686.      *
  687.      * @param objectClass invoke static method on this class
  688.      * @param methodName  get method with this name
  689.      * @param args        use these arguments - treat null as empty array (passing null will result in calling the parameterless method with name
  690.      *                    {@code methodName}).
  691.      * @return The value returned by the invoked method
  692.      * @throws NoSuchMethodException     if there is no such accessible method
  693.      * @throws InvocationTargetException wraps an exception thrown by the method invoked
  694.      * @throws IllegalAccessException    if the requested method is not accessible via reflection
  695.      * @since 1.8.0
  696.      */
  697.     public static Object invokeExactStaticMethod(final Class<?> objectClass, final String methodName, Object[] args)
  698.             throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
  699.         if (args == null) {
  700.             args = BeanUtils.EMPTY_OBJECT_ARRAY;
  701.         }
  702.         final int arguments = args.length;
  703.         final Class<?>[] parameterTypes = new Class[arguments];
  704.         for (int i = 0; i < arguments; i++) {
  705.             parameterTypes[i] = args[i].getClass();
  706.         }
  707.         return invokeExactStaticMethod(objectClass, methodName, args, parameterTypes);
  708.     }

  709.     /**
  710.      * <p>
  711.      * Invoke a static method whose parameter types match exactly the parameter types given.
  712.      * </p>
  713.      *
  714.      * <p>
  715.      * This uses reflection to invoke the method obtained from a call to {@link #getAccessibleMethod(Class, String, Class[])}.
  716.      * </p>
  717.      *
  718.      * @param objectClass    invoke static method on this class
  719.      * @param methodName     get method with this name
  720.      * @param args           use these arguments - treat null as empty array (passing null will result in calling the parameterless method with name
  721.      *                       {@code methodName}).
  722.      * @param parameterTypes match these parameters - treat null as empty array
  723.      * @return The value returned by the invoked method
  724.      * @throws NoSuchMethodException     if there is no such accessible method
  725.      * @throws InvocationTargetException wraps an exception thrown by the method invoked
  726.      * @throws IllegalAccessException    if the requested method is not accessible via reflection
  727.      * @since 1.8.0
  728.      */
  729.     public static Object invokeExactStaticMethod(final Class<?> objectClass, final String methodName, Object[] args, Class<?>[] parameterTypes)
  730.             throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
  731.         if (args == null) {
  732.             args = BeanUtils.EMPTY_OBJECT_ARRAY;
  733.         }

  734.         if (parameterTypes == null) {
  735.             parameterTypes = BeanUtils.EMPTY_CLASS_ARRAY;
  736.         }

  737.         final Method method = getAccessibleMethod(objectClass, methodName, parameterTypes);
  738.         if (method == null) {
  739.             throw new NoSuchMethodException("No such accessible method: " + methodName + "() on class: " + objectClass.getName());
  740.         }
  741.         return method.invoke(null, args);
  742.     }

  743.     /**
  744.      * <p>
  745.      * Invoke a named method whose parameter type matches the object type.
  746.      * </p>
  747.      *
  748.      * <p>
  749.      * The behavior of this method is less deterministic than {@code invokeExactMethod()}. It loops through all methods with names that match and then executes
  750.      * the first it finds with compatible parameters.
  751.      * </p>
  752.      *
  753.      * <p>
  754.      * This method supports calls to methods taking primitive parameters via passing in wrapping classes. So, for example, a {@code Boolean} class would match a
  755.      * {@code boolean} primitive.
  756.      * </p>
  757.      *
  758.      * <p>
  759.      * This is a convenient wrapper for {@link #invokeMethod(Object object,String methodName,Object [] args)}.
  760.      * </p>
  761.      *
  762.      * @param object     invoke method on this object
  763.      * @param methodName get method with this name
  764.      * @param arg        use this argument. May be null (this will result in calling the parameterless method with name {@code methodName}).
  765.      * @return The value returned by the invoked method
  766.      * @throws NoSuchMethodException     if there is no such accessible method
  767.      * @throws InvocationTargetException wraps an exception thrown by the method invoked
  768.      * @throws IllegalAccessException    if the requested method is not accessible via reflection
  769.      */
  770.     public static Object invokeMethod(final Object object, final String methodName, final Object arg)
  771.             throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
  772.         final Object[] args = toArray(arg);
  773.         return invokeMethod(object, methodName, args);
  774.     }

  775.     /**
  776.      * <p>
  777.      * Invoke a named method whose parameter type matches the object type.
  778.      * </p>
  779.      *
  780.      * <p>
  781.      * The behavior of this method is less deterministic than {@link #invokeExactMethod(Object object,String methodName,Object [] args)}. It loops through all
  782.      * methods with names that match and then executes the first it finds with compatible parameters.
  783.      * </p>
  784.      *
  785.      * <p>
  786.      * This method supports calls to methods taking primitive parameters via passing in wrapping classes. So, for example, a {@code Boolean} class would match a
  787.      * {@code boolean} primitive.
  788.      * </p>
  789.      *
  790.      * <p>
  791.      * This is a convenient wrapper for {@link #invokeMethod(Object object, String methodName, Object[] args, Class[] parameterTypes)}.
  792.      * </p>
  793.      *
  794.      * @param object     invoke method on this object
  795.      * @param methodName get method with this name
  796.      * @param args       use these arguments - treat null as empty array (passing null will result in calling the parameterless method with name
  797.      *                   {@code methodName}).
  798.      * @return The value returned by the invoked method
  799.      * @throws NoSuchMethodException     if there is no such accessible method
  800.      * @throws InvocationTargetException wraps an exception thrown by the method invoked
  801.      * @throws IllegalAccessException    if the requested method is not accessible via reflection
  802.      */
  803.     public static Object invokeMethod(final Object object, final String methodName, Object[] args)
  804.             throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
  805.         if (args == null) {
  806.             args = BeanUtils.EMPTY_OBJECT_ARRAY;
  807.         }
  808.         final int arguments = args.length;
  809.         final Class<?>[] parameterTypes = new Class[arguments];
  810.         for (int i = 0; i < arguments; i++) {
  811.             parameterTypes[i] = args[i].getClass();
  812.         }
  813.         return invokeMethod(object, methodName, args, parameterTypes);
  814.     }

  815.     /**
  816.      * <p>
  817.      * Invoke a named method whose parameter type matches the object type.
  818.      * </p>
  819.      *
  820.      * <p>
  821.      * The behavior of this method is less deterministic than
  822.      * {@link #invokeExactMethod(Object object, String methodName, Object[] args, Class[] parameterTypes)}. It loops through all methods with names that match
  823.      * and then executes the first it finds with compatible parameters.
  824.      * </p>
  825.      *
  826.      * <p>
  827.      * This method supports calls to methods taking primitive parameters via passing in wrapping classes. So, for example, a {@code Boolean} class would match a
  828.      * {@code boolean} primitive.
  829.      * </p>
  830.      *
  831.      *
  832.      * @param object         invoke method on this object
  833.      * @param methodName     get method with this name
  834.      * @param args           use these arguments - treat null as empty array (passing null will result in calling the parameterless method with name
  835.      *                       {@code methodName}).
  836.      * @param parameterTypes match these parameters - treat null as empty array
  837.      * @return The value returned by the invoked method
  838.      * @throws NoSuchMethodException     if there is no such accessible method
  839.      * @throws InvocationTargetException wraps an exception thrown by the method invoked
  840.      * @throws IllegalAccessException    if the requested method is not accessible via reflection
  841.      */
  842.     public static Object invokeMethod(final Object object, final String methodName, Object[] args, Class<?>[] parameterTypes)
  843.             throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
  844.         if (parameterTypes == null) {
  845.             parameterTypes = BeanUtils.EMPTY_CLASS_ARRAY;
  846.         }
  847.         if (args == null) {
  848.             args = BeanUtils.EMPTY_OBJECT_ARRAY;
  849.         }

  850.         final Method method = getMatchingAccessibleMethod(object.getClass(), methodName, parameterTypes);
  851.         if (method == null) {
  852.             throw new NoSuchMethodException("No such accessible method: " + methodName + "() on object: " + object.getClass().getName());
  853.         }
  854.         return method.invoke(object, args);
  855.     }

  856.     /**
  857.      * <p>
  858.      * Invoke a named static method whose parameter type matches the object type.
  859.      * </p>
  860.      *
  861.      * <p>
  862.      * The behavior of this method is less deterministic than {@link #invokeExactMethod(Object, String, Object[], Class[])}. It loops through all methods with
  863.      * names that match and then executes the first it finds with compatible parameters.
  864.      * </p>
  865.      *
  866.      * <p>
  867.      * This method supports calls to methods taking primitive parameters via passing in wrapping classes. So, for example, a {@code Boolean} class would match a
  868.      * {@code boolean} primitive.
  869.      * </p>
  870.      *
  871.      * <p>
  872.      * This is a convenient wrapper for {@link #invokeStaticMethod(Class objectClass,String methodName,Object [] args)}.
  873.      * </p>
  874.      *
  875.      * @param objectClass invoke static method on this class
  876.      * @param methodName  get method with this name
  877.      * @param arg         use this argument. May be null (this will result in calling the parameterless method with name {@code methodName}).
  878.      * @return The value returned by the invoked method
  879.      * @throws NoSuchMethodException     if there is no such accessible method
  880.      * @throws InvocationTargetException wraps an exception thrown by the method invoked
  881.      * @throws IllegalAccessException    if the requested method is not accessible via reflection
  882.      * @since 1.8.0
  883.      */
  884.     public static Object invokeStaticMethod(final Class<?> objectClass, final String methodName, final Object arg)
  885.             throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
  886.         final Object[] args = toArray(arg);
  887.         return invokeStaticMethod(objectClass, methodName, args);
  888.     }

  889.     /**
  890.      * <p>
  891.      * Invoke a named static method whose parameter type matches the object type.
  892.      * </p>
  893.      *
  894.      * <p>
  895.      * The behavior of this method is less deterministic than {@link #invokeExactMethod(Object object,String methodName,Object [] args)}. It loops through all
  896.      * methods with names that match and then executes the first it finds with compatible parameters.
  897.      * </p>
  898.      *
  899.      * <p>
  900.      * This method supports calls to methods taking primitive parameters via passing in wrapping classes. So, for example, a {@code Boolean} class would match a
  901.      * {@code boolean} primitive.
  902.      * </p>
  903.      *
  904.      * <p>
  905.      * This is a convenient wrapper for {@link #invokeStaticMethod(Class objectClass, String methodName, Object[] args, Class[] parameterTypes)}.
  906.      * </p>
  907.      *
  908.      * @param objectClass invoke static method on this class
  909.      * @param methodName  get method with this name
  910.      * @param args        use these arguments - treat null as empty array (passing null will result in calling the parameterless method with name
  911.      *                    {@code methodName}).
  912.      * @return The value returned by the invoked method
  913.      * @throws NoSuchMethodException     if there is no such accessible method
  914.      * @throws InvocationTargetException wraps an exception thrown by the method invoked
  915.      * @throws IllegalAccessException    if the requested method is not accessible via reflection
  916.      * @since 1.8.0
  917.      */
  918.     public static Object invokeStaticMethod(final Class<?> objectClass, final String methodName, Object[] args)
  919.             throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
  920.         if (args == null) {
  921.             args = BeanUtils.EMPTY_OBJECT_ARRAY;
  922.         }
  923.         final int arguments = args.length;
  924.         final Class<?>[] parameterTypes = new Class[arguments];
  925.         for (int i = 0; i < arguments; i++) {
  926.             parameterTypes[i] = args[i].getClass();
  927.         }
  928.         return invokeStaticMethod(objectClass, methodName, args, parameterTypes);
  929.     }

  930.     /**
  931.      * <p>
  932.      * Invoke a named static method whose parameter type matches the object type.
  933.      * </p>
  934.      *
  935.      * <p>
  936.      * The behavior of this method is less deterministic than
  937.      * {@link #invokeExactStaticMethod(Class objectClass, String methodName, Object[] args, Class[] parameterTypes)}. It loops through all methods with names
  938.      * that match and then executes the first it finds with compatible parameters.
  939.      * </p>
  940.      *
  941.      * <p>
  942.      * This method supports calls to methods taking primitive parameters via passing in wrapping classes. So, for example, a {@code Boolean} class would match a
  943.      * {@code boolean} primitive.
  944.      * </p>
  945.      *
  946.      *
  947.      * @param objectClass    invoke static method on this class
  948.      * @param methodName     get method with this name
  949.      * @param args           use these arguments - treat null as empty array (passing null will result in calling the parameterless method with name
  950.      *                       {@code methodName}).
  951.      * @param parameterTypes match these parameters - treat null as empty array
  952.      * @return The value returned by the invoked method
  953.      * @throws NoSuchMethodException     if there is no such accessible method
  954.      * @throws InvocationTargetException wraps an exception thrown by the method invoked
  955.      * @throws IllegalAccessException    if the requested method is not accessible via reflection
  956.      * @since 1.8.0
  957.      */
  958.     public static Object invokeStaticMethod(final Class<?> objectClass, final String methodName, Object[] args, Class<?>[] parameterTypes)
  959.             throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {

  960.         if (parameterTypes == null) {
  961.             parameterTypes = BeanUtils.EMPTY_CLASS_ARRAY;
  962.         }
  963.         if (args == null) {
  964.             args = BeanUtils.EMPTY_OBJECT_ARRAY;
  965.         }

  966.         final Method method = getMatchingAccessibleMethod(objectClass, methodName, parameterTypes);
  967.         if (method == null) {
  968.             throw new NoSuchMethodException("No such accessible method: " + methodName + "() on class: " + objectClass.getName());
  969.         }
  970.         return method.invoke(null, args);
  971.     }

  972.     /**
  973.      * <p>
  974.      * Determine whether a type can be used as a parameter in a method invocation. This method handles primitive conversions correctly.
  975.      * </p>
  976.      *
  977.      * <p>
  978.      * In order words, it will match a {@code Boolean</code> to a <code>boolean},
  979.      * a {@code Long</code> to a <code>long},
  980.      * a {@code Float</code> to a <code>float},
  981.      * a {@code Integer</code> to a <code>int},
  982.      * and a {@code Double</code> to a <code>double}.
  983.      * Now logic widening matches are allowed.
  984.      * For example, a {@code Long</code> will not match a <code>int}.
  985.      *
  986.      * @param parameterType    the type of parameter accepted by the method
  987.      * @param parameterization the type of parameter being tested
  988.      * @return true if the assignment is compatible.
  989.      */
  990.     public static boolean isAssignmentCompatible(final Class<?> parameterType, final Class<?> parameterization) {
  991.         // try plain assignment
  992.         if (parameterType.isAssignableFrom(parameterization)) {
  993.             return true;
  994.         }

  995.         if (parameterType.isPrimitive()) {
  996.             // this method does *not* do widening - you must specify exactly
  997.             // is this the right behavior?
  998.             final Class<?> parameterWrapperClazz = getPrimitiveWrapper(parameterType);
  999.             if (parameterWrapperClazz != null) {
  1000.                 return parameterWrapperClazz.equals(parameterization);
  1001.             }
  1002.         }

  1003.         return false;
  1004.     }

  1005.     /**
  1006.      * Sets whether methods should be cached for greater performance or not, default is {@code true}.
  1007.      *
  1008.      * @param cacheMethods {@code true} if methods should be cached for greater performance, otherwise {@code false}
  1009.      * @since 1.8.0
  1010.      */
  1011.     public static synchronized void setCacheMethods(final boolean cacheMethods) {
  1012.         CACHE_METHODS = cacheMethods;
  1013.         if (!CACHE_METHODS) {
  1014.             clearCache();
  1015.         }
  1016.     }

  1017.     /**
  1018.      * Try to make the method accessible
  1019.      *
  1020.      * @param method The source arguments
  1021.      */
  1022.     private static void setMethodAccessible(final Method method) {
  1023.         try {
  1024.             //
  1025.             // XXX Default access superclass workaround
  1026.             //
  1027.             // When a public class has a default access superclass
  1028.             // with public methods, these methods are accessible.
  1029.             // Calling them from compiled code works fine.
  1030.             //
  1031.             // Unfortunately, using reflection to invoke these methods
  1032.             // seems to (wrongly) to prevent access even when the method
  1033.             // modifier is public.
  1034.             //
  1035.             // The following workaround solves the problem but will only
  1036.             // work from sufficiently privileges code.
  1037.             //
  1038.             // Better workarounds would be gratefully accepted.
  1039.             //
  1040.             if (!method.isAccessible()) {
  1041.                 method.setAccessible(true);
  1042.             }

  1043.         } catch (final SecurityException se) {
  1044.             // log but continue just in case the method.invoke works anyway
  1045.             if (!loggedAccessibleWarning) {
  1046.                 boolean vulnerableJVM = false;
  1047.                 try {
  1048.                     final String specVersion = SystemProperties.getJavaSpecificationVersion();
  1049.                     if (specVersion.charAt(0) == '1'
  1050.                             && (specVersion.charAt(2) == '0' || specVersion.charAt(2) == '1' || specVersion.charAt(2) == '2' || specVersion.charAt(2) == '3')) {

  1051.                         vulnerableJVM = true;
  1052.                     }
  1053.                 } catch (final SecurityException e) {
  1054.                     // don't know - so display warning
  1055.                     vulnerableJVM = true;
  1056.                 }
  1057.                 if (vulnerableJVM) {
  1058.                     LOG.warn("Current Security Manager restricts use of workarounds for reflection bugs " + " in pre-1.4 JVMs.");
  1059.                 }
  1060.                 loggedAccessibleWarning = true;
  1061.             }
  1062.             LOG.debug("Cannot setAccessible on method. Therefore cannot use jvm access bug workaround.", se);
  1063.         }
  1064.     }

  1065.     private static Object[] toArray(final Object arg) {
  1066.         Object[] args = null;
  1067.         if (arg != null) {
  1068.             args = new Object[] { arg };
  1069.         }
  1070.         return args;
  1071.     }

  1072.     /**
  1073.      * Find a non primitive representation for given primitive class.
  1074.      *
  1075.      * @param clazz the class to find a representation for, not null
  1076.      * @return the original class if it not a primitive. Otherwise the wrapper class. Not null
  1077.      */
  1078.     public static Class<?> toNonPrimitiveClass(final Class<?> clazz) {
  1079.         if (clazz.isPrimitive()) {
  1080.             final Class<?> primitiveClazz = MethodUtils.getPrimitiveWrapper(clazz);
  1081.             // the above method returns
  1082.             if (primitiveClazz != null) {
  1083.                 return primitiveClazz;
  1084.             }
  1085.         }
  1086.         return clazz;
  1087.     }

  1088.     private MethodUtils() {
  1089.         // empty
  1090.     }
  1091. }