001    package org.picocontainer.injectors;
002    
003    import org.picocontainer.Parameter;
004    import org.picocontainer.ComponentMonitor;
005    import org.picocontainer.LifecycleStrategy;
006    import org.picocontainer.PicoCompositionException;
007    import org.picocontainer.PicoContainer;
008    import org.picocontainer.ParameterName;
009    
010    import java.lang.reflect.Member;
011    import java.lang.reflect.Constructor;
012    import java.lang.reflect.InvocationTargetException;
013    import java.lang.reflect.Method;
014    import java.util.List;
015    import java.util.ArrayList;
016    import java.util.Collections;
017    import java.util.Set;
018    import java.util.HashSet;
019    import java.security.AccessController;
020    import java.security.PrivilegedAction;
021    
022    public abstract class IterativeInjector extends AbstractInjector {
023        private transient ThreadLocalCyclicDependencyGuard instantiationGuard;
024        protected transient List<Member> injectionMembers;
025        protected transient Class[] injectionTypes;
026    
027        /**
028         * Constructs a IterativeInjector
029         *
030         * @param componentKey            the search key for this implementation
031         * @param componentImplementation the concrete implementation
032         * @param parameters              the parameters to use for the initialization
033         * @param monitor                 the component monitor used by this addAdapter
034         * @param lifecycleStrategy       the component lifecycle strategy used by this addAdapter
035         * @throws org.picocontainer.injectors.AbstractInjector.NotConcreteRegistrationException
036         *                              if the implementation is not a concrete class.
037         * @throws NullPointerException if one of the parameters is <code>null</code>
038         */
039        public IterativeInjector(final Object componentKey, final Class componentImplementation, Parameter[] parameters, ComponentMonitor monitor, LifecycleStrategy lifecycleStrategy) throws  NotConcreteRegistrationException {
040            super(componentKey, componentImplementation, parameters, monitor, lifecycleStrategy);
041        }
042    
043    
044        protected Constructor getConstructor()  {
045            Object retVal = AccessController.doPrivileged(new PrivilegedAction() {
046                public Object run() {
047                    try {
048                        return getComponentImplementation().getConstructor((Class[])null);
049                    } catch (NoSuchMethodException e) {
050                        return new PicoCompositionException(e);
051                    } catch (SecurityException e) {
052                        return new PicoCompositionException(e);
053                    }
054                }
055            });
056            if (retVal instanceof Constructor) {
057                return (Constructor) retVal;
058            } else {
059                throw (PicoCompositionException) retVal;
060            }
061        }
062    
063        private Parameter[] getMatchingParameterListForSetters(PicoContainer container) throws PicoCompositionException {
064            if (injectionMembers == null) {
065                initializeInjectionMembersAndTypeLists();
066            }
067    
068            final List<Object> matchingParameterList = new ArrayList<Object>(Collections.nCopies(injectionMembers.size(), null));
069            final Set<Integer> nonMatchingParameterPositions = new HashSet<Integer>();
070            final Parameter[] currentParameters = parameters != null ? parameters : createDefaultParameters(injectionTypes);
071            for (int i = 0; i < currentParameters.length; i++) {
072                final Parameter parameter = currentParameters[i];
073                boolean failedDependency = true;
074                for (int j = 0; j < injectionTypes.length; j++) {
075                    if (matchingParameterList.get(j) == null && parameter.isResolvable(container, this, injectionTypes[j],
076                                                                                       new IterativeInjectorParameterName())) {
077                        matchingParameterList.set(j, parameter);
078                        failedDependency = false;
079                        break;
080                    }
081                }
082                if (failedDependency) {
083                    nonMatchingParameterPositions.add(i);
084                }
085            }
086    
087            final Set<Class> unsatisfiableDependencyTypes = new HashSet<Class>();
088            for (int i = 0; i < matchingParameterList.size(); i++) {
089                if (matchingParameterList.get(i) == null) {
090                    unsatisfiableDependencyTypes.add(injectionTypes[i]);
091                }
092            }
093            if (unsatisfiableDependencyTypes.size() > 0) {
094                unsatisfiedDependencies(container, unsatisfiableDependencyTypes);
095            } else if (nonMatchingParameterPositions.size() > 0) {
096                throw new PicoCompositionException("Following parameters do not match any of the injectionMembers for " + getComponentImplementation() + ": " + nonMatchingParameterPositions.toString());
097            }
098            return matchingParameterList.toArray(new Parameter[matchingParameterList.size()]);
099        }
100    
101        protected void unsatisfiedDependencies(PicoContainer container, Set<Class> unsatisfiableDependencyTypes) {
102            throw new UnsatisfiableDependenciesException(this, null, unsatisfiableDependencyTypes, container);
103        }
104    
105        public Object getComponentInstance(final PicoContainer container) throws PicoCompositionException {
106            final Constructor constructor = getConstructor();
107            if (instantiationGuard == null) {
108                instantiationGuard = new ThreadLocalCyclicDependencyGuard() {
109                    public Object run() {
110                        final Parameter[] matchingParameters = getMatchingParameterListForSetters(guardedContainer);
111                        ComponentMonitor componentMonitor = currentMonitor();
112                        Object componentInstance;
113    
114                        componentInstance = getOrMakeInstance(container, constructor, componentMonitor);
115                        Member member = null;
116                        Object injected[] = new Object[injectionMembers.size()];
117                        try {
118                            for (int i = 0; i < injectionMembers.size(); i++) {
119                                member = injectionMembers.get(i);
120                                componentMonitor.invoking(container, IterativeInjector.this, member, componentInstance);
121                                if (matchingParameters[i] == null) {
122                                    continue;
123                                }
124                                Object toInject = matchingParameters[i].resolveInstance(guardedContainer, IterativeInjector.this, injectionTypes[i],
125                                                                                        new IterativeInjectorParameterName());
126                                injectIntoMember(member, componentInstance, toInject);
127                                injected[i] = toInject;
128                            }
129                            return componentInstance;
130                        } catch (InvocationTargetException e) {
131                            return caughtInvocationTargetException(componentMonitor, member, componentInstance, e);
132                        } catch (IllegalAccessException e) {
133                            return caughtIllegalAccessException(componentMonitor, member, componentInstance, e);
134                        }
135    
136                    }
137                };
138            }
139            instantiationGuard.setGuardedContainer(container);
140            return instantiationGuard.observe(getComponentImplementation());
141        }
142    
143        protected Object getOrMakeInstance(PicoContainer container,
144                                           Constructor constructor,
145                                           ComponentMonitor componentMonitor) {
146            long startTime = System.currentTimeMillis();
147            Constructor constructorToUse = componentMonitor.instantiating(container,
148                                                                          IterativeInjector.this, constructor);
149            Object componentInstance;
150            try {
151                componentInstance = newInstance(constructorToUse, null);
152            } catch (InvocationTargetException e) {
153                componentMonitor.instantiationFailed(container, IterativeInjector.this, constructorToUse, e);
154                if (e.getTargetException() instanceof RuntimeException) {
155                    throw (RuntimeException)e.getTargetException();
156                } else if (e.getTargetException() instanceof Error) {
157                    throw (Error)e.getTargetException();
158                }
159                throw new PicoCompositionException(e.getTargetException());
160            } catch (InstantiationException e) {
161                return caughtInstantiationException(componentMonitor, constructor, e, container);
162            } catch (IllegalAccessException e) {
163                return caughtIllegalAccessException(componentMonitor, constructor, e, container);
164            }
165            componentMonitor.instantiated(container,
166                                          IterativeInjector.this,
167                                          constructorToUse,
168                                          componentInstance,
169                                          null,
170                                          System.currentTimeMillis() - startTime);
171            return componentInstance;
172        }
173    
174        protected void injectIntoMember(Member member, Object componentInstance, Object toInject)
175            throws IllegalAccessException, InvocationTargetException {
176            ((Method)member).invoke(componentInstance, toInject);
177        }
178    
179        public void verify(final PicoContainer container) throws PicoCompositionException {
180            if (verifyingGuard == null) {
181                verifyingGuard = new ThreadLocalCyclicDependencyGuard() {
182                    public Object run() {
183                        final Parameter[] currentParameters = getMatchingParameterListForSetters(guardedContainer);
184                        for (int i = 0; i < currentParameters.length; i++) {
185                            currentParameters[i].verify(container, IterativeInjector.this, injectionTypes[i],
186                                                        new IterativeInjectorParameterName());
187                        }
188                        return null;
189                    }
190                };
191            }
192            verifyingGuard.setGuardedContainer(container);
193            verifyingGuard.observe(getComponentImplementation());
194        }
195    
196        protected void initializeInjectionMembersAndTypeLists() {
197            injectionMembers = new ArrayList<Member>();
198            final List<Class> typeList = new ArrayList<Class>();
199            final Method[] methods = getMethods();
200            for (final Method method : methods) {
201                final Class[] parameterTypes = method.getParameterTypes();
202                // We're only interested if there is only one parameter and the method name is bean-style.
203                if (parameterTypes.length == 1) {
204                    boolean isInjector = isInjectorMethod(method);
205                    if (isInjector) {
206                        injectionMembers.add(method);
207                        typeList.add(parameterTypes[0]);
208                    }
209                }
210            }
211            injectionTypes = typeList.toArray(new Class[0]);
212        }
213    
214        protected boolean isInjectorMethod(Method method) {
215            return false;
216        }
217    
218        private Method[] getMethods() {
219            return (Method[]) AccessController.doPrivileged(new PrivilegedAction() {
220                public Object run() {
221                    return getComponentImplementation().getMethods();
222                }
223            });
224        }
225    
226        private static class IterativeInjectorParameterName implements ParameterName {
227            public String getName() {
228                return ""; // TODO
229            }
230        }
231    
232    }