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: *****************************************************************************/package org.picocontainer.injectors;
010:
011: import org.picocontainer.ComponentMonitor;
012: import org.picocontainer.LifecycleStrategy;
013: import org.picocontainer.Parameter;
014: import org.picocontainer.PicoCompositionException;
015: import org.picocontainer.PicoContainer;
016: import org.picocontainer.lifecycle.NullLifecycleStrategy;
017: import org.picocontainer.monitors.NullComponentMonitor;
018:
019: import java.lang.reflect.Constructor;
020: import java.lang.reflect.InvocationTargetException;
021: import java.lang.reflect.Modifier;
022: import java.lang.annotation.Annotation;
023: import java.security.AccessController;
024: import java.security.PrivilegedAction;
025: import java.util.ArrayList;
026: import java.util.Arrays;
027: import java.util.Collections;
028: import java.util.Comparator;
029: import java.util.HashSet;
030: import java.util.List;
031: import java.util.Set;
032:
033: /**
034: * Injection will happen through a constructor for the component.
035: *
036: * @author Paul Hammant
037: * @author Aslak Hellesøy
038: * @author Jon Tirsén
039: * @author Zohar Melamed
040: * @author Jörg Schaible
041: * @author Mauro Talevi
042: */
043: public class ConstructorInjector<T> extends SingleMemberInjector<T> {
044: /**
045: * Serialization UUID.
046: */
047: private static final long serialVersionUID = 3663020107106785481L;
048:
049: private transient List<Constructor<T>> sortedMatchingConstructors;
050: private transient ThreadLocalCyclicDependencyGuard<T> instantiationGuard;
051:
052: /**
053: * Constructor injector that uses no monitor and no lifecycle adapter. This is a more
054: * convenient constructor for use when instantiating a constructor injector directly.
055: * @param componentKey the search key for this implementation
056: * @param componentImplementation the concrete implementation
057: * @param parameters the parameters used for initialization
058: */
059: public ConstructorInjector(final Object componentKey,
060: final Class<?> componentImplementation,
061: Parameter... parameters) {
062: this (componentKey, componentImplementation, parameters,
063: new NullComponentMonitor(),
064: new NullLifecycleStrategy(), false);
065: }
066:
067: /**
068: * Creates a ConstructorInjector
069: *
070: * @param componentKey the search key for this implementation
071: * @param componentImplementation the concrete implementation
072: * @param parameters the parameters to use for the initialization
073: * @param monitor the component monitor used by this addAdapter
074: * @param lifecycleStrategy the component lifecycle strategy used by this addAdapter
075: * @param useNames use argument names when looking up dependencies
076: * @throws org.picocontainer.injectors.AbstractInjector.NotConcreteRegistrationException
077: * if the implementation is not a concrete class.
078: * @throws NullPointerException if one of the parameters is <code>null</code>
079: */
080: public ConstructorInjector(final Object componentKey,
081: final Class componentImplementation,
082: Parameter[] parameters, ComponentMonitor monitor,
083: LifecycleStrategy lifecycleStrategy, boolean useNames)
084: throws NotConcreteRegistrationException {
085: super (componentKey, componentImplementation, parameters,
086: monitor, lifecycleStrategy, useNames);
087: }
088:
089: protected Constructor<T> getGreediestSatisfiableConstructor(
090: PicoContainer container) throws PicoCompositionException {
091: final Set<Constructor> conflicts = new HashSet<Constructor>();
092: final Set<List<Class>> unsatisfiableDependencyTypes = new HashSet<List<Class>>();
093: if (sortedMatchingConstructors == null) {
094: sortedMatchingConstructors = getSortedMatchingConstructors();
095: }
096: Constructor<T> greediestConstructor = null;
097: int lastSatisfiableConstructorSize = -1;
098: Class<?> unsatisfiedDependencyType = null;
099: for (final Constructor<T> sortedMatchingConstructor : sortedMatchingConstructors) {
100: boolean failedDependency = false;
101: Class[] parameterTypes = sortedMatchingConstructor
102: .getParameterTypes();
103: Annotation[] bindings = getBindings(sortedMatchingConstructor
104: .getParameterAnnotations());
105: Parameter[] currentParameters = parameters != null ? parameters
106: : createDefaultParameters(parameterTypes);
107:
108: // remember: all constructors with less arguments than the given parameters are filtered out already
109: for (int j = 0; j < currentParameters.length; j++) {
110: // check whether this constructor is statisfiable
111: Class<?> boxed = box(parameterTypes[j]);
112: boolean un = useNames();
113: if (currentParameters[j].isResolvable(container, this ,
114: boxed, new ParameterNameBinding(getParanamer(),
115: getComponentImplementation(),
116: sortedMatchingConstructor, j), un,
117: bindings[j])) {
118: continue;
119: }
120: unsatisfiableDependencyTypes.add(Arrays
121: .asList(parameterTypes));
122: unsatisfiedDependencyType = box(parameterTypes[j]);
123: failedDependency = true;
124: break;
125: }
126:
127: if (greediestConstructor != null
128: && parameterTypes.length != lastSatisfiableConstructorSize) {
129: if (conflicts.isEmpty()) {
130: // we found our match [aka. greedy and satisfied]
131: return greediestConstructor;
132: } else {
133: // fits although not greedy
134: conflicts.add(sortedMatchingConstructor);
135: }
136: } else if (!failedDependency
137: && lastSatisfiableConstructorSize == parameterTypes.length) {
138: // satisfied and same size as previous one?
139: conflicts.add(sortedMatchingConstructor);
140: conflicts.add(greediestConstructor);
141: } else if (!failedDependency) {
142: greediestConstructor = sortedMatchingConstructor;
143: lastSatisfiableConstructorSize = parameterTypes.length;
144: }
145: }
146: if (!conflicts.isEmpty()) {
147: throw new PicoCompositionException(conflicts.size()
148: + " satisfiable constructors is too many for '"
149: + getComponentImplementation()
150: + "'. Constructor List:"
151: + conflicts.toString().replace(
152: getComponentImplementation().getName(),
153: "<init>").replace("public <i", "<i"));
154: } else if (greediestConstructor == null
155: && !unsatisfiableDependencyTypes.isEmpty()) {
156: throw new UnsatisfiableDependenciesException(this ,
157: unsatisfiedDependencyType,
158: unsatisfiableDependencyTypes, container);
159: } else if (greediestConstructor == null) {
160: // be nice to the user, show all constructors that were filtered out
161: final Set<Constructor> nonMatching = new HashSet<Constructor>();
162: for (Constructor constructor : getConstructors()) {
163: nonMatching.add(constructor);
164: }
165: throw new PicoCompositionException(
166: "Either the specified parameters do not match any of the following constructors: "
167: + nonMatching.toString()
168: + "; OR the constructors were not accessible for '"
169: + getComponentImplementation().getName()
170: + "'");
171: }
172: return greediestConstructor;
173: }
174:
175: public T getComponentInstance(final PicoContainer container)
176: throws PicoCompositionException {
177: if (instantiationGuard == null) {
178: instantiationGuard = new ThreadLocalCyclicDependencyGuard<T>() {
179: public T run() {
180: Constructor<T> constructor;
181: try {
182: constructor = getGreediestSatisfiableConstructor(guardedContainer);
183: } catch (AmbiguousComponentResolutionException e) {
184: e.setComponent(getComponentImplementation());
185: throw e;
186: }
187: ComponentMonitor componentMonitor = currentMonitor();
188: try {
189: Object[] parameters = getMemberArguments(
190: guardedContainer, constructor);
191: constructor = componentMonitor.instantiating(
192: container, ConstructorInjector.this ,
193: constructor);
194: if (constructor == null) {
195: throw new NullPointerException(
196: "Component Monitor "
197: + componentMonitor
198: + " returned a null constructor from method 'instantiating' after passing in "
199: + constructor);
200: }
201: long startTime = System.currentTimeMillis();
202: T inst = newInstance(constructor, parameters);
203: componentMonitor.instantiated(container,
204: ConstructorInjector.this , constructor,
205: inst, parameters, System
206: .currentTimeMillis()
207: - startTime);
208: return inst;
209: } catch (InvocationTargetException e) {
210: componentMonitor.instantiationFailed(container,
211: ConstructorInjector.this , constructor,
212: e);
213: if (e.getTargetException() instanceof RuntimeException) {
214: throw (RuntimeException) e
215: .getTargetException();
216: } else if (e.getTargetException() instanceof Error) {
217: throw (Error) e.getTargetException();
218: }
219: throw new PicoCompositionException(e
220: .getTargetException());
221: } catch (InstantiationException e) {
222: return caughtInstantiationException(
223: componentMonitor, constructor, e,
224: container);
225: } catch (IllegalAccessException e) {
226: return caughtIllegalAccessException(
227: componentMonitor, constructor, e,
228: container);
229:
230: }
231: }
232: };
233: }
234: instantiationGuard.setGuardedContainer(container);
235: return instantiationGuard.observe(getComponentImplementation());
236: }
237:
238: protected Object[] getMemberArguments(PicoContainer container,
239: final Constructor ctor) {
240: return super .getMemberArguments(container, ctor, ctor
241: .getParameterTypes(), getBindings(ctor
242: .getParameterAnnotations()));
243: }
244:
245: private List<Constructor<T>> getSortedMatchingConstructors() {
246: List<Constructor<T>> matchingConstructors = new ArrayList<Constructor<T>>();
247: Constructor<T>[] allConstructors = getConstructors();
248: // filter out all constructors that will definately not match
249: for (Constructor<T> constructor : allConstructors) {
250: if ((parameters == null || constructor.getParameterTypes().length == parameters.length)
251: && (constructor.getModifiers() & Modifier.PUBLIC) != 0) {
252: matchingConstructors.add(constructor);
253: }
254: }
255: // optimize list of constructors moving the longest at the beginning
256: if (parameters == null) {
257: Collections.sort(matchingConstructors,
258: new Comparator<Constructor>() {
259: public int compare(Constructor arg0,
260: Constructor arg1) {
261: return arg1.getParameterTypes().length
262: - arg0.getParameterTypes().length;
263: }
264: });
265: }
266: return matchingConstructors;
267: }
268:
269: private Constructor<T>[] getConstructors() {
270: return AccessController
271: .doPrivileged(new PrivilegedAction<Constructor<T>[]>() {
272: public Constructor<T>[] run() {
273: return (Constructor<T>[]) getComponentImplementation()
274: .getDeclaredConstructors();
275: }
276: });
277: }
278:
279: public void verify(final PicoContainer container)
280: throws PicoCompositionException {
281: if (verifyingGuard == null) {
282: verifyingGuard = new ThreadLocalCyclicDependencyGuard() {
283: public Object run() {
284: final Constructor constructor = getGreediestSatisfiableConstructor(guardedContainer);
285: final Class[] parameterTypes = constructor
286: .getParameterTypes();
287: final Parameter[] currentParameters = parameters != null ? parameters
288: : createDefaultParameters(parameterTypes);
289: for (int i = 0; i < currentParameters.length; i++) {
290: currentParameters[i].verify(container,
291: ConstructorInjector.this ,
292: box(parameterTypes[i]),
293: new ParameterNameBinding(
294: getParanamer(),
295: getComponentImplementation(),
296: constructor, i), useNames(),
297: getBindings(constructor
298: .getParameterAnnotations())[i]);
299: }
300: return null;
301: }
302: };
303: }
304: verifyingGuard.setGuardedContainer(container);
305: verifyingGuard.observe(getComponentImplementation());
306: }
307:
308: public String getDescriptor() {
309: return "ConstructorInjector-";
310: }
311:
312: }
|