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