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øy
046     * @author Jon Tirsén
047     * @author Zohar Melamed
048     * @author Jö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    }