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