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.beanutils;
19  
20  import java.lang.reflect.Constructor;
21  import java.lang.reflect.InvocationTargetException;
22  import java.lang.reflect.Modifier;
23  
24  /**
25   * <p> Utility reflection methods focussed on constructors, modelled after {@link MethodUtils}. </p>
26   *
27   * <h3>Known Limitations</h3>
28   * <h4>Accessing Public Constructors In A Default Access Superclass</h4>
29   * <p>There is an issue when invoking public constructors contained in a default access superclass.
30   * Reflection locates these constructors fine and correctly assigns them as public.
31   * However, an <code>IllegalAccessException</code> is thrown if the constructors is invoked.</p>
32   *
33   * <p><code>ConstructorUtils</code> contains a workaround for this situation.
34   * It will attempt to call <code>setAccessible</code> on this constructor.
35   * If this call succeeds, then the method can be invoked as normal.
36   * This call will only succeed when the application has sufficient security privilages.
37   * If this call fails then a warning will be logged and the method may fail.</p>
38   *
39   * @version $Id$
40   */
41  public class ConstructorUtils {
42  
43      // --------------------------------------------------------- Private Members
44      /** An empty class array */
45      private static final Class<?>[] EMPTY_CLASS_PARAMETERS = new Class<?>[0];
46      /** An empty object array */
47      private static final Object[] EMPTY_OBJECT_ARRAY = new Object[0];
48  
49      // --------------------------------------------------------- Public Methods
50  
51      /**
52       * <p>Convenience method returning new instance of <code>klazz</code> using a single argument constructor.
53       * The formal parameter type is inferred from the actual values of <code>arg</code>.
54       * See {@link #invokeExactConstructor(Class, Object[], Class[])} for more details.</p>
55       *
56       * <p>The signatures should be assignment compatible.</p>
57       *
58       * @param <T> the type of the object to be constructed
59       * @param klass the class to be constructed.
60       * @param arg the actual argument. May be null (this will result in calling the default constructor).
61       * @return new instance of <code>klazz</code>
62       *
63       * @throws NoSuchMethodException If the constructor cannot be found
64       * @throws IllegalAccessException If an error occurs accessing the constructor
65       * @throws InvocationTargetException If an error occurs invoking the constructor
66       * @throws InstantiationException If an error occurs instantiating the class
67       *
68       * @see #invokeConstructor(java.lang.Class, java.lang.Object[], java.lang.Class[])
69       */
70      public static <T> T invokeConstructor(final Class<T> klass, final Object arg)
71          throws
72              NoSuchMethodException,
73              IllegalAccessException,
74              InvocationTargetException,
75              InstantiationException {
76  
77          final Object[] args = toArray(arg);
78          return invokeConstructor(klass, args);
79      }
80  
81      /**
82       * <p>Returns new instance of <code>klazz</code> created using the actual arguments <code>args</code>.
83       * The formal parameter types are inferred from the actual values of <code>args</code>.
84       * See {@link #invokeExactConstructor(Class, Object[], Class[])} for more details.</p>
85       *
86       * <p>The signatures should be assignment compatible.</p>
87       *
88       * @param <T> the type of the object to be constructed
89       * @param klass the class to be constructed.
90       * @param args actual argument array. May be null (this will result in calling the default constructor).
91       * @return new instance of <code>klazz</code>
92       *
93       * @throws NoSuchMethodException If the constructor cannot be found
94       * @throws IllegalAccessException If an error occurs accessing the constructor
95       * @throws InvocationTargetException If an error occurs invoking the constructor
96       * @throws InstantiationException If an error occurs instantiating the class
97       *
98       * @see #invokeConstructor(java.lang.Class, java.lang.Object[], java.lang.Class[])
99       */
100     public static <T> T invokeConstructor(final Class<T> klass, Object[] args)
101         throws
102             NoSuchMethodException,
103             IllegalAccessException,
104             InvocationTargetException,
105             InstantiationException {
106 
107         if (null == args) {
108             args = EMPTY_OBJECT_ARRAY;
109         }
110         final int arguments = args.length;
111         final Class<?> parameterTypes[] = new Class<?>[arguments];
112         for (int i = 0; i < arguments; i++) {
113             parameterTypes[i] = args[i].getClass();
114         }
115         return invokeConstructor(klass, args, parameterTypes);
116     }
117 
118     /**
119      * <p>Returns new instance of <code>klazz</code> created using constructor
120      * with signature <code>parameterTypes</code> and actual arguments <code>args</code>.</p>
121      *
122      * <p>The signatures should be assignment compatible.</p>
123      *
124      * @param <T> the type of the object to be constructed
125      * @param klass the class to be constructed.
126      * @param args actual argument array. May be null (this will result in calling the default constructor).
127      * @param parameterTypes parameter types array
128      * @return new instance of <code>klazz</code>
129      *
130      * @throws NoSuchMethodException if matching constructor cannot be found
131      * @throws IllegalAccessException thrown on the constructor's invocation
132      * @throws InvocationTargetException thrown on the constructor's invocation
133      * @throws InstantiationException thrown on the constructor's invocation
134      * @see Constructor#newInstance
135      */
136     public static <T> T invokeConstructor(
137         final Class<T> klass,
138         Object[] args,
139         Class<?>[] parameterTypes)
140         throws
141             NoSuchMethodException,
142             IllegalAccessException,
143             InvocationTargetException,
144             InstantiationException {
145 
146         if (parameterTypes == null) {
147             parameterTypes = EMPTY_CLASS_PARAMETERS;
148         }
149         if (args == null) {
150             args = EMPTY_OBJECT_ARRAY;
151         }
152 
153         final Constructor<T> ctor =
154             getMatchingAccessibleConstructor(klass, parameterTypes);
155         if (null == ctor) {
156             throw new NoSuchMethodException(
157                 "No such accessible constructor on object: " + klass.getName());
158         }
159         return ctor.newInstance(args);
160     }
161 
162 
163     /**
164      * <p>Convenience method returning new instance of <code>klazz</code> using a single argument constructor.
165      * The formal parameter type is inferred from the actual values of <code>arg</code>.
166      * See {@link #invokeExactConstructor(Class, Object[], Class[])} for more details.</p>
167      *
168      * <p>The signatures should match exactly.</p>
169      *
170      * @param <T> the type of the object to be constructed
171      * @param klass the class to be constructed.
172      * @param arg the actual argument. May be null (this will result in calling the default constructor).
173      * @return new instance of <code>klazz</code>
174      *
175      * @throws NoSuchMethodException If the constructor cannot be found
176      * @throws IllegalAccessException If an error occurs accessing the constructor
177      * @throws InvocationTargetException If an error occurs invoking the constructor
178      * @throws InstantiationException If an error occurs instantiating the class
179      *
180      * @see #invokeExactConstructor(java.lang.Class, java.lang.Object[], java.lang.Class[])
181      */
182     public static <T> T invokeExactConstructor(final Class<T> klass, final Object arg)
183         throws
184             NoSuchMethodException,
185             IllegalAccessException,
186             InvocationTargetException,
187             InstantiationException {
188 
189         final Object[] args = toArray(arg);
190         return invokeExactConstructor(klass, args);
191     }
192 
193     /**
194      * <p>Returns new instance of <code>klazz</code> created using the actual arguments <code>args</code>.
195      * The formal parameter types are inferred from the actual values of <code>args</code>.
196      * See {@link #invokeExactConstructor(Class, Object[], Class[])} for more details.</p>
197      *
198      * <p>The signatures should match exactly.</p>
199      *
200      * @param <T> the type of the object to be constructed
201      * @param klass the class to be constructed.
202      * @param args actual argument array. May be null (this will result in calling the default constructor).
203      * @return new instance of <code>klazz</code>
204      *
205      * @throws NoSuchMethodException If the constructor cannot be found
206      * @throws IllegalAccessException If an error occurs accessing the constructor
207      * @throws InvocationTargetException If an error occurs invoking the constructor
208      * @throws InstantiationException If an error occurs instantiating the class
209      *
210      * @see #invokeExactConstructor(java.lang.Class, java.lang.Object[], java.lang.Class[])
211      */
212     public static <T> T invokeExactConstructor(final Class<T> klass, Object[] args)
213         throws
214             NoSuchMethodException,
215             IllegalAccessException,
216             InvocationTargetException,
217             InstantiationException {
218 
219         if (null == args) {
220             args = EMPTY_OBJECT_ARRAY;
221         }
222         final int arguments = args.length;
223         final Class<?> parameterTypes[] = new Class[arguments];
224         for (int i = 0; i < arguments; i++) {
225             parameterTypes[i] = args[i].getClass();
226         }
227         return invokeExactConstructor(klass, args, parameterTypes);
228     }
229 
230     /**
231      * <p>Returns new instance of <code>klazz</code> created using constructor
232      * with signature <code>parameterTypes</code> and actual arguments
233      * <code>args</code>.</p>
234      *
235      * <p>The signatures should match exactly.</p>
236      *
237      * @param <T> the type of the object to be constructed
238      * @param klass the class to be constructed.
239      * @param args actual argument array. May be null (this will result in calling the default constructor).
240      * @param parameterTypes parameter types array
241      * @return new instance of <code>klazz</code>
242      *
243      * @throws NoSuchMethodException if matching constructor cannot be found
244      * @throws IllegalAccessException thrown on the constructor's invocation
245      * @throws InvocationTargetException thrown on the constructor's invocation
246      * @throws InstantiationException thrown on the constructor's invocation
247      * @see Constructor#newInstance
248      */
249     public static <T> T invokeExactConstructor(
250         final Class<T> klass,
251         Object[] args,
252         Class<?>[] parameterTypes)
253         throws
254             NoSuchMethodException,
255             IllegalAccessException,
256             InvocationTargetException,
257             InstantiationException {
258 
259         if (args == null) {
260             args = EMPTY_OBJECT_ARRAY;
261         }
262 
263         if (parameterTypes == null) {
264             parameterTypes = EMPTY_CLASS_PARAMETERS;
265         }
266 
267         final Constructor<T> ctor = getAccessibleConstructor(klass, parameterTypes);
268         if (null == ctor) {
269             throw new NoSuchMethodException(
270                 "No such accessible constructor on object: " + klass.getName());
271         }
272         return ctor.newInstance(args);
273     }
274 
275     /**
276      * Returns a constructor with single argument.
277      * @param <T> the type of the constructor
278      * @param klass the class to be constructed
279      * @param parameterType The constructor parameter type
280      * @return null if matching accessible constructor can not be found.
281      * @see Class#getConstructor
282      * @see #getAccessibleConstructor(java.lang.reflect.Constructor)
283      */
284     public static <T> Constructor<T> getAccessibleConstructor(
285         final Class<T> klass,
286         final Class<?> parameterType) {
287 
288         final Class<?>[] parameterTypes = { parameterType };
289         return getAccessibleConstructor(klass, parameterTypes);
290     }
291 
292     /**
293      * Returns a constructor given a class and signature.
294      * @param <T> the type to be constructed
295      * @param klass the class to be constructed
296      * @param parameterTypes the parameter array
297      * @return null if matching accessible constructor can not be found
298      * @see Class#getConstructor
299      * @see #getAccessibleConstructor(java.lang.reflect.Constructor)
300      */
301     public static <T> Constructor<T> getAccessibleConstructor(
302         final Class<T> klass,
303         final Class<?>[] parameterTypes) {
304 
305         try {
306             return getAccessibleConstructor(
307                 klass.getConstructor(parameterTypes));
308         } catch (final NoSuchMethodException e) {
309             return (null);
310         }
311     }
312 
313     /**
314      * Returns accessible version of the given constructor.
315      * @param <T> the type of the constructor
316      * @param ctor prototype constructor object.
317      * @return <code>null</code> if accessible constructor can not be found.
318      * @see java.lang.SecurityManager
319      */
320     public static <T> Constructor<T> getAccessibleConstructor(final Constructor<T> ctor) {
321 
322         // Make sure we have a method to check
323         if (ctor == null) {
324             return (null);
325         }
326 
327         // If the requested method is not public we cannot call it
328         if (!Modifier.isPublic(ctor.getModifiers())) {
329             return (null);
330         }
331 
332         // If the declaring class is public, we are done
333         final Class<T> clazz = ctor.getDeclaringClass();
334         if (Modifier.isPublic(clazz.getModifiers())) {
335             return (ctor);
336         }
337 
338         // what else can we do?
339         return null;
340     }
341 
342     private static Object[] toArray(final Object arg) {
343         Object[] args = null;
344         if (arg != null) {
345             args = new Object[] { arg };
346         }
347         return args;
348     }
349 
350     // -------------------------------------------------------- Private Methods
351     /**
352      * <p>Find an accessible constructor with compatible parameters.
353      * Compatible parameters mean that every method parameter is assignable from
354      * the given parameters. In other words, it finds constructor that will take
355      * the parameters given.</p>
356      *
357      * <p>First it checks if there is constructor matching the exact signature.
358      * If no such, all the constructors of the class are tested if their signatures
359      * are assignment compatible with the parameter types.
360      * The first matching constructor is returned.</p>
361      *
362      * @param <T> the type of the class to be inspected
363      * @param clazz find constructor for this class
364      * @param parameterTypes find method with compatible parameters
365      * @return a valid Constructor object. If there's no matching constructor, returns <code>null</code>.
366      */
367     private static <T> Constructor<T> getMatchingAccessibleConstructor(
368         final Class<T> clazz,
369         final Class<?>[] parameterTypes) {
370         // see if we can find the method directly
371         // most of the time this works and it's much faster
372         try {
373             final Constructor<T> ctor = clazz.getConstructor(parameterTypes);
374             try {
375                 //
376                 // XXX Default access superclass workaround
377                 //
378                 // When a public class has a default access superclass
379                 // with public methods, these methods are accessible.
380                 // Calling them from compiled code works fine.
381                 //
382                 // Unfortunately, using reflection to invoke these methods
383                 // seems to (wrongly) to prevent access even when the method
384                 // modifer is public.
385                 //
386                 // The following workaround solves the problem but will only
387                 // work from sufficiently privilages code.
388                 //
389                 // Better workarounds would be greatfully accepted.
390                 //
391                 ctor.setAccessible(true);
392             } catch (final SecurityException se) {
393                 /* SWALLOW, if workaround fails don't fret. */
394             }
395             return ctor;
396 
397         } catch (final NoSuchMethodException e) { /* SWALLOW */
398         }
399 
400         // search through all methods
401         final int paramSize = parameterTypes.length;
402         final Constructor<?>[] ctors = clazz.getConstructors();
403         for (Constructor<?> ctor2 : ctors) {
404             // compare parameters
405             final Class<?>[] ctorParams = ctor2.getParameterTypes();
406             final int ctorParamSize = ctorParams.length;
407             if (ctorParamSize == paramSize) {
408                 boolean match = true;
409                 for (int n = 0; n < ctorParamSize; n++) {
410                     if (!MethodUtils
411                         .isAssignmentCompatible(
412                             ctorParams[n],
413                             parameterTypes[n])) {
414                         match = false;
415                         break;
416                     }
417                 }
418 
419                 if (match) {
420                     // get accessible version of method
421                     final Constructor<?> ctor = getAccessibleConstructor(ctor2);
422                     if (ctor != null) {
423                         try {
424                             ctor.setAccessible(true);
425                         } catch (final SecurityException se) {
426                             /* Swallow SecurityException
427                              * TODO: Why?
428                              */
429                         }
430                         @SuppressWarnings("unchecked")
431                         final
432                         // Class.getConstructors() actually returns constructors
433                         // of type T, so it is safe to cast.
434                         Constructor<T> typedCtor = (Constructor<T>) ctor;
435                         return typedCtor;
436                     }
437                 }
438             }
439         }
440 
441         return null;
442     }
443 
444 }