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