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