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