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    }