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.lang3.reflect;
19
20 import java.lang.reflect.Constructor;
21 import java.lang.reflect.InvocationTargetException;
22 import java.util.Objects;
23
24 import org.apache.commons.lang3.ArrayUtils;
25 import org.apache.commons.lang3.ClassUtils;
26
27 /**
28 * Utility reflection methods focused on constructors, modeled after {@link MethodUtils}.
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 {@code public} constructors contained in a default access superclass. Reflection correctly locates these constructors and
34 * assigns them as {@code public}. However, an {@link IllegalAccessException} is thrown if the constructor is invoked.
35 * </p>
36 *
37 * <p>
38 * {@link ConstructorUtils} contains a workaround for this situation: it will attempt to call {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)}
39 * on this constructor. If this call succeeds, then the method can be invoked as normal. This call will only succeed when the application has sufficient
40 * security privileges. If this call fails then a warning will be logged and the method may fail.
41 * </p>
42 *
43 * @since 2.5
44 */
45 public class ConstructorUtils {
46
47 /**
48 * Finds a constructor given a class and signature, checking accessibility.
49 *
50 * <p>
51 * This finds the constructor and ensures that it is accessible. The constructor signature must match the parameter types exactly.
52 * </p>
53 *
54 * @param <T> the constructor type.
55 * @param cls the class to find a constructor for, not {@code null}.
56 * @param parameterTypes the array of parameter types, {@code null} treated as empty.
57 * @return the constructor, {@code null} if no matching accessible constructor found.
58 * @throws NullPointerException if {@code cls} is {@code null}
59 * @throws SecurityException Thrown if a security manager is present and the caller's class loader is not the same as or an ancestor of the class loader
60 * for the class and invocation of {@link SecurityManager#checkPackageAccess(String)} denies access to the package of the
61 * class.
62 * @see Class#getConstructor
63 * @see #getAccessibleConstructor(java.lang.reflect.Constructor)
64 */
65 public static <T> Constructor<T> getAccessibleConstructor(final Class<T> cls, final Class<?>... parameterTypes) {
66 Objects.requireNonNull(cls, "cls");
67 try {
68 return getAccessibleConstructor(cls.getConstructor(parameterTypes));
69 } catch (final NoSuchMethodException e) {
70 return null;
71 }
72 }
73
74 /**
75 * Checks if the specified constructor is accessible.
76 *
77 * <p>
78 * This simply ensures that the constructor is accessible.
79 * </p>
80 *
81 * @param <T> the constructor type.
82 * @param ctor the prototype constructor object, not {@code null}.
83 * @return the constructor, {@code null} if no matching accessible constructor found.
84 * @see SecurityManager
85 * @throws NullPointerException if {@code ctor} is {@code null}
86 * @throws SecurityException Thrown if a security manager is present and a caller's class loader is not the same as or an ancestor of the class loader
87 * for a class and invocation of {@link SecurityManager#checkPackageAccess(String)} denies access to the package of the class.
88 */
89 public static <T> Constructor<T> getAccessibleConstructor(final Constructor<T> ctor) {
90 Objects.requireNonNull(ctor, "ctor");
91 return MemberUtils.isAccessible(ctor) && isAccessible(ctor.getDeclaringClass()) ? ctor : null;
92 }
93
94 /**
95 * Finds an accessible constructor with compatible parameters.
96 *
97 * <p>
98 * This checks all the constructor and finds one with compatible parameters This requires that every parameter is assignable from the given parameter types.
99 * This is a more flexible search than the normal exact matching algorithm.
100 * </p>
101 * <p>
102 * First it checks if there is a constructor matching the exact signature. If not then all the constructors of the class are checked to see if their
103 * signatures are assignment-compatible with the parameter types. The first assignment-compatible matching constructor is returned.
104 * </p>
105 *
106 * @param <T> the constructor type.
107 * @param cls the class to find a constructor for, not {@code null}.
108 * @param parameterTypes find method with compatible parameters.
109 * @return the constructor, null if no matching accessible constructor found.
110 * @throws NullPointerException Thrown if {@code cls} is {@code null}
111 * @throws SecurityException Thrown if a security manager is present and the caller's class loader is not the same as or an ancestor of the class loader for the
112 * class and invocation of {@link SecurityManager#checkPackageAccess(String)} denies access to the package of the class.
113 * @see SecurityManager#checkPackageAccess(String)
114 */
115 public static <T> Constructor<T> getMatchingAccessibleConstructor(final Class<T> cls, final Class<?>... parameterTypes) {
116 Objects.requireNonNull(cls, "cls");
117 // see if we can find the constructor directly
118 // most of the time this works and it's much faster
119 try {
120 return MemberUtils.setAccessibleWorkaround(cls.getConstructor(parameterTypes));
121 } catch (final NoSuchMethodException ignored) {
122 // ignore
123 }
124 Constructor<T> result = null;
125 /*
126 * (1) Class.getConstructors() is documented to return Constructor<T> so as long as the array is not subsequently modified, everything's fine.
127 */
128 final Constructor<?>[] ctors = cls.getConstructors();
129 // return best match:
130 for (Constructor<?> ctor : ctors) {
131 // compare parameters
132 if (MemberUtils.isMatchingConstructor(ctor, parameterTypes)) {
133 // get accessible version of constructor
134 ctor = getAccessibleConstructor(ctor);
135 if (ctor != null) {
136 MemberUtils.setAccessibleWorkaround(ctor);
137 if (result == null || MemberUtils.compareConstructorFit(ctor, result, parameterTypes) < 0) {
138 // temporary variable for annotation, see comment above (1)
139 @SuppressWarnings("unchecked")
140 final Constructor<T> constructor = (Constructor<T>) ctor;
141 result = constructor;
142 }
143 }
144 }
145 }
146 return result;
147 }
148
149 /**
150 * Returns a new instance of the specified class inferring the right constructor from the types of the arguments.
151 *
152 * <p>
153 * This locates and calls a constructor. The constructor signature must match the argument types by assignment compatibility.
154 * </p>
155 *
156 * @param <T> the type to be constructed.
157 * @param cls the class to be constructed, not {@code null}.
158 * @param args the array of arguments, {@code null} treated as empty.
159 * @return new instance of {@code cls}, not {@code null}.
160 * @throws NullPointerException Thrown if {@code cls} is {@code null}.
161 * @throws NoSuchMethodException Thrown if a matching constructor cannot be found.
162 * @throws IllegalAccessException Thrown if the found {@code Constructor} is enforcing Java language access control and the underlying constructor is
163 * inaccessible.
164 * @throws IllegalArgumentException Thrown if:
165 * <ul>
166 * <li>the number of actual and formal parameters differ; or</li>
167 * <li>an unwrapping conversion for primitive arguments fails; or</li>
168 * <li>after possible unwrapping, a parameter value cannot be converted to the corresponding formal parameter type by a
169 * method invocation conversion; if this constructor pertains to an enum type.
170 * </ul>
171 * @throws InstantiationException Thrown if the class that declares the underlying constructor represents an abstract class.
172 * @throws InvocationTargetException Thrown if the underlying constructor throws an exception.
173 * @throws ExceptionInInitializerError Thrown if the initialization provoked by this method fails.
174 * @throws SecurityException Thrown if a security manager is present and the caller's class loader is not the same as or an ancestor of the class
175 * loader for the class and invocation of {@link SecurityManager#checkPackageAccess(String)} denies access to the
176 * package of the class.
177 * @see #invokeConstructor(Class, Object[], Class[])
178 */
179 public static <T> T invokeConstructor(final Class<T> cls, final Object... args)
180 throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
181 final Object[] actuals = ArrayUtils.nullToEmpty(args);
182 return invokeConstructor(cls, actuals, ClassUtils.toClass(actuals));
183 }
184
185 /**
186 * Returns a new instance of the specified class choosing the right constructor from the list of parameter types.
187 *
188 * <p>
189 * This locates and calls a constructor. The constructor signature must match the parameter types by assignment compatibility.
190 * </p>
191 *
192 * @param <T> the type to be constructed.
193 * @param cls the class to be constructed, not {@code null}.
194 * @param args the array of arguments, {@code null} treated as empty.
195 * @param parameterTypes the array of parameter types, {@code null} treated as empty.
196 * @return new instance of {@code cls}, not {@code null}
197 * @throws NullPointerException Thrown if {@code cls} is {@code null}.
198 * @throws NoSuchMethodException Thrown if a matching constructor cannot be found.
199 * @throws IllegalAccessException Thrown if the found {@code Constructor} is enforcing Java language access control and the underlying constructor is
200 * inaccessible.
201 * @throws IllegalArgumentException Thrown if:
202 * <ul>
203 * <li>the number of actual and formal parameters differ; or</li>
204 * <li>an unwrapping conversion for primitive arguments fails; or</li>
205 * <li>after possible unwrapping, a parameter value cannot be converted to the corresponding formal parameter type by a
206 * method invocation conversion; if this constructor pertains to an enum type.
207 * </ul>
208 * @throws InstantiationException Thrown if the class that declares the underlying constructor represents an abstract class.
209 * @throws InvocationTargetException Thrown if the underlying constructor throws an exception.
210 * @throws ExceptionInInitializerError Thrown if the initialization provoked by this method fails.
211 * @throws SecurityException Thrown if a security manager is present and the caller's class loader is not the same as or an ancestor of the class
212 * loader for the class and invocation of {@link SecurityManager#checkPackageAccess(String)} denies access to the
213 * package of the class.
214 * @see Constructor#newInstance(Object...)
215 * @see Constructor#newInstance
216 */
217 public static <T> T invokeConstructor(final Class<T> cls, final Object[] args, final Class<?>[] parameterTypes)
218 throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
219 final Object[] actuals = ArrayUtils.nullToEmpty(args);
220 final Constructor<T> ctor = getMatchingAccessibleConstructor(cls, ArrayUtils.nullToEmpty(parameterTypes));
221 if (ctor == null) {
222 throw new NoSuchMethodException("No such accessible constructor on object: " + cls.getName());
223 }
224 return ctor.newInstance(MethodUtils.toVarArgs(ctor, actuals));
225 }
226
227 /**
228 * Returns a new instance of the specified class inferring the right constructor from the types of the arguments.
229 *
230 * <p>
231 * This locates and calls a constructor. The constructor signature must match the argument types exactly.
232 * </p>
233 *
234 * @param <T> the type to be constructed.
235 * @param cls the class to be constructed, not {@code null}.
236 * @param args the array of arguments, {@code null} treated as empty.
237 * @return new instance of {@code cls}, not {@code null}.
238 * @throws NullPointerException Thrown if {@code cls} is {@code null}.
239 * @throws NoSuchMethodException Thrown if a matching constructor cannot be found.
240 * @throws IllegalAccessException Thrown if the found {@code Constructor} is enforcing Java language access control and the underlying constructor is
241 * inaccessible.
242 * @throws IllegalArgumentException Thrown if:
243 * <ul>
244 * <li>the number of actual and formal parameters differ; or</li>
245 * <li>an unwrapping conversion for primitive arguments fails; or</li>
246 * <li>after possible unwrapping, a parameter value cannot be converted to the corresponding formal parameter type by a
247 * method invocation conversion; if this constructor pertains to an enum type.
248 * </ul>
249 * @throws InstantiationException Thrown if the class that declares the underlying constructor represents an abstract class.
250 * @throws InvocationTargetException Thrown if the underlying constructor throws an exception.
251 * @throws ExceptionInInitializerError Thrown if the initialization provoked by this method fails.
252 * @throws SecurityException Thrown if a security manager is present and the caller's class loader is not the same as or an ancestor of the class
253 * loader for the class and invocation of {@link SecurityManager#checkPackageAccess(String)} denies access to the
254 * package of the class.
255 * @see Constructor#newInstance(Object...)
256 * @see #invokeExactConstructor(Class, Object[], Class[])
257 */
258 public static <T> T invokeExactConstructor(final Class<T> cls, final Object... args)
259 throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
260 final Object[] actuals = ArrayUtils.nullToEmpty(args);
261 return invokeExactConstructor(cls, actuals, ClassUtils.toClass(actuals));
262 }
263
264 /**
265 * Returns a new instance of the specified class choosing the right constructor from the list of parameter types.
266 *
267 * <p>
268 * This locates and calls a constructor. The constructor signature must match the parameter types exactly.
269 * </p>
270 *
271 * @param <T> the type to construct.
272 * @param cls the class to construct, not {@code null}.
273 * @param args the array of arguments, {@code null} treated as empty.
274 * @param parameterTypes the array of parameter types, {@code null} treated as empty.
275 * @return new instance of {@code cls}, not {@code null}.
276 * @throws NullPointerException Thrown if {@code cls} is {@code null}.
277 * @throws NoSuchMethodException Thrown if a matching constructor cannot be found.
278 * @throws IllegalAccessException Thrown if the found {@code Constructor} is enforcing Java language access control and the underlying constructor is
279 * inaccessible.
280 * @throws IllegalArgumentException Thrown if:
281 * <ul>
282 * <li>the number of actual and formal parameters differ; or</li>
283 * <li>an unwrapping conversion for primitive arguments fails; or</li>
284 * <li>after possible unwrapping, a parameter value cannot be converted to the corresponding formal parameter type by a
285 * method invocation conversion; if this constructor pertains to an enum type.
286 * </ul>
287 * @throws InstantiationException Thrown if the class that declares the underlying constructor represents an abstract class.
288 * @throws InvocationTargetException Thrown if the underlying constructor throws an exception.
289 * @throws ExceptionInInitializerError Thrown if the initialization provoked by this method fails.
290 * @throws SecurityException Thrown if a security manager is present and the caller's class loader is not the same as or an ancestor of the class
291 * loader for the class and invocation of {@link SecurityManager#checkPackageAccess(String)} denies access to the
292 * package of the class.
293 * @see Constructor#newInstance(Object...)
294 */
295 public static <T> T invokeExactConstructor(final Class<T> cls, final Object[] args, final Class<?>[] parameterTypes)
296 throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
297 final Constructor<T> ctor = getAccessibleConstructor(cls, ArrayUtils.nullToEmpty(parameterTypes));
298 if (ctor == null) {
299 throw new NoSuchMethodException("No such accessible constructor on object: " + cls.getName());
300 }
301 return ctor.newInstance(ArrayUtils.nullToEmpty(args));
302 }
303
304 /**
305 * Tests whether the specified class is generally accessible, i.e. is declared in an entirely {@code public} manner.
306 *
307 * @param type to check.
308 * @return {@code true} if {@code type} and any enclosing classes are {@code public}.
309 * @throws SecurityException Thrown if a security manager is present and a caller's class loader is not the same as or an ancestor of the class loader for a
310 * class and invocation of {@link SecurityManager#checkPackageAccess(String)} denies access to the package of the class.
311 */
312 private static boolean isAccessible(final Class<?> type) {
313 Class<?> cls = type;
314 while (cls != null) {
315 if (!ClassUtils.isPublic(cls)) {
316 return false;
317 }
318 cls = cls.getEnclosingClass();
319 }
320 return true;
321 }
322
323 /**
324 * ConstructorUtils instances should NOT be constructed in standard programming. Instead, the class should be used as
325 * {@code ConstructorUtils.invokeConstructor(cls, args)}.
326 *
327 * <p>
328 * This constructor is {@code public} to permit tools that require a JavaBean instance to operate.
329 * </p>
330 *
331 * @deprecated TODO Make private in 4.0.
332 */
333 @Deprecated
334 public ConstructorUtils() {
335 // empty
336 }
337 }