    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    }