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