View Javadoc
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    *      http://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 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 }