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