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.reflect.Constructor; 021import java.lang.reflect.InvocationTargetException; 022import java.lang.reflect.Modifier; 023 024/** 025 * <p>Utility reflection methods focused on constructors, modeled after {@link MethodUtils}.</p> 026 * 027 * <strong>Known Limitations: Accessing Public Constructors In A Default Access Superclass</strong> 028 * <p>There is an issue when invoking public constructors contained in a default access superclass. 029 * Reflection locates these constructors fine and correctly assigns them as public. 030 * However, an <code>IllegalAccessException</code> is thrown if the constructors is invoked.</p> 031 * 032 * <p><code>ConstructorUtils</code> contains a workaround for this situation. 033 * It will attempt to call <code>setAccessible</code> on this constructor. 034 * If this call succeeds, then the method can be invoked as normal. 035 * This call will only succeed when the application has sufficient security privileges. 036 * If this call fails then a warning will be logged and the method may fail.</p> 037 */ 038public class ConstructorUtils { 039 040 /** An empty class array */ 041 private static final Class<?>[] EMPTY_CLASS_PARAMETERS = {}; 042 /** An empty object array */ 043 private static final Object[] EMPTY_OBJECT_ARRAY = {}; 044 045 /** 046 * Returns a constructor with single argument. 047 * @param <T> the type of the constructor 048 * @param klass the class to be constructed 049 * @param parameterType The constructor parameter type 050 * @return null if matching accessible constructor cannot be found. 051 * @see Class#getConstructor 052 * @see #getAccessibleConstructor(java.lang.reflect.Constructor) 053 */ 054 public static <T> Constructor<T> getAccessibleConstructor( 055 final Class<T> klass, 056 final Class<?> parameterType) { 057 058 final Class<?>[] parameterTypes = { parameterType }; 059 return getAccessibleConstructor(klass, parameterTypes); 060 } 061 062 /** 063 * Returns a constructor given a class and signature. 064 * @param <T> the type to be constructed 065 * @param klass the class to be constructed 066 * @param parameterTypes the parameter array 067 * @return null if matching accessible constructor cannot be found 068 * @see Class#getConstructor 069 * @see #getAccessibleConstructor(java.lang.reflect.Constructor) 070 */ 071 public static <T> Constructor<T> getAccessibleConstructor( 072 final Class<T> klass, 073 final Class<?>[] parameterTypes) { 074 075 try { 076 return getAccessibleConstructor( 077 klass.getConstructor(parameterTypes)); 078 } catch (final NoSuchMethodException e) { 079 return null; 080 } 081 } 082 083 /** 084 * Returns accessible version of the given constructor. 085 * @param <T> the type of the constructor 086 * @param ctor prototype constructor object. 087 * @return <code>null</code> if accessible constructor cannot be found. 088 * @see java.lang.SecurityManager 089 */ 090 public static <T> Constructor<T> getAccessibleConstructor(final Constructor<T> ctor) { 091 092 // Make sure we have a method to check 093 // If the requested method is not public we cannot call it 094 if (ctor == null || !Modifier.isPublic(ctor.getModifiers())) { 095 return null; 096 } 097 098 // If the declaring class is public, we are done 099 final Class<T> clazz = ctor.getDeclaringClass(); 100 if (Modifier.isPublic(clazz.getModifiers())) { 101 return ctor; 102 } 103 104 // what else can we do? 105 return null; 106 } 107 108 /** 109 * <p>Find an accessible constructor with compatible parameters. 110 * Compatible parameters mean that every method parameter is assignable from 111 * the given parameters. In other words, it finds constructor that will take 112 * the parameters given.</p> 113 * 114 * <p>First it checks if there is constructor matching the exact signature. 115 * If no such, all the constructors of the class are tested if their signatures 116 * are assignment compatible with the parameter types. 117 * The first matching constructor is returned.</p> 118 * 119 * @param <T> the type of the class to be inspected 120 * @param clazz find constructor for this class 121 * @param parameterTypes find method with compatible parameters 122 * @return a valid Constructor object. If there's no matching constructor, returns <code>null</code>. 123 */ 124 private static <T> Constructor<T> getMatchingAccessibleConstructor( 125 final Class<T> clazz, 126 final Class<?>[] parameterTypes) { 127 // see if we can find the method directly 128 // most of the time this works and it's much faster 129 try { 130 final Constructor<T> ctor = clazz.getConstructor(parameterTypes); 131 try { 132 // 133 // XXX Default access superclass workaround 134 // 135 // When a public class has a default access superclass 136 // with public methods, these methods are accessible. 137 // Calling them from compiled code works fine. 138 // 139 // Unfortunately, using reflection to invoke these methods 140 // seems to (wrongly) to prevent access even when the method 141 // modifer is public. 142 // 143 // The following workaround solves the problem but will only 144 // work from sufficiently privileges code. 145 // 146 // Better workarounds would be greatfully accepted. 147 // 148 ctor.setAccessible(true); 149 } catch (final SecurityException se) { 150 /* SWALLOW, if workaround fails don't fret. */ 151 } 152 return ctor; 153 154 } catch (final NoSuchMethodException e) { /* SWALLOW */ 155 } 156 157 // search through all methods 158 final int paramSize = parameterTypes.length; 159 final Constructor<?>[] ctors = clazz.getConstructors(); 160 for (final Constructor<?> ctor2 : ctors) { 161 // compare parameters 162 final Class<?>[] ctorParams = ctor2.getParameterTypes(); 163 final int ctorParamSize = ctorParams.length; 164 if (ctorParamSize == paramSize) { 165 boolean match = true; 166 for (int n = 0; n < ctorParamSize; n++) { 167 if (!MethodUtils 168 .isAssignmentCompatible( 169 ctorParams[n], 170 parameterTypes[n])) { 171 match = false; 172 break; 173 } 174 } 175 176 if (match) { 177 // get accessible version of method 178 final Constructor<?> ctor = getAccessibleConstructor(ctor2); 179 if (ctor != null) { 180 try { 181 ctor.setAccessible(true); 182 } catch (final SecurityException se) { 183 /* Swallow SecurityException 184 * TODO: Why? 185 */ 186 } 187 @SuppressWarnings("unchecked") 188 final 189 // Class.getConstructors() actually returns constructors 190 // of type T, so it is safe to cast. 191 Constructor<T> typedCtor = (Constructor<T>) ctor; 192 return typedCtor; 193 } 194 } 195 } 196 } 197 198 return null; 199 } 200 201 /** 202 * <p>Convenience method returning new instance of <code>klazz</code> using a single argument constructor. 203 * The formal parameter type is inferred from the actual values of <code>arg</code>. 204 * See {@link #invokeExactConstructor(Class, Object[], Class[])} for more details.</p> 205 * 206 * <p>The signatures should be assignment compatible.</p> 207 * 208 * @param <T> the type of the object to be constructed 209 * @param klass the class to be constructed. 210 * @param arg the actual argument. May be null (this will result in calling the default constructor). 211 * @return new instance of <code>klazz</code> 212 * @throws NoSuchMethodException If the constructor cannot be found 213 * @throws IllegalAccessException If an error occurs accessing the constructor 214 * @throws InvocationTargetException If an error occurs invoking the constructor 215 * @throws InstantiationException If an error occurs instantiating the class 216 * @see #invokeConstructor(java.lang.Class, java.lang.Object[], java.lang.Class[]) 217 */ 218 public static <T> T invokeConstructor(final Class<T> klass, final Object arg) 219 throws 220 NoSuchMethodException, 221 IllegalAccessException, 222 InvocationTargetException, 223 InstantiationException { 224 225 final Object[] args = toArray(arg); 226 return invokeConstructor(klass, args); 227 } 228 229 /** 230 * <p>Returns new instance of <code>klazz</code> created using the actual arguments <code>args</code>. 231 * The formal parameter types are inferred from the actual values of <code>args</code>. 232 * See {@link #invokeExactConstructor(Class, Object[], Class[])} for more details.</p> 233 * 234 * <p>The signatures should be assignment compatible.</p> 235 * 236 * @param <T> the type of the object to be constructed 237 * @param klass the class to be constructed. 238 * @param args actual argument array. May be null (this will result in calling the default constructor). 239 * @return new instance of <code>klazz</code> 240 * @throws NoSuchMethodException If the constructor cannot be found 241 * @throws IllegalAccessException If an error occurs accessing the constructor 242 * @throws InvocationTargetException If an error occurs invoking the constructor 243 * @throws InstantiationException If an error occurs instantiating the class 244 * @see #invokeConstructor(java.lang.Class, java.lang.Object[], java.lang.Class[]) 245 */ 246 public static <T> T invokeConstructor(final Class<T> klass, Object[] args) 247 throws 248 NoSuchMethodException, 249 IllegalAccessException, 250 InvocationTargetException, 251 InstantiationException { 252 253 if (null == args) { 254 args = EMPTY_OBJECT_ARRAY; 255 } 256 final int arguments = args.length; 257 final Class<?> parameterTypes[] = new Class<?>[arguments]; 258 for (int i = 0; i < arguments; i++) { 259 parameterTypes[i] = args[i].getClass(); 260 } 261 return invokeConstructor(klass, args, parameterTypes); 262 } 263 264 /** 265 * <p>Returns new instance of <code>klazz</code> created using constructor 266 * with signature <code>parameterTypes</code> and actual arguments <code>args</code>.</p> 267 * 268 * <p>The signatures should be assignment compatible.</p> 269 * 270 * @param <T> the type of the object to be constructed 271 * @param klass the class to be constructed. 272 * @param args actual argument array. May be null (this will result in calling the default constructor). 273 * @param parameterTypes parameter types array 274 * @return new instance of <code>klazz</code> 275 * @throws NoSuchMethodException if matching constructor cannot be found 276 * @throws IllegalAccessException thrown on the constructor's invocation 277 * @throws InvocationTargetException thrown on the constructor's invocation 278 * @throws InstantiationException thrown on the constructor's invocation 279 * @see Constructor#newInstance 280 */ 281 public static <T> T invokeConstructor( 282 final Class<T> klass, 283 Object[] args, 284 Class<?>[] parameterTypes) 285 throws 286 NoSuchMethodException, 287 IllegalAccessException, 288 InvocationTargetException, 289 InstantiationException { 290 291 if (parameterTypes == null) { 292 parameterTypes = EMPTY_CLASS_PARAMETERS; 293 } 294 if (args == null) { 295 args = EMPTY_OBJECT_ARRAY; 296 } 297 298 final Constructor<T> ctor = 299 getMatchingAccessibleConstructor(klass, parameterTypes); 300 if (null == ctor) { 301 throw new NoSuchMethodException( 302 "No such accessible constructor on object: " + klass.getName()); 303 } 304 return ctor.newInstance(args); 305 } 306 307 /** 308 * <p>Convenience method returning new instance of <code>klazz</code> using a single argument constructor. 309 * The formal parameter type is inferred from the actual values of <code>arg</code>. 310 * See {@link #invokeExactConstructor(Class, Object[], Class[])} for more details.</p> 311 * 312 * <p>The signatures should match exactly.</p> 313 * 314 * @param <T> the type of the object to be constructed 315 * @param klass the class to be constructed. 316 * @param arg the actual argument. May be null (this will result in calling the default constructor). 317 * @return new instance of <code>klazz</code> 318 * @throws NoSuchMethodException If the constructor cannot be found 319 * @throws IllegalAccessException If an error occurs accessing the constructor 320 * @throws InvocationTargetException If an error occurs invoking the constructor 321 * @throws InstantiationException If an error occurs instantiating the class 322 * @see #invokeExactConstructor(java.lang.Class, java.lang.Object[], java.lang.Class[]) 323 */ 324 public static <T> T invokeExactConstructor(final Class<T> klass, final Object arg) 325 throws 326 NoSuchMethodException, 327 IllegalAccessException, 328 InvocationTargetException, 329 InstantiationException { 330 331 final Object[] args = toArray(arg); 332 return invokeExactConstructor(klass, args); 333 } 334 335 /** 336 * <p>Returns new instance of <code>klazz</code> created using the actual arguments <code>args</code>. 337 * The formal parameter types are inferred from the actual values of <code>args</code>. 338 * See {@link #invokeExactConstructor(Class, Object[], Class[])} for more details.</p> 339 * 340 * <p>The signatures should match exactly.</p> 341 * 342 * @param <T> the type of the object to be constructed 343 * @param klass the class to be constructed. 344 * @param args actual argument array. May be null (this will result in calling the default constructor). 345 * @return new instance of <code>klazz</code> 346 * @throws NoSuchMethodException If the constructor cannot be found 347 * @throws IllegalAccessException If an error occurs accessing the constructor 348 * @throws InvocationTargetException If an error occurs invoking the constructor 349 * @throws InstantiationException If an error occurs instantiating the class 350 * @see #invokeExactConstructor(java.lang.Class, java.lang.Object[], java.lang.Class[]) 351 */ 352 public static <T> T invokeExactConstructor(final Class<T> klass, Object[] args) 353 throws 354 NoSuchMethodException, 355 IllegalAccessException, 356 InvocationTargetException, 357 InstantiationException { 358 359 if (null == args) { 360 args = EMPTY_OBJECT_ARRAY; 361 } 362 final int arguments = args.length; 363 final Class<?> parameterTypes[] = new Class[arguments]; 364 for (int i = 0; i < arguments; i++) { 365 parameterTypes[i] = args[i].getClass(); 366 } 367 return invokeExactConstructor(klass, args, parameterTypes); 368 } 369 370 /** 371 * <p>Returns new instance of <code>klazz</code> created using constructor 372 * with signature <code>parameterTypes</code> and actual arguments 373 * <code>args</code>.</p> 374 * 375 * <p>The signatures should match exactly.</p> 376 * 377 * @param <T> the type of the object to be constructed 378 * @param klass the class to be constructed. 379 * @param args actual argument array. May be null (this will result in calling the default constructor). 380 * @param parameterTypes parameter types array 381 * @return new instance of <code>klazz</code> 382 * @throws NoSuchMethodException if matching constructor cannot be found 383 * @throws IllegalAccessException thrown on the constructor's invocation 384 * @throws InvocationTargetException thrown on the constructor's invocation 385 * @throws InstantiationException thrown on the constructor's invocation 386 * @see Constructor#newInstance 387 */ 388 public static <T> T invokeExactConstructor( 389 final Class<T> klass, 390 Object[] args, 391 Class<?>[] parameterTypes) 392 throws 393 NoSuchMethodException, 394 IllegalAccessException, 395 InvocationTargetException, 396 InstantiationException { 397 398 if (args == null) { 399 args = EMPTY_OBJECT_ARRAY; 400 } 401 402 if (parameterTypes == null) { 403 parameterTypes = EMPTY_CLASS_PARAMETERS; 404 } 405 406 final Constructor<T> ctor = getAccessibleConstructor(klass, parameterTypes); 407 if (null == ctor) { 408 throw new NoSuchMethodException( 409 "No such accessible constructor on object: " + klass.getName()); 410 } 411 return ctor.newInstance(args); 412 } 413 414 private static Object[] toArray(final Object arg) { 415 Object[] args = null; 416 if (arg != null) { 417 args = new Object[] { arg }; 418 } 419 return args; 420 } 421 422 /** 423 * Deprecated, all methods are static. 424 * 425 * @deprecated Will be private in 2.0. 426 */ 427 @Deprecated 428 public ConstructorUtils() { 429 // empty 430 } 431 432}