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