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 }