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 }