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