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