    001    /*****************************************************************************
    002     * Copyright (c) PicoContainer Organization. All rights reserved.            *
    003     * ------------------------------------------------------------------------- *
    004     * The software in this package is published under the terms of the BSD      *
    005     * style license a copy of which has been included with this distribution in *
    006     * the LICENSE.txt file.                                                     *
    007     *                                                                           *
    008     * Idea by Rachel Davies, Original code by Aslak Hellesoy and Paul Hammant   *
    009     *****************************************************************************/
    010
    011    package org.picocontainer.injectors;
    012
    013    import org.picocontainer.ComponentMonitor;
    014    import org.picocontainer.LifecycleStrategy;
    015    import org.picocontainer.Parameter;
    016    import org.picocontainer.PicoCompositionException;
    017    import org.picocontainer.PicoContainer;
    018    import org.picocontainer.ComponentAdapter;
    019    import org.picocontainer.NameBinding;
    020    import org.picocontainer.lifecycle.NullLifecycleStrategy;
    021    import org.picocontainer.monitors.NullComponentMonitor;
    022
    023    import java.lang.reflect.Constructor;
    024    import java.lang.reflect.InvocationTargetException;
    025    import java.lang.reflect.Modifier;
    026    import java.lang.reflect.Type;
    027    import java.lang.reflect.TypeVariable;
    028    import java.lang.annotation.Annotation;
    029    import java.security.AccessController;
    030    import java.security.PrivilegedAction;
    031    import java.util.ArrayList;
    032    import java.util.Arrays;
    033    import java.util.Collections;
    034    import java.util.Comparator;
    035    import java.util.HashSet;
    036    import java.util.List;
    037    import java.util.Set;
    038    import java.util.Map;
    039    import java.util.HashMap;
    040
    041    /**
    042     * Injection will happen through a constructor for the component.
    043     *
    044     * @author Paul Hammant
    045     * @author Aslak Helles&oslash;y
    046     * @author Jon Tirs&eacute;n
    047     * @author Zohar Melamed
    048     * @author J&ouml;rg Schaible
    049     * @author Mauro Talevi
    050     */
    051    @SuppressWarnings("serial")
    052    public class ConstructorInjector<T> extends SingleMemberInjector<T> {
    053
    054            private transient List<Constructor<T>> sortedMatchingConstructors;
    055        private transient ThreadLocalCyclicDependencyGuard<T> instantiationGuard;
    056        private boolean rememberChosenConstructor = true;
    057        private transient CtorAndAdapters<T> chosenConstructor;
    058
    059        /**
    060         * Constructor injector that uses no monitor and no lifecycle adapter.  This is a more
    061         * convenient constructor for use when instantiating a constructor injector directly.
    062         * @param componentKey the search key for this implementation
    063         * @param componentImplementation the concrete implementation
    064         * @param parameters the parameters used for initialization
    065         */
    066        public ConstructorInjector(final Object componentKey, final Class<?> componentImplementation, Parameter... parameters) {
    067            this(componentKey, componentImplementation, parameters, new NullComponentMonitor(), new NullLifecycleStrategy(), false);
    068        }
    069
    070        /**
    071         * Creates a ConstructorInjector
    072         *
    073         * @param componentKey            the search key for this implementation
    074         * @param componentImplementation the concrete implementation
    075         * @param parameters              the parameters to use for the initialization
    076         * @param monitor                 the component monitor used by this addAdapter
    077         * @param lifecycleStrategy       the component lifecycle strategy used by this addAdapter
    078         * @param useNames                use argument names when looking up dependencies
    079         * @throws org.picocontainer.injectors.AbstractInjector.NotConcreteRegistrationException
    080         *                              if the implementation is not a concrete class.
    081         * @throws NullPointerException if one of the parameters is <code>null</code>
    082         */
    083        public ConstructorInjector(final Object componentKey, final Class componentImplementation, Parameter[] parameters, ComponentMonitor monitor,
    084                                   LifecycleStrategy lifecycleStrategy, boolean useNames) throws  NotConcreteRegistrationException {
    085            super(componentKey, componentImplementation, parameters, monitor, lifecycleStrategy, useNames);
    086        }
    087
    088        /**
    089         * Creates a ConstructorInjector
    090         *
    091         * @param componentKey            the search key for this implementation
    092         * @param componentImplementation the concrete implementation
    093         * @param parameters              the parameters to use for the initialization
    094         * @param monitor                 the component monitor used by this addAdapter
    095         * @param lifecycleStrategy       the component lifecycle strategy used by this addAdapter
    096         * @param useNames                use argument names when looking up dependencies
    097         * @param rememberChosenCtor      remember the chosen constructor (to speed up second/subsequent calls)
    098         * @throws org.picocontainer.injectors.AbstractInjector.NotConcreteRegistrationException
    099         *                              if the implementation is not a concrete class.
    100         * @throws NullPointerException if one of the parameters is <code>null</code>
    101         */
    102        public ConstructorInjector(final Object componentKey, final Class componentImplementation, Parameter[] parameters, ComponentMonitor monitor,
    103                                   LifecycleStrategy lifecycleStrategy, boolean useNames, boolean rememberChosenCtor) throws  NotConcreteRegistrationException {
    104            super(componentKey, componentImplementation, parameters, monitor, lifecycleStrategy, useNames);
    105            this.rememberChosenConstructor = rememberChosenCtor;
    106        }
    107
    108        private CtorAndAdapters<T> getGreediestSatisfiableConstructor(PicoContainer guardedContainer, Class<T> componentImplementation) {
    109            CtorAndAdapters<T> ctor = null;
    110            try {
    111                if (chosenConstructor == null) {
    112                    ctor = getGreediestSatisfiableConstructor(guardedContainer);
    113                }
    114                if (rememberChosenConstructor) {
    115                    if (chosenConstructor == null) {
    116                        chosenConstructor = ctor;
    117                    } else {
    118                        ctor = chosenConstructor;
    119                    }
    120                }
    121            } catch (AmbiguousComponentResolutionException e) {
    122                e.setComponent(getComponentImplementation());
    123                throw e;
    124            }
    125            return ctor;
    126        }
    127
    128        protected CtorAndAdapters<T> getGreediestSatisfiableConstructor(PicoContainer container) throws PicoCompositionException {
    129            final Set<Constructor> conflicts = new HashSet<Constructor>();
    130            final Set<List<Type>> unsatisfiableDependencyTypes = new HashSet<List<Type>>();
    131            final Map<ResolverKey, Parameter.Resolver> resolvers = new HashMap<ResolverKey, Parameter.Resolver>();
    132            if (sortedMatchingConstructors == null) {
    133                sortedMatchingConstructors = getSortedMatchingConstructors();
    134            }
    135            Constructor<T> greediestConstructor = null;
    136            Parameter[] greediestConstructorsParameters = null;
    137            ComponentAdapter[] greediestConstructorsParametersComponentAdapters = null;
    138            int lastSatisfiableConstructorSize = -1;
    139            Type unsatisfiedDependencyType = null;
    140            for (final Constructor<T> sortedMatchingConstructor : sortedMatchingConstructors) {
    141                boolean failedDependency = false;
    142                Type[] parameterTypes = sortedMatchingConstructor.getGenericParameterTypes();
    143                fixParameterType(sortedMatchingConstructor, parameterTypes);
    144                Annotation[] bindings = getBindings(sortedMatchingConstructor.getParameterAnnotations());
    145                final Parameter[] currentParameters = parameters != null ? parameters : createDefaultParameters(parameterTypes);
    146                final ComponentAdapter<?>[] currentAdapters = new ComponentAdapter<?>[currentParameters.length];
    147                // remember: all constructors with less arguments than the given parameters are filtered out already
    148                for (int j = 0; j < currentParameters.length; j++) {
    149                    // check whether this constructor is satisfiable
    150                    Type expectedType = box(parameterTypes[j]);
    151                    NameBinding expectedNameBinding = new ParameterNameBinding(getParanamer(), sortedMatchingConstructor, j);
    152                    ResolverKey resolverKey = new ResolverKey(expectedType, useNames() == true ? expectedNameBinding.getName() : null, useNames(), bindings[j], currentParameters[j]);
    153                    Parameter.Resolver resolver = resolvers.get(resolverKey);
    154                    if (resolver == null) {
    155                        resolver = currentParameters[j].resolve(container, this, null, expectedType, expectedNameBinding, useNames(), bindings[j]);
    156                        resolvers.put(resolverKey, resolver);
    157                    }
    158                    if (resolver.isResolved()) {
    159                        currentAdapters[j] = resolver.getComponentAdapter();
    160                        continue;
    161                    }
    162                    unsatisfiableDependencyTypes.add(Arrays.asList(parameterTypes));
    163                    unsatisfiedDependencyType = box(parameterTypes[j]);
    164                    failedDependency = true;
    165                    break;
    166                }
    167
    168                if (greediestConstructor != null && parameterTypes.length != lastSatisfiableConstructorSize) {
    169                    if (conflicts.isEmpty()) {
    170                        // we found our match [aka. greedy and satisfied]
    171                        return new CtorAndAdapters<T>(greediestConstructor, greediestConstructorsParameters, greediestConstructorsParametersComponentAdapters);
    172                    } else {
    173                        // fits although not greedy
    174                        conflicts.add(sortedMatchingConstructor);
    175                    }
    176                } else if (!failedDependency && lastSatisfiableConstructorSize == parameterTypes.length) {
    177                    // satisfied and same size as previous one?
    178                    conflicts.add(sortedMatchingConstructor);
    179                    conflicts.add(greediestConstructor);
    180                } else if (!failedDependency) {
    181                    greediestConstructor = sortedMatchingConstructor;
    182                    greediestConstructorsParameters = currentParameters;
    183                    greediestConstructorsParametersComponentAdapters = currentAdapters;
    184                    lastSatisfiableConstructorSize = parameterTypes.length;
    185                }
    186            }
    187            if (!conflicts.isEmpty()) {
    188                throw new PicoCompositionException(conflicts.size() + " satisfiable constructors is too many for '"+getComponentImplementation()+"'. Constructor List:" + conflicts.toString().replace(getComponentImplementation().getName(),"<init>").replace("public <i","<i"));
    189            } else if (greediestConstructor == null && !unsatisfiableDependencyTypes.isEmpty()) {
    190                throw new UnsatisfiableDependenciesException(this, unsatisfiedDependencyType, unsatisfiableDependencyTypes, container);
    191            } else if (greediestConstructor == null) {
    192                // be nice to the user, show all constructors that were filtered out
    193                final Set<Constructor> nonMatching = new HashSet<Constructor>();
    194                for (Constructor constructor : getConstructors()) {
    195                    nonMatching.add(constructor);
    196                }
    197                throw new PicoCompositionException("Either the specified parameters do not match any of the following constructors: " + nonMatching.toString() + "; OR the constructors were not accessible for '" + getComponentImplementation().getName() + "'");
    198            }
    199            return new CtorAndAdapters<T>(greediestConstructor, greediestConstructorsParameters, greediestConstructorsParametersComponentAdapters);
    200        }
    201
    202        private static final class ResolverKey {
    203            private final Type expectedType;
    204            private final String pName;
    205            private final boolean useNames;
    206            private final Annotation binding;
    207            private final Parameter currentParameter;
    208
    209            private ResolverKey(Type expectedType, String pName, boolean useNames, Annotation binding, Parameter currentParameter) {
    210                this.expectedType = expectedType;
    211                this.pName = pName;
    212                this.useNames = useNames;
    213                this.binding = binding;
    214                this.currentParameter = currentParameter;
    215            }
    216
    217            // Generated by IDEA
    218
    219            public boolean equals(Object o) {
    220                if (this == o) return true;
    221                if (o == null || getClass() != o.getClass()) return false;
    222
    223                ResolverKey that = (ResolverKey) o;
    224
    225                if (useNames != that.useNames) return false;
    226                if (binding != null ? !binding.equals(that.binding) : that.binding != null) return false;
    227                if (!currentParameter.equals(that.currentParameter)) return false;
    228                if (!expectedType.equals(that.expectedType)) return false;
    229                if (pName != null ? !pName.equals(that.pName) : that.pName != null) return false;
    230
    231                return true;
    232            }
    233
    234            public int hashCode() {
    235                int result;
    236                result = expectedType.hashCode();
    237                result = 31 * result + (pName != null ? pName.hashCode() : 0);
    238                result = 31 * result + (useNames ? 1 : 0);
    239                result = 31 * result + (binding != null ? binding.hashCode() : 0);
    240                result = 31 * result + currentParameter.hashCode();
    241                return result;
    242            }
    243        }
    244
    245        private void fixParameterType(Constructor<T> ctor, Type[] parameterTypes) {
    246            for (int i = 0; i < parameterTypes.length; i++) {
    247                Type parameterType = parameterTypes[i];
    248                if (parameterType instanceof TypeVariable) {
    249                    parameterTypes[i] = ctor.getParameterTypes()[i];
    250                }
    251            }
    252        }
    253
    254        protected class CtorAndAdapters<T> {
    255            private final Constructor<T> ctor;
    256            private final Parameter[] parameters;
    257            private final ComponentAdapter[] injecteeAdapters;
    258
    259            public CtorAndAdapters(Constructor<T> ctor, Parameter[] parameters, ComponentAdapter[] injecteeAdapters) {
    260                this.ctor = ctor;
    261                this.parameters = parameters;
    262                this.injecteeAdapters = injecteeAdapters;
    263            }
    264
    265            public Constructor<T> getConstructor() {
    266                return ctor;
    267            }
    268
    269            public Object[] getParameterArguments(PicoContainer container) {
    270                Type[] parameterTypes = ctor.getGenericParameterTypes();
    271                // as per fixParameterType()
    272                for (int i = 0; i < parameterTypes.length; i++) {
    273                    Type parameterType = parameterTypes[i];
    274                    if (parameterType instanceof TypeVariable) {
    275                        parameterTypes[i] = ctor.getParameterTypes()[i];
    276                    }
    277                }
    278                boxParameters(parameterTypes);
    279                Object[] result = new Object[parameters.length];
    280                Annotation[] bindings = getBindings(ctor.getParameterAnnotations());
    281                for (int i = 0; i < parameters.length; i++) {
    282
    283                    result[i] = getParameter(container, ctor, i, parameterTypes[i],
    284                            bindings[i], parameters[i], injecteeAdapters[i]);
    285                }
    286                return result;
    287            }
    288
    289            public ComponentAdapter[] getInjecteeAdapters() {
    290                return injecteeAdapters;
    291            }
    292
    293            public Parameter[] getParameters() {
    294                return parameters;
    295            }
    296        }
    297
    298        public T getComponentInstance(final PicoContainer container, Type into) throws PicoCompositionException {
    299            if (instantiationGuard == null) {
    300                instantiationGuard = new ThreadLocalCyclicDependencyGuard<T>() {
    301                    public T run() {
    302                        CtorAndAdapters<T> ctor = getGreediestSatisfiableConstructor(guardedContainer, getComponentImplementation());
    303                        ComponentMonitor componentMonitor = currentMonitor();
    304                        Constructor<T> ct = ctor.getConstructor();
    305                        try {
    306                            Object[] parameters = ctor.getParameterArguments(guardedContainer);
    307                            ct = componentMonitor.instantiating(container, ConstructorInjector.this, ct);
    308                            if(ctor == null) {
    309                                throw new NullPointerException("Component Monitor " + componentMonitor
    310                                                + " returned a null constructor from method 'instantiating' after passing in " + ctor);
    311                            }
    312                            long startTime = System.currentTimeMillis();
    313                            T inst = instantiate(ct, parameters);
    314                            componentMonitor.instantiated(container, ConstructorInjector.this,
    315                                    ct, inst, parameters, System.currentTimeMillis() - startTime);
    316                            return inst;
    317                        } catch (InvocationTargetException e) {
    318                            componentMonitor.instantiationFailed(container, ConstructorInjector.this, ct, e);
    319                            if (e.getTargetException() instanceof RuntimeException) {
    320                                throw (RuntimeException) e.getTargetException();
    321                            } else if (e.getTargetException() instanceof Error) {
    322                                throw (Error) e.getTargetException();
    323                            }
    324                            throw new PicoCompositionException(e.getTargetException());
    325                        } catch (InstantiationException e) {
    326                            return caughtInstantiationException(componentMonitor, ct, e, container);
    327                        } catch (IllegalAccessException e) {
    328                            return caughtIllegalAccessException(componentMonitor, ct, e, container);
    329
    330                        }
    331                    }
    332                };
    333            }
    334            instantiationGuard.setGuardedContainer(container);
    335            return instantiationGuard.observe(getComponentImplementation());
    336        }
    337
    338
    339        protected T instantiate(Constructor<T> constructor, Object[] parameters) throws InstantiationException, IllegalAccessException, InvocationTargetException {
    340            T inst = newInstance(constructor, parameters);
    341            return inst;
    342        }
    343
    344        private List<Constructor<T>> getSortedMatchingConstructors() {
    345            List<Constructor<T>> matchingConstructors = new ArrayList<Constructor<T>>();
    346            Constructor<T>[] allConstructors = getConstructors();
    347            // filter out all constructors that will definately not match
    348            for (Constructor<T> constructor : allConstructors) {
    349                if ((parameters == null || constructor.getParameterTypes().length == parameters.length) && (constructor.getModifiers() & Modifier.PUBLIC) != 0) {
    350                    matchingConstructors.add(constructor);
    351                }
    352            }
    353            // optimize list of constructors moving the longest at the beginning
    354            if (parameters == null) {
    355                Collections.sort(matchingConstructors, new Comparator<Constructor>() {
    356                    public int compare(Constructor arg0, Constructor arg1) {
    357                        return arg1.getParameterTypes().length - arg0.getParameterTypes().length;
    358                    }
    359                });
    360            }
    361            return matchingConstructors;
    362        }
    363
    364        private Constructor<T>[] getConstructors() {
    365            return AccessController.doPrivileged(new PrivilegedAction<Constructor<T>[]>() {
    366                public Constructor<T>[] run() {
    367                    return (Constructor<T>[]) getComponentImplementation().getDeclaredConstructors();
    368                }
    369            });
    370        }
    371
    372        @Override
    373        public void verify(final PicoContainer container) throws PicoCompositionException {
    374            if (verifyingGuard == null) {
    375                verifyingGuard = new ThreadLocalCyclicDependencyGuard() {
    376                    public Object run() {
    377                        final Constructor constructor = getGreediestSatisfiableConstructor(guardedContainer).getConstructor();
    378                        final Class[] parameterTypes = constructor.getParameterTypes();
    379                        final Parameter[] currentParameters = parameters != null ? parameters : createDefaultParameters(parameterTypes);
    380                        for (int i = 0; i < currentParameters.length; i++) {
    381                            currentParameters[i].verify(container, ConstructorInjector.this, box(parameterTypes[i]),
    382                                new ParameterNameBinding(getParanamer(),  constructor, i),
    383                                    useNames(), getBindings(constructor.getParameterAnnotations())[i]);
    384                        }
    385                        return null;
    386                    }
    387                };
    388            }
    389            verifyingGuard.setGuardedContainer(container);
    390            verifyingGuard.observe(getComponentImplementation());
    391        }
    392
    393        public String getDescriptor() {
    394            return "ConstructorInjector-";
    395        }
    396
    397
    398    }