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