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