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   * @author Craig R. McClanahan
    40   * @author Ralph Schaer
    41   * @author Chris Audley
    42   * @author Rey Francois
    43   * @author Gregor Rayman
    44   * @author Jan Sorensen
    45   * @author Robert Burrell Donkin
    46   * @author Rodney Waldhoff
    47   * @version $Revision: 555824 $ $Date: 2007-07-13 01:27:15 +0100 (Fri, 13 Jul 2007) $
    48   */
    49  public class ConstructorUtils {
    50
    51      // --------------------------------------------------------- Private Members
    52      /*** An empty class array */
    53      private static final Class[] EMPTY_CLASS_PARAMETERS = new Class[0];
    54      /*** An empty object array */
    55      private static final Object[] EMPTY_OBJECT_ARRAY = new Object[0];
    56
    57      // --------------------------------------------------------- Public Methods
    58
    59      /***
    60       * <p>Convenience method returning new instance of <code>klazz</code> using a single argument constructor.
    61       * The formal parameter type is inferred from the actual values of <code>arg</code>.
    62       * See {@link #invokeExactConstructor(Class, Object[], Class[])} for more details.</p>
    63       *
    64       * <p>The signatures should be assignment compatible.</p>
    65       *
    66       * @param klass the class to be constructed.
    67       * @param arg the actual argument
    68       * @return new instance of <code>klazz</code>
    69       *
    70       * @throws NoSuchMethodException If the constructor cannot be found
    71       * @throws IllegalAccessException If an error occurs accessing the constructor
    72       * @throws InvocationTargetException If an error occurs invoking the constructor
    73       * @throws InstantiationException If an error occurs instantiating the class
    74       *
    75       * @see #invokeConstructor(java.lang.Class, java.lang.Object[], java.lang.Class[])
    76       */
    77      public static Object invokeConstructor(Class klass, Object arg)
    78          throws
    79              NoSuchMethodException,
    80              IllegalAccessException,
    81              InvocationTargetException,
    82              InstantiationException {
    83
    84          Object[] args = { arg };
    85          return invokeConstructor(klass, args);
    86
    87      }
    88
    89      /***
    90       * <p>Returns new instance of <code>klazz</code> created using the actual arguments <code>args</code>.
    91       * The formal parameter types are inferred from the actual values of <code>args</code>.
    92       * See {@link #invokeExactConstructor(Class, Object[], Class[])} for more details.</p>
    93       *
    94       * <p>The signatures should be assignment compatible.</p>
    95       *
    96       * @param klass the class to be constructed.
    97       * @param args actual argument array
    98       * @return new instance of <code>klazz</code>
    99       *
    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 }