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.ComponentAdapter;
014    import org.picocontainer.ComponentMonitor;
015    import org.picocontainer.Emjection;
016    import org.picocontainer.NameBinding;
017    import org.picocontainer.Parameter;
018    import org.picocontainer.PicoCompositionException;
019    import org.picocontainer.PicoContainer;
020    import org.picocontainer.monitors.NullComponentMonitor;
021    
022    import java.lang.annotation.Annotation;
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.security.AccessController;
029    import java.security.PrivilegedAction;
030    import java.util.ArrayList;
031    import java.util.Collections;
032    import java.util.Comparator;
033    import java.util.HashMap;
034    import java.util.HashSet;
035    import java.util.Iterator;
036    import java.util.List;
037    import java.util.Map;
038    import java.util.Set;
039    
040    /**
041     * Injection will happen through a constructor for the component.
042     *
043     * @author Paul Hammant
044     * @author Aslak Hellesøy
045     * @author Jon Tirsén
046     * @author Zohar Melamed
047     * @author Jörg Schaible
048     * @author Mauro Talevi
049     */
050    @SuppressWarnings("serial")
051    public class ConstructorInjector<T> extends SingleMemberInjector<T> {
052            
053            private transient List<Constructor<T>> sortedMatchingConstructors;
054        private transient ThreadLocalCyclicDependencyGuard<T> instantiationGuard;
055        private boolean rememberChosenConstructor = true;
056        private transient CtorAndAdapters<T> chosenConstructor;
057        private boolean enableEmjection = false;
058        private boolean allowNonPublicClasses = false;
059    
060        /**
061         * Constructor injector that uses no monitor and no lifecycle adapter.  This is a more
062         * convenient constructor for use when instantiating a constructor injector directly.
063         * @param componentKey the search key for this implementation
064         * @param componentImplementation the concrete implementation
065         * @param parameters the parameters used for initialization
066         */
067        public ConstructorInjector(final Object componentKey, final Class<?> componentImplementation, Parameter... parameters) {
068            this(componentKey, componentImplementation, parameters, new NullComponentMonitor(), false);
069        }
070    
071        /**
072         * Creates a ConstructorInjector
073         *
074         * @param componentKey            the search key for this implementation
075         * @param componentImplementation the concrete implementation
076         * @param parameters              the parameters to use for the initialization
077         * @param monitor                 the component monitor 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                                   boolean useNames) throws  NotConcreteRegistrationException {
085            super(componentKey, componentImplementation, parameters, monitor, 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 useNames                use argument names when looking up dependencies
096         * @param rememberChosenCtor      remember the chosen constructor (to speed up second/subsequent calls)
097         * @throws org.picocontainer.injectors.AbstractInjector.NotConcreteRegistrationException
098         *                              if the implementation is not a concrete class.
099         * @throws NullPointerException if one of the parameters is <code>null</code>
100         */
101        public ConstructorInjector(final Object componentKey, final Class componentImplementation, Parameter[] parameters, ComponentMonitor monitor,
102                                   boolean useNames, boolean rememberChosenCtor) throws  NotConcreteRegistrationException {
103            super(componentKey, componentImplementation, parameters, monitor, useNames);
104            this.rememberChosenConstructor = rememberChosenCtor;
105        }
106    
107        private CtorAndAdapters<T> getGreediestSatisfiableConstructor(PicoContainer guardedContainer, @SuppressWarnings("unused") Class<? extends T> componentImplementation) {
108            CtorAndAdapters<T> ctor = null;
109            if (chosenConstructor == null) {
110                ctor = getGreediestSatisfiableConstructor(guardedContainer);
111            }
112            if (rememberChosenConstructor) {
113                if (chosenConstructor == null) {
114                    chosenConstructor = ctor;
115                } else {
116                    ctor = chosenConstructor;
117                }
118            }
119            return ctor;
120        }
121    
122        @SuppressWarnings("synthetic-access")
123        protected CtorAndAdapters<T> getGreediestSatisfiableConstructor(PicoContainer container) throws PicoCompositionException {
124            final Set<Constructor> conflicts = new HashSet<Constructor>();
125            final Set<Type> unsatisfiableDependencyTypes = new HashSet<Type>();
126            final Map<ResolverKey, Parameter.Resolver> resolvers = new HashMap<ResolverKey, Parameter.Resolver>();
127            if (sortedMatchingConstructors == null) {
128                sortedMatchingConstructors = getSortedMatchingConstructors();
129            }
130            Constructor<T> greediestConstructor = null;
131            Parameter[] greediestConstructorsParameters = null;
132            ComponentAdapter[] greediestConstructorsParametersComponentAdapters = null;
133            int lastSatisfiableConstructorSize = -1;
134            Type unsatisfiedDependency = null;
135            Constructor unsatisfiedConstructor = null;
136            for (final Constructor<T> sortedMatchingConstructor : sortedMatchingConstructors) {
137                try {
138                    boolean failedDependency = false;
139                    Type[] parameterTypes = sortedMatchingConstructor.getGenericParameterTypes();
140                    fixGenericParameterTypes(sortedMatchingConstructor, parameterTypes);
141                    Annotation[] bindings = getBindings(sortedMatchingConstructor.getParameterAnnotations());
142                    final Parameter[] currentParameters = parameters != null ? parameters : createDefaultParameters(parameterTypes.length);
143                    final ComponentAdapter<?>[] currentAdapters = new ComponentAdapter<?>[currentParameters.length];
144                    // remember: all constructors with less arguments than the given parameters are filtered out already
145                    for (int j = 0; j < currentParameters.length; j++) {
146                        // check whether this constructor is satisfiable
147                        Type expectedType = box(parameterTypes[j]);
148                        NameBinding expectedNameBinding = new ParameterNameBinding(getParanamer(), sortedMatchingConstructor, j);
149                        ResolverKey resolverKey = new ResolverKey(expectedType, useNames() ? expectedNameBinding.getName() : null, useNames(), bindings[j], currentParameters[j]);
150                        Parameter.Resolver resolver = resolvers.get(resolverKey);
151                        if (resolver == null) {
152                            resolver = currentParameters[j].resolve(container, this, null, expectedType, expectedNameBinding, useNames(), bindings[j]);
153                            resolvers.put(resolverKey, resolver);
154                        }
155                        if (resolver.isResolved()) {
156                            currentAdapters[j] = resolver.getComponentAdapter();
157                            continue;
158                        }
159                        unsatisfiableDependencyTypes.add(expectedType);
160                        unsatisfiedDependency = box(parameterTypes[j]);
161                        unsatisfiedConstructor = sortedMatchingConstructor;
162                        failedDependency = true;
163                    }
164    
165                    if (greediestConstructor != null && parameterTypes.length != lastSatisfiableConstructorSize) {
166                        if (conflicts.isEmpty()) {
167                            // we found our match [aka. greedy and satisfied]
168                            return new CtorAndAdapters<T>(greediestConstructor, greediestConstructorsParameters, greediestConstructorsParametersComponentAdapters);
169                        }
170                        // fits although not greedy
171                        conflicts.add(sortedMatchingConstructor);
172                    } else if (!failedDependency && lastSatisfiableConstructorSize == parameterTypes.length) {
173                        // satisfied and same size as previous one?
174                        conflicts.add(sortedMatchingConstructor);
175                        conflicts.add(greediestConstructor);
176                    } else if (!failedDependency) {
177                        greediestConstructor = sortedMatchingConstructor;
178                        greediestConstructorsParameters = currentParameters;
179                        greediestConstructorsParametersComponentAdapters = currentAdapters;
180                        lastSatisfiableConstructorSize = parameterTypes.length;
181                    }
182                } catch (AmbiguousComponentResolutionException e) {
183                    // embellish with the constructor being injected into.
184                    e.setMember(sortedMatchingConstructor);
185                    throw e;
186                }
187            }
188            if (!conflicts.isEmpty()) {
189                throw new PicoCompositionException(conflicts.size() + " satisfiable constructors is too many for '"+getComponentImplementation()+"'. Constructor List:" + conflicts.toString().replace(getComponentImplementation().getName(),"<init>").replace("public <i","<i"));
190            } else if (greediestConstructor == null && !unsatisfiableDependencyTypes.isEmpty()) {
191                throw new UnsatisfiableDependenciesException(this.getComponentImplementation().getName()
192                        + " has unsatisfied dependency '" + unsatisfiedDependency
193                        + "' for constructor '" + unsatisfiedConstructor + "'" + " from " + container);
194            } else if (greediestConstructor == null) {
195                // be nice to the user, show all constructors that were filtered out
196                final Set<Constructor> nonMatching = new HashSet<Constructor>();
197                for (Constructor constructor : getConstructors()) {
198                    nonMatching.add(constructor);
199                }
200                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() + "'");
201            }
202            return new CtorAndAdapters<T>(greediestConstructor, greediestConstructorsParameters, greediestConstructorsParametersComponentAdapters);
203        }
204    
205        private String toList(Set<Type> unsatisfiableDependencyTypes) {
206            StringBuilder sb = new StringBuilder();
207            Iterator<Type> it = unsatisfiableDependencyTypes.iterator();
208            while (it.hasNext()) {
209                Type next = it.next();
210                sb.append(next.toString().replace("class ", ""));
211                sb.append(", ");
212            }
213            String s = sb.toString();
214            return s.substring(0, s.lastIndexOf(", "));
215        }
216    
217        public void enableEmjection(boolean enableEmjection) {
218            this.enableEmjection = enableEmjection;
219        }
220    
221    
222        public ConstructorInjector<T> withNonPublicConstructors() {
223            allowNonPublicClasses = true;
224            return this;
225        }
226    
227        private static final class ResolverKey {
228            private final Type expectedType;
229            private final String pName;
230            private final boolean useNames;
231            private final Annotation binding;
232            private final Parameter currentParameter;
233    
234            private ResolverKey(Type expectedType, String pName, boolean useNames, Annotation binding, Parameter currentParameter) {
235                this.expectedType = expectedType;
236                this.pName = pName;
237                this.useNames = useNames;
238                this.binding = binding;
239                this.currentParameter = currentParameter;
240            }
241    
242            // Generated by IDEA
243            @Override
244            public boolean equals(Object o) {
245                if (this == o) return true;
246                if (o == null || getClass() != o.getClass()) return false;
247    
248                ResolverKey that = (ResolverKey) o;
249    
250                if (useNames != that.useNames) return false;
251                if (binding != null ? !binding.equals(that.binding) : that.binding != null) return false;
252                if (!currentParameter.equals(that.currentParameter)) return false;
253                if (!expectedType.equals(that.expectedType)) return false;
254                if (pName != null ? !pName.equals(that.pName) : that.pName != null) return false;
255    
256                return true;
257            }
258    
259            @Override
260            public int hashCode() {
261                int result;
262                result = expectedType.hashCode();
263                result = 31 * result + (pName != null ? pName.hashCode() : 0);
264                result = 31 * result + (useNames ? 1 : 0);
265                result = 31 * result + (binding != null ? binding.hashCode() : 0);
266                result = 31 * result + currentParameter.hashCode();
267                return result;
268            }
269        }
270    
271        private void fixGenericParameterTypes(Constructor<T> ctor, Type[] parameterTypes) {
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        }
279    
280        protected class CtorAndAdapters<TYPE> {
281            private final Constructor<TYPE> ctor;
282            private final Parameter[] constructorParameters;
283            private final ComponentAdapter[] injecteeAdapters;
284    
285            public CtorAndAdapters(Constructor<TYPE> ctor, Parameter[] parameters, ComponentAdapter[] injecteeAdapters) {
286                this.ctor = ctor;
287                this.constructorParameters = parameters;
288                this.injecteeAdapters = injecteeAdapters;
289            }
290    
291            public Constructor<TYPE> getConstructor() {
292                return ctor;
293            }
294    
295            public Object[] getParameterArguments(PicoContainer container) {
296                Type[] parameterTypes = ctor.getGenericParameterTypes();
297                // as per fixParameterType()
298                for (int i = 0; i < parameterTypes.length; i++) {
299                    Type parameterType = parameterTypes[i];
300                    if (parameterType instanceof TypeVariable) {
301                        parameterTypes[i] = ctor.getParameterTypes()[i];
302                    }
303                }
304                boxParameters(parameterTypes);            
305                Object[] result = new Object[constructorParameters.length];
306                Annotation[] bindings = getBindings(ctor.getParameterAnnotations());
307                for (int i = 0; i < constructorParameters.length; i++) {
308    
309                    result[i] = getParameter(container, ctor, i, parameterTypes[i],
310                            bindings[i], constructorParameters[i], injecteeAdapters[i]);
311                }
312                return result;
313            }
314    
315            public ComponentAdapter[] getInjecteeAdapters() {
316                return injecteeAdapters;
317            }
318    
319            public Parameter[] getParameters() {
320                return constructorParameters;
321            }
322        }
323    
324        @Override
325        public T getComponentInstance(final PicoContainer container, @SuppressWarnings("unused") Type into) throws PicoCompositionException {
326            if (instantiationGuard == null) {
327                instantiationGuard = new ThreadLocalCyclicDependencyGuard<T>() {
328                    @Override
329                    @SuppressWarnings("synthetic-access")
330                    public T run(Object instance) {
331                        CtorAndAdapters<T> ctorAndAdapters = getGreediestSatisfiableConstructor(guardedContainer, getComponentImplementation());
332                        ComponentMonitor componentMonitor = currentMonitor();
333                        Constructor<T> ctor = ctorAndAdapters.getConstructor();
334                        try {
335                            Object[] ctorParameters = ctorAndAdapters.getParameterArguments(guardedContainer);
336                            ctor = componentMonitor.instantiating(container, ConstructorInjector.this, ctor);
337                            if(ctorAndAdapters == null) {
338                                throw new NullPointerException("Component Monitor " + componentMonitor 
339                                                + " returned a null constructor from method 'instantiating' after passing in " + ctorAndAdapters);
340                            }
341                            long startTime = System.currentTimeMillis();
342                            T inst = newInstance(ctor, ctorParameters);
343                            componentMonitor.instantiated(container, ConstructorInjector.this,
344                                    ctor, inst, ctorParameters, System.currentTimeMillis() - startTime);
345                            return inst;
346                        } catch (InvocationTargetException e) {
347                            componentMonitor.instantiationFailed(container, ConstructorInjector.this, ctor, e);
348                            if (e.getTargetException() instanceof RuntimeException) {
349                                throw (RuntimeException) e.getTargetException();
350                            } else if (e.getTargetException() instanceof Error) {
351                                throw (Error) e.getTargetException();
352                            }
353                            throw new PicoCompositionException(e.getTargetException());
354                        } catch (InstantiationException e) {
355                            return caughtInstantiationException(componentMonitor, ctor, e, container);
356                        } catch (IllegalAccessException e) {
357                            return caughtIllegalAccessException(componentMonitor, ctor, e, container);
358    
359                        }
360                    }
361                };
362            }
363            instantiationGuard.setGuardedContainer(container);
364            T inst = instantiationGuard.observe(getComponentImplementation(), null);
365            decorate(inst, container);
366            return inst;
367        }
368    
369        private void decorate(T inst, PicoContainer container) {
370            if (enableEmjection) {
371                Emjection.setupEmjection(inst, container);
372            }
373        }
374    
375        private List<Constructor<T>> getSortedMatchingConstructors() {
376            List<Constructor<T>> matchingConstructors = new ArrayList<Constructor<T>>();
377            Constructor<T>[] allConstructors = getConstructors();
378            // filter out all constructors that will definately not match
379            for (Constructor<T> constructor : allConstructors) {
380                int modifiers = constructor.getModifiers();
381                if ((parameters == null || constructor.getParameterTypes().length == parameters.length)
382                        && (allowNonPublicClasses || (modifiers & Modifier.PUBLIC) != 0)) {
383                    if ((modifiers & Modifier.PUBLIC) == 0) {
384                        constructor.setAccessible(true);
385                    }
386                    matchingConstructors.add(constructor);
387                }
388            }
389            // optimize list of constructors moving the longest at the beginning
390            if (parameters == null) {               
391                Collections.sort(matchingConstructors, new Comparator<Constructor>() {
392                    public int compare(Constructor arg0, Constructor arg1) {
393                        return arg1.getParameterTypes().length - arg0.getParameterTypes().length;
394                    }
395                });
396            }
397            return matchingConstructors;
398        }
399    
400        private Constructor<T>[] getConstructors() {
401            return AccessController.doPrivileged(new PrivilegedAction<Constructor<T>[]>() {
402                public Constructor<T>[] run() {
403                    return (Constructor<T>[]) getComponentImplementation().getDeclaredConstructors();
404                }
405            });
406        }
407    
408        @Override
409        public void verify(final PicoContainer container) throws PicoCompositionException {
410            if (verifyingGuard == null) {
411                verifyingGuard = new ThreadLocalCyclicDependencyGuard() {
412                    @Override
413                    public Object run(Object instance) {
414                        final Constructor constructor = getGreediestSatisfiableConstructor(guardedContainer).getConstructor();
415                        final Class[] parameterTypes = constructor.getParameterTypes();
416                        final Parameter[] currentParameters = parameters != null ? parameters : createDefaultParameters(parameterTypes.length);
417                        for (int i = 0; i < currentParameters.length; i++) {
418                            currentParameters[i].verify(container, ConstructorInjector.this, box(parameterTypes[i]),
419                                new ParameterNameBinding(getParanamer(),  constructor, i),
420                                    useNames(), getBindings(constructor.getParameterAnnotations())[i]);
421                        }
422                        return null;
423                    }
424                };
425            }
426            verifyingGuard.setGuardedContainer(container);
427            verifyingGuard.observe(getComponentImplementation(), null);
428        }
429    
430        @Override
431        public String getDescriptor() {
432            return "ConstructorInjector-";
433        }
434    
435    
436    }