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