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 }