001: package org.picocontainer.injectors;
002:
003: import org.picocontainer.ComponentMonitor;
004: import org.picocontainer.LifecycleStrategy;
005: import org.picocontainer.Parameter;
006: import org.picocontainer.NameBinding;
007: import org.picocontainer.PicoCompositionException;
008: import org.picocontainer.PicoContainer;
009: import org.picocontainer.annotations.Bind;
010:
011: import java.lang.reflect.AccessibleObject;
012: import java.lang.reflect.Constructor;
013: import java.lang.reflect.InvocationTargetException;
014: import java.lang.reflect.Member;
015: import java.lang.reflect.Method;
016: import java.lang.annotation.Annotation;
017: import java.security.AccessController;
018: import java.security.PrivilegedAction;
019: import java.util.ArrayList;
020: import java.util.Collections;
021: import java.util.HashSet;
022: import java.util.List;
023: import java.util.Set;
024:
025: import com.thoughtworks.paranamer.CachingParanamer;
026: import com.thoughtworks.paranamer.Paranamer;
027:
028: /**
029: * Injection will happen iteratively after component instantiation
030: */
031: public abstract class IterativeInjector extends AbstractInjector {
032: private transient ThreadLocalCyclicDependencyGuard instantiationGuard;
033: protected transient List<AccessibleObject> injectionMembers;
034: protected transient Class[] injectionTypes;
035: protected transient Annotation[] bindings;
036:
037: private transient CachingParanamer paranamer = new CachingParanamer();
038:
039: /**
040: * Constructs a IterativeInjector
041: *
042: * @param componentKey the search key for this implementation
043: * @param componentImplementation the concrete implementation
044: * @param parameters the parameters to use for the initialization
045: * @param monitor the component monitor used by this addAdapter
046: * @param lifecycleStrategy the component lifecycle strategy used by this addAdapter
047: * @param useNames use argument names when looking up dependencies
048: * @throws org.picocontainer.injectors.AbstractInjector.NotConcreteRegistrationException
049: * if the implementation is not a concrete class.
050: * @throws NullPointerException if one of the parameters is <code>null</code>
051: */
052: public IterativeInjector(final Object componentKey,
053: final Class componentImplementation,
054: Parameter[] parameters, ComponentMonitor monitor,
055: LifecycleStrategy lifecycleStrategy, boolean useNames)
056: throws NotConcreteRegistrationException {
057: super (componentKey, componentImplementation, parameters,
058: monitor, lifecycleStrategy, useNames);
059: }
060:
061: protected CachingParanamer getParanamer() {
062: return paranamer;
063: }
064:
065: protected Constructor getConstructor() {
066: Object retVal = AccessController
067: .doPrivileged(new PrivilegedAction() {
068: public Object run() {
069: try {
070: return getComponentImplementation()
071: .getConstructor((Class[]) null);
072: } catch (NoSuchMethodException e) {
073: return new PicoCompositionException(e);
074: } catch (SecurityException e) {
075: return new PicoCompositionException(e);
076: }
077: }
078: });
079: if (retVal instanceof Constructor) {
080: return (Constructor) retVal;
081: } else {
082: throw (PicoCompositionException) retVal;
083: }
084: }
085:
086: private Parameter[] getMatchingParameterListForSetters(
087: PicoContainer container) throws PicoCompositionException {
088: if (injectionMembers == null) {
089: initializeInjectionMembersAndTypeLists();
090: }
091:
092: final List<Object> matchingParameterList = new ArrayList<Object>(
093: Collections.nCopies(injectionMembers.size(), null));
094: final Set<Integer> nonMatchingParameterPositions = new HashSet<Integer>();
095: final Parameter[] currentParameters = parameters != null ? parameters
096: : createDefaultParameters(injectionTypes);
097: for (int i = 0; i < currentParameters.length; i++) {
098: final Parameter parameter = currentParameters[i];
099: boolean failedDependency = true;
100: for (int j = 0; j < injectionTypes.length; j++) {
101: Object o = matchingParameterList.get(j);
102: AccessibleObject member = injectionMembers.get(i);
103: boolean b = parameter.isResolvable(container, this ,
104: injectionTypes[j],
105: makeParameterNameImpl(member), useNames(),
106: bindings[j]);
107: if (o == null && b) {
108: matchingParameterList.set(j, parameter);
109: failedDependency = false;
110: break;
111: }
112: }
113: if (failedDependency) {
114: nonMatchingParameterPositions.add(i);
115: }
116: }
117:
118: final Set<Class> unsatisfiableDependencyTypes = new HashSet<Class>();
119: for (int i = 0; i < matchingParameterList.size(); i++) {
120: if (matchingParameterList.get(i) == null) {
121: unsatisfiableDependencyTypes.add(injectionTypes[i]);
122: }
123: }
124: if (unsatisfiableDependencyTypes.size() > 0) {
125: unsatisfiedDependencies(container,
126: unsatisfiableDependencyTypes);
127: } else if (nonMatchingParameterPositions.size() > 0) {
128: throw new PicoCompositionException(
129: "Following parameters do not match any of the injectionMembers for "
130: + getComponentImplementation() + ": "
131: + nonMatchingParameterPositions.toString());
132: }
133: return matchingParameterList
134: .toArray(new Parameter[matchingParameterList.size()]);
135: }
136:
137: protected NameBinding makeParameterNameImpl(AccessibleObject member) {
138: return new ParameterNameBinding(paranamer,
139: getComponentImplementation(), member, 0);
140: }
141:
142: protected void unsatisfiedDependencies(PicoContainer container,
143: Set<Class> unsatisfiableDependencyTypes) {
144: throw new UnsatisfiableDependenciesException(this , null,
145: unsatisfiableDependencyTypes, container);
146: }
147:
148: public Object getComponentInstance(final PicoContainer container)
149: throws PicoCompositionException {
150: final Constructor constructor = getConstructor();
151: if (instantiationGuard == null) {
152: instantiationGuard = new ThreadLocalCyclicDependencyGuard() {
153: public Object run() {
154: final Parameter[] matchingParameters = getMatchingParameterListForSetters(guardedContainer);
155: ComponentMonitor componentMonitor = currentMonitor();
156: Object componentInstance;
157:
158: componentInstance = getOrMakeInstance(container,
159: constructor, componentMonitor);
160: AccessibleObject member = null;
161: Object injected[] = new Object[injectionMembers
162: .size()];
163: try {
164: for (int i = 0; i < injectionMembers.size(); i++) {
165: member = injectionMembers.get(i);
166: componentMonitor.invoking(container,
167: IterativeInjector.this ,
168: (Member) member, componentInstance);
169: if (matchingParameters[i] == null) {
170: continue;
171: }
172: Object toInject = matchingParameters[i]
173: .resolveInstance(
174: guardedContainer,
175: IterativeInjector.this ,
176: injectionTypes[i],
177: makeParameterNameImpl(injectionMembers
178: .get(i)),
179: useNames(), bindings[i]);
180: injectIntoMember(member, componentInstance,
181: toInject);
182: injected[i] = toInject;
183: }
184: return componentInstance;
185: } catch (InvocationTargetException e) {
186: return caughtInvocationTargetException(
187: componentMonitor, (Member) member,
188: componentInstance, e);
189: } catch (IllegalAccessException e) {
190: return caughtIllegalAccessException(
191: componentMonitor, (Member) member,
192: componentInstance, e);
193: }
194:
195: }
196: };
197: }
198: instantiationGuard.setGuardedContainer(container);
199: return instantiationGuard.observe(getComponentImplementation());
200: }
201:
202: protected Object getOrMakeInstance(PicoContainer container,
203: Constructor constructor, ComponentMonitor componentMonitor) {
204: long startTime = System.currentTimeMillis();
205: Constructor constructorToUse = componentMonitor.instantiating(
206: container, IterativeInjector.this , constructor);
207: Object componentInstance;
208: try {
209: componentInstance = newInstance(constructorToUse, null);
210: } catch (InvocationTargetException e) {
211: componentMonitor.instantiationFailed(container,
212: IterativeInjector.this , constructorToUse, e);
213: if (e.getTargetException() instanceof RuntimeException) {
214: throw (RuntimeException) e.getTargetException();
215: } else if (e.getTargetException() instanceof Error) {
216: throw (Error) e.getTargetException();
217: }
218: throw new PicoCompositionException(e.getTargetException());
219: } catch (InstantiationException e) {
220: return caughtInstantiationException(componentMonitor,
221: constructor, e, container);
222: } catch (IllegalAccessException e) {
223: return caughtIllegalAccessException(componentMonitor,
224: constructor, e, container);
225: }
226: componentMonitor.instantiated(container,
227: IterativeInjector.this , constructorToUse,
228: componentInstance, null, System.currentTimeMillis()
229: - startTime);
230: return componentInstance;
231: }
232:
233: protected void injectIntoMember(AccessibleObject member,
234: Object componentInstance, Object toInject)
235: throws IllegalAccessException, InvocationTargetException {
236: ((Method) member).invoke(componentInstance, toInject);
237: }
238:
239: public void verify(final PicoContainer container)
240: throws PicoCompositionException {
241: if (verifyingGuard == null) {
242: verifyingGuard = new ThreadLocalCyclicDependencyGuard() {
243: public Object run() {
244: final Parameter[] currentParameters = getMatchingParameterListForSetters(guardedContainer);
245: for (int i = 0; i < currentParameters.length; i++) {
246: currentParameters[i].verify(container,
247: IterativeInjector.this ,
248: injectionTypes[i],
249: makeParameterNameImpl(injectionMembers
250: .get(i)), useNames(),
251: bindings[i]);
252: }
253: return null;
254: }
255: };
256: }
257: verifyingGuard.setGuardedContainer(container);
258: verifyingGuard.observe(getComponentImplementation());
259: }
260:
261: protected void initializeInjectionMembersAndTypeLists() {
262: injectionMembers = new ArrayList<AccessibleObject>();
263: List<Annotation> bingingIds = new ArrayList<Annotation>();
264: final List<Class> typeList = new ArrayList<Class>();
265: final Method[] methods = getMethods();
266: for (final Method method : methods) {
267: final Class[] parameterTypes = method.getParameterTypes();
268: // We're only interested if there is only one parameter and the method name is bean-style.
269: if (parameterTypes.length == 1) {
270: boolean isInjector = isInjectorMethod(method);
271: if (isInjector) {
272: injectionMembers.add(method);
273: typeList.add(box(parameterTypes[0]));
274: bingingIds.add(getBindings(method, 0));
275: }
276: }
277: }
278: injectionTypes = typeList.toArray(new Class[0]);
279: bindings = bingingIds.toArray(new Annotation[0]);
280: }
281:
282: private Annotation getBindings(Method method, int i) {
283: Annotation[][] parameterAnnotations = method
284: .getParameterAnnotations();
285: if (parameterAnnotations.length >= i + 1) {
286: Annotation[] o = parameterAnnotations[i];
287: for (int j = 0; j < o.length; j++) {
288: Annotation annotation = o[j];
289: if (o[j].annotationType().getAnnotation(Bind.class) != null) {
290: return o[j];
291: }
292: }
293: return null;
294:
295: }
296: if (parameterAnnotations != null) {
297: //return ((Bind) method.getAnnotation(Bind.class)).id();
298: System.out.println("");
299: }
300: return null;
301:
302: }
303:
304: protected boolean isInjectorMethod(Method method) {
305: return false;
306: }
307:
308: private Method[] getMethods() {
309: return (Method[]) AccessController
310: .doPrivileged(new PrivilegedAction() {
311: public Object run() {
312: return getComponentImplementation()
313: .getMethods();
314: }
315: });
316: }
317:
318: }
|