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
011package org.picocontainer.injectors;
012
013import org.picocontainer.ComponentAdapter;
014import org.picocontainer.ComponentMonitor;
015import org.picocontainer.Emjection;
016import org.picocontainer.NameBinding;
017import org.picocontainer.Parameter;
018import org.picocontainer.PicoCompositionException;
019import org.picocontainer.PicoContainer;
020import org.picocontainer.monitors.NullComponentMonitor;
021
022import java.lang.annotation.Annotation;
023import java.lang.reflect.Constructor;
024import java.lang.reflect.InvocationTargetException;
025import java.lang.reflect.Modifier;
026import java.lang.reflect.Type;
027import java.lang.reflect.TypeVariable;
028import java.security.AccessController;
029import java.security.PrivilegedAction;
030import java.util.ArrayList;
031import java.util.Collections;
032import java.util.Comparator;
033import java.util.HashMap;
034import java.util.HashSet;
035import java.util.Iterator;
036import java.util.List;
037import java.util.Map;
038import 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")
051public 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}