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