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