1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * https://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18 package org.apache.commons.beanutils2;
19
20 import java.lang.reflect.Array;
21 import java.lang.reflect.Constructor;
22 import java.lang.reflect.InvocationTargetException;
23 import java.lang.reflect.Modifier;
24
25 /**
26 * <p>
27 * Utility reflection methods focused on constructors, modeled after {@link MethodUtils}.
28 * </p>
29 *
30 * <h2>Known Limitations</h2>
31 * <h3>Accessing Public Constructors In A Default Access Superclass</h3>
32 * <p>
33 * There is an issue when invoking public constructors contained in a default access superclass. Reflection locates these constructors fine and correctly
34 * assigns them as public. However, an {@code IllegalAccessException} is thrown if the constructors is invoked.
35 * </p>
36 *
37 * <p>
38 * {@code ConstructorUtils} contains a workaround for this situation. It will attempt to call {@code setAccessible} on this constructor. If this call succeeds,
39 * then the method can be invoked as normal. This call will only succeed when the application has sufficient security privileges. If this call fails then a
40 * warning will be logged and the method may fail.
41 * </p>
42 */
43 public final class ConstructorUtils {
44
45 /**
46 * Returns a constructor with single argument.
47 *
48 * @param <T> the type of the constructor
49 * @param klass the class to be constructed
50 * @param parameterType The constructor parameter type
51 * @return null if matching accessible constructor cannot be found.
52 * @see Class#getConstructor
53 * @see #getAccessibleConstructor(java.lang.reflect.Constructor)
54 */
55 public static <T> Constructor<T> getAccessibleConstructor(final Class<T> klass, final Class<?> parameterType) {
56
57 final Class<?>[] parameterTypes = { parameterType };
58 return getAccessibleConstructor(klass, parameterTypes);
59 }
60
61 /**
62 * Returns a constructor given a class and signature.
63 *
64 * @param <T> the type to be constructed
65 * @param klass the class to be constructed
66 * @param parameterTypes the parameter array
67 * @return null if matching accessible constructor cannot be found
68 * @see Class#getConstructor
69 * @see #getAccessibleConstructor(java.lang.reflect.Constructor)
70 */
71 public static <T> Constructor<T> getAccessibleConstructor(final Class<T> klass, final Class<?>[] parameterTypes) {
72
73 try {
74 return getAccessibleConstructor(klass.getConstructor(parameterTypes));
75 } catch (final NoSuchMethodException e) {
76 return null;
77 }
78 }
79
80 /**
81 * Returns accessible version of the given constructor.
82 *
83 * @param <T> the type of the constructor
84 * @param ctor prototype constructor object.
85 * @return {@code null} if accessible constructor cannot be found.
86 * @see SecurityManager
87 */
88 public static <T> Constructor<T> getAccessibleConstructor(final Constructor<T> ctor) {
89
90 // Make sure we have a method to check
91 if (ctor == null) {
92 return null;
93 }
94
95 // If the requested method is not public we cannot call it
96 if (!Modifier.isPublic(ctor.getModifiers())) {
97 return null;
98 }
99
100 // If the declaring class is public, we are done
101 final Class<T> clazz = ctor.getDeclaringClass();
102 if (Modifier.isPublic(clazz.getModifiers())) {
103 return ctor;
104 }
105
106 // what else can we do?
107 return null;
108 }
109
110 /**
111 * <p>
112 * Find an accessible constructor with compatible parameters. Compatible parameters mean that every method parameter is assignable from the given
113 * parameters. In other words, it finds constructor that will take the parameters given.
114 * </p>
115 *
116 * <p>
117 * First it checks if there is constructor matching the exact signature. If no such, all the constructors of the class are tested if their signatures are
118 * assignment compatible with the parameter types. The first matching constructor is returned.
119 * </p>
120 *
121 * @param <T> the type of the class to be inspected
122 * @param clazz find constructor for this class
123 * @param parameterTypes find method with compatible parameters
124 * @return a valid Constructor object. If there's no matching constructor, returns {@code null}.
125 */
126 private static <T> Constructor<T> getMatchingAccessibleConstructor(final Class<T> clazz, 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 // modifier 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 gratefully 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.isAssignmentCompatible(ctorParams[n], parameterTypes[n])) {
168 match = false;
169 break;
170 }
171 }
172
173 if (match) {
174 // get accessible version of method
175 final Constructor<?> ctor = getAccessibleConstructor(ctor2);
176 if (ctor != null) {
177 try {
178 ctor.setAccessible(true);
179 } catch (final SecurityException se) {
180 /*
181 * Swallow SecurityException TODO: Why?
182 */
183 }
184 // Class.getConstructors() actually returns constructors
185 // of type T, so it is safe to cast.
186 return (Constructor<T>) ctor;
187 }
188 }
189 }
190 }
191
192 return null;
193 }
194
195 /**
196 * <p>
197 * Convenience method returning new instance of {@code klazz} using a single argument constructor. The formal parameter type is inferred from the actual
198 * values of {@code arg}. See {@link #invokeExactConstructor(Class, Object[], Class[])} for more details.
199 * </p>
200 *
201 * <p>
202 * The signatures should be assignment compatible.
203 * </p>
204 *
205 * @param <T> the type of the object to be constructed
206 * @param klass the class to be constructed.
207 * @param arg the actual argument. May be null (this will result in calling the default constructor).
208 * @return new instance of {@code klazz}
209 * @throws NoSuchMethodException If the constructor cannot be found
210 * @throws IllegalAccessException If an error occurs accessing the constructor
211 * @throws InvocationTargetException If an error occurs invoking the constructor
212 * @throws InstantiationException If an error occurs instantiating the class
213 * @see #invokeConstructor(Class, Object[], Class[])
214 */
215 public static <T> T invokeConstructor(final Class<T> klass, final Object arg)
216 throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
217
218 final Object[] args = toArray(arg);
219 return invokeConstructor(klass, args);
220 }
221
222 /**
223 * <p>
224 * Returns new instance of {@code klazz</code> created using the actual arguments <code>args}. The formal parameter types are inferred from the actual
225 * values of {@code args}. See {@link #invokeExactConstructor(Class, Object[], Class[])} for more details.
226 * </p>
227 *
228 * <p>
229 * The signatures should be assignment compatible.
230 * </p>
231 *
232 * @param <T> the type of the object to be constructed
233 * @param klass the class to be constructed.
234 * @param args actual argument array. May be null (this will result in calling the default constructor).
235 * @return new instance of {@code klazz}
236 * @throws NoSuchMethodException If the constructor cannot be found
237 * @throws IllegalAccessException If an error occurs accessing the constructor
238 * @throws InvocationTargetException If an error occurs invoking the constructor
239 * @throws InstantiationException If an error occurs instantiating the class
240 * @see #invokeConstructor(Class, Object[], Class[])
241 */
242 public static <T> T invokeConstructor(final Class<T> klass, Object[] args)
243 throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
244
245 if (null == args) {
246 args = BeanUtils.EMPTY_OBJECT_ARRAY;
247 }
248 final int arguments = args.length;
249 final Class<?>[] parameterTypes = new Class<?>[arguments];
250 for (int i = 0; i < arguments; i++) {
251 parameterTypes[i] = args[i].getClass();
252 }
253 return invokeConstructor(klass, args, parameterTypes);
254 }
255
256 /**
257 * <p>
258 * Returns new instance of {@code klazz} created using constructor with signature {@code parameterTypes</code> and actual arguments <code>args}.
259 * </p>
260 *
261 * <p>
262 * The signatures should be assignment compatible.
263 * </p>
264 *
265 * @param <T> the type of the object to be constructed
266 * @param klass the class to be constructed.
267 * @param args actual argument array. May be null (this will result in calling the default constructor).
268 * @param parameterTypes parameter types array
269 * @return new instance of {@code klazz}
270 * @throws NoSuchMethodException if matching constructor cannot be found
271 * @throws IllegalAccessException thrown on the constructor's invocation
272 * @throws InvocationTargetException thrown on the constructor's invocation
273 * @throws InstantiationException thrown on the constructor's invocation
274 * @see Constructor#newInstance
275 */
276 public static <T> T invokeConstructor(final Class<T> klass, Object[] args, Class<?>[] parameterTypes)
277 throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
278
279 if (parameterTypes == null) {
280 parameterTypes = BeanUtils.EMPTY_CLASS_ARRAY;
281 }
282 if (args == null) {
283 args = BeanUtils.EMPTY_OBJECT_ARRAY;
284 }
285
286 final Constructor<T> ctor = getMatchingAccessibleConstructor(klass, parameterTypes);
287 if (null == ctor) {
288 throw new NoSuchMethodException("No such accessible constructor on object: " + klass.getName());
289 }
290 return ctor.newInstance(args);
291 }
292
293 /**
294 * <p>
295 * Convenience method returning new instance of {@code klazz} using a single argument constructor. The formal parameter type is inferred from the actual
296 * values of {@code arg}. See {@link #invokeExactConstructor(Class, Object[], Class[])} for more details.
297 * </p>
298 *
299 * <p>
300 * The signatures should match exactly.
301 * </p>
302 *
303 * @param <T> the type of the object to be constructed
304 * @param klass the class to be constructed.
305 * @param arg the actual argument. May be null (this will result in calling the default constructor).
306 * @return new instance of {@code klazz}
307 * @throws NoSuchMethodException If the constructor cannot be found
308 * @throws IllegalAccessException If an error occurs accessing the constructor
309 * @throws InvocationTargetException If an error occurs invoking the constructor
310 * @throws InstantiationException If an error occurs instantiating the class
311 * @see #invokeExactConstructor(Class, Object[], Class[])
312 */
313 public static <T> T invokeExactConstructor(final Class<T> klass, final Object arg)
314 throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
315
316 final Object[] args = toArray(arg);
317 return invokeExactConstructor(klass, args);
318 }
319
320 /**
321 * <p>
322 * Returns new instance of {@code klazz</code> created using the actual arguments <code>args}. The formal parameter types are inferred from the actual
323 * values of {@code args}. See {@link #invokeExactConstructor(Class, Object[], Class[])} for more details.
324 * </p>
325 *
326 * <p>
327 * The signatures should match exactly.
328 * </p>
329 *
330 * @param <T> the type of the object to be constructed
331 * @param klass the class to be constructed.
332 * @param args actual argument array. May be null (this will result in calling the default constructor).
333 * @return new instance of {@code klazz}
334 * @throws NoSuchMethodException If the constructor cannot be found
335 * @throws IllegalAccessException If an error occurs accessing the constructor
336 * @throws InvocationTargetException If an error occurs invoking the constructor
337 * @throws InstantiationException If an error occurs instantiating the class
338 * @see #invokeExactConstructor(Class, Object[], Class[])
339 */
340 public static <T> T invokeExactConstructor(final Class<T> klass, Object[] args)
341 throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
342
343 if (null == args) {
344 args = BeanUtils.EMPTY_OBJECT_ARRAY;
345 }
346 final int arguments = args.length;
347 final Class<?>[] parameterTypes = new Class[arguments];
348 for (int i = 0; i < arguments; i++) {
349 parameterTypes[i] = args[i].getClass();
350 }
351 return invokeExactConstructor(klass, args, parameterTypes);
352 }
353
354 /**
355 * <p>
356 * Returns new instance of {@code klazz} created using constructor with signature {@code parameterTypes} and actual arguments {@code args}.
357 * </p>
358 *
359 * <p>
360 * The signatures should match exactly.
361 * </p>
362 *
363 * @param <T> the type of the object to be constructed
364 * @param klass the class to be constructed.
365 * @param args actual argument array. May be null (this will result in calling the default constructor).
366 * @param parameterTypes parameter types array
367 * @return new instance of {@code klazz}
368 * @throws NoSuchMethodException if matching constructor cannot be found
369 * @throws IllegalAccessException thrown on the constructor's invocation
370 * @throws InvocationTargetException thrown on the constructor's invocation
371 * @throws InstantiationException thrown on the constructor's invocation
372 * @see Constructor#newInstance
373 */
374 public static <T> T invokeExactConstructor(final Class<T> klass, Object[] args, Class<?>[] parameterTypes)
375 throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
376
377 if (args == null) {
378 args = BeanUtils.EMPTY_OBJECT_ARRAY;
379 }
380
381 if (parameterTypes == null) {
382 parameterTypes = BeanUtils.EMPTY_CLASS_ARRAY;
383 }
384
385 final Constructor<T> ctor = getAccessibleConstructor(klass, parameterTypes);
386 if (null == ctor) {
387 throw new NoSuchMethodException("No such accessible constructor on object: " + klass.getName());
388 }
389 return ctor.newInstance(args);
390 }
391
392 /**
393 * Delegates to {@link Array#newInstance(Class, int)}.
394 *
395 * @param <T> Component type.
396 * @param componentType See {@link Array#newInstance(Class, int)}.
397 * @param length See {@link Array#newInstance(Class, int)}.
398 * @return See {@link Array#newInstance(Class, int)}.
399 */
400 @SuppressWarnings("unchecked")
401 public static <T> T[] newArray(final Class<T> componentType, final int length) {
402 return (T[]) Array.newInstance(componentType, length);
403 }
404
405 private static Object[] toArray(final Object arg) {
406 Object[] args = null;
407 if (arg != null) {
408 args = new Object[] { arg };
409 }
410 return args;
411 }
412
413 private ConstructorUtils() {
414 // empty
415 }
416 }