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