    001    /*****************************************************************************
    002     * Copyright (c) PicoContainer Organization. All rights reserved.            *
    003     * ------------------------------------------------------------------------- *
    004     * The software in this package is published under the terms of the BSD      *
    005     * style license a copy of which has been included with this distribution in *
    006     * the LICENSE.txt file.                                                     *
    007     *                                                                           *
    008     *****************************************************************************/
    009
    010    package org.picocontainer.injectors;
    011
    012    import java.lang.reflect.InvocationTargetException;
    013    import java.lang.reflect.Method;
    014    import java.lang.reflect.Type;
    015    import java.lang.reflect.Member;
    016    import java.lang.reflect.AccessibleObject;
    017    import java.lang.annotation.Annotation;
    018
    019    import org.picocontainer.ComponentMonitor;
    020    import org.picocontainer.LifecycleStrategy;
    021    import org.picocontainer.Parameter;
    022    import org.picocontainer.PicoCompositionException;
    023    import org.picocontainer.PicoContainer;
    024    import org.picocontainer.annotations.Nullable;
    025
    026    /**
    027     * Injection will happen through a single method for the component.
    028     *
    029     * Most likely it is a method called 'inject', though that can be overridden.
    030     *
    031     * @author Paul Hammant
    032     * @author Aslak Helles&oslash;y
    033     * @author Jon Tirs&eacute;n
    034     * @author Zohar Melamed
    035     * @author J&ouml;rg Schaible
    036     * @author Mauro Talevi
    037     */
    038    @SuppressWarnings("serial")
    039    public class MethodInjector<T> extends SingleMemberInjector<T> {
    040        private transient ThreadLocalCyclicDependencyGuard instantiationGuard;
    041        private final String methodName;
    042
    043        /**
    044         * Creates a MethodInjector
    045         *
    046         * @param componentKey            the search key for this implementation
    047         * @param componentImplementation the concrete implementation
    048         * @param parameters              the parameters to use for the initialization
    049         * @param monitor                 the component monitor used by this addAdapter
    050         * @param lifecycleStrategy       the component lifecycle strategy used by this addAdapter
    051         * @param methodName              the method name
    052         * @param useNames                use argument names when looking up dependencies
    053         * @throws AbstractInjector.NotConcreteRegistrationException
    054         *                              if the implementation is not a concrete class.
    055         * @throws NullPointerException if one of the parameters is <code>null</code>
    056         */
    057        public MethodInjector(final Object componentKey, final Class componentImplementation, Parameter[] parameters, ComponentMonitor monitor,
    058                              LifecycleStrategy lifecycleStrategy, String methodName, boolean useNames) throws AbstractInjector.NotConcreteRegistrationException {
    059            super(componentKey, componentImplementation, parameters, monitor, lifecycleStrategy, useNames);
    060            this.methodName = methodName;
    061        }
    062
    063        protected Method getInjectorMethod() {
    064            Method[] methods = new Method[0];
    065            try {
    066                methods = super.getComponentImplementation().getMethods();
    067            } catch (AmbiguousComponentResolutionException e) {
    068                e.setComponent(getComponentImplementation());
    069                throw e;
    070            }
    071            for (Method method : methods) {
    072                if (method.getName().equals(methodName)) {
    073                    return method;
    074                }
    075            }
    076            return null;
    077        }
    078
    079        public T getComponentInstance(final PicoContainer container, Type into) throws PicoCompositionException {
    080            if (instantiationGuard == null) {
    081                instantiationGuard = new ThreadLocalCyclicDependencyGuard() {
    082                    public Object run() {
    083                        Method method = getInjectorMethod();
    084                        T inst = null;
    085                        ComponentMonitor componentMonitor = currentMonitor();
    086                        try {
    087                            componentMonitor.instantiating(container, MethodInjector.this, null);
    088                            long startTime = System.currentTimeMillis();
    089                            Object[] parameters = null;
    090                            inst = getComponentImplementation().newInstance();
    091                            if (method != null) {
    092                                parameters = getMemberArguments(guardedContainer, method);
    093                                invokeMethod(method, parameters, inst, container);
    094                            }
    095                            componentMonitor.instantiated(container, MethodInjector.this,
    096                                                          null, inst, parameters, System.currentTimeMillis() - startTime);
    097                            return inst;
    098                        } catch (InstantiationException e) {
    099                            return caughtInstantiationException(componentMonitor, null, e, container);
    100                        } catch (IllegalAccessException e) {
    101                            return caughtIllegalAccessException(componentMonitor, method, inst, e);
    102
    103                        }
    104                    }
    105                };
    106            }
    107            instantiationGuard.setGuardedContainer(container);
    108            return (T) instantiationGuard.observe(getComponentImplementation());
    109        }
    110
    111        protected Object[] getMemberArguments(PicoContainer container, final Method method) {
    112            return super.getMemberArguments(container, method, method.getParameterTypes(), getBindings(method.getParameterAnnotations()));
    113        }
    114
    115        @Override
    116        public Object decorateComponentInstance(final PicoContainer container, final Type into, final T instance) {
    117            if (instantiationGuard == null) {
    118                instantiationGuard = new ThreadLocalCyclicDependencyGuard() {
    119                    public Object run() {
    120                        Method method = getInjectorMethod();
    121                        if (method.getDeclaringClass().isAssignableFrom(instance.getClass())) {
    122                            Object[] parameters = getMemberArguments(guardedContainer, method);
    123                            return invokeMethod(method, parameters, instance, container);
    124                        }
    125                        return null;
    126                    }
    127                };
    128            }
    129            instantiationGuard.setGuardedContainer(container);
    130            return instantiationGuard.observe(getComponentImplementation());
    131
    132        }
    133
    134        private Object invokeMethod(Method method, Object[] parameters, T instance, PicoContainer container) {
    135            try {
    136                currentMonitor().invoking(container, MethodInjector.this, (Member) method, instance);
    137                return method.invoke(instance, parameters);
    138            } catch (IllegalAccessException e) {
    139                return caughtIllegalAccessException(currentMonitor(), method, instance, e);
    140            } catch (InvocationTargetException e) {
    141                currentMonitor().invocationFailed(method, instance, e);
    142                if (e.getTargetException() instanceof RuntimeException) {
    143                    throw (RuntimeException) e.getTargetException();
    144                } else if (e.getTargetException() instanceof Error) {
    145                    throw (Error) e.getTargetException();
    146                }
    147                return null;
    148            }
    149        }
    150
    151
    152        @Override
    153        public void verify(final PicoContainer container) throws PicoCompositionException {
    154            if (verifyingGuard == null) {
    155                verifyingGuard = new ThreadLocalCyclicDependencyGuard() {
    156                    public Object run() {
    157                        final Method method = getInjectorMethod();
    158                        final Class[] parameterTypes = method.getParameterTypes();
    159                        final Parameter[] currentParameters = parameters != null ? parameters : createDefaultParameters(parameterTypes);
    160                        for (int i = 0; i < currentParameters.length; i++) {
    161                            currentParameters[i].verify(container, MethodInjector.this, parameterTypes[i],
    162                                new ParameterNameBinding(getParanamer(), getComponentImplementation(), method, i), useNames(),
    163                                                        getBindings(method.getParameterAnnotations())[i]);
    164                        }
    165                        return null;
    166                    }
    167                };
    168            }
    169            verifyingGuard.setGuardedContainer(container);
    170            verifyingGuard.observe(getComponentImplementation());
    171        }
    172
    173        public String getDescriptor() {
    174            return "MethodInjector-";
    175        }
    176
    177        protected boolean isNullParamAllowed(AccessibleObject member, int i) {
    178            Annotation[] annotations = ((Method) member).getParameterAnnotations()[i];
    179            for (Annotation annotation : annotations) {
    180                if (annotation instanceof Nullable) {
    181                    return true;
    182                }
    183            }
    184            return false;
    185        }
    186
    187
    188        public static class ByReflectionMethod extends MethodInjector {
    189            private final Method injectionMethod;
    190
    191            public ByReflectionMethod(Object componentKey, Class componentImplementation, Parameter[] parameters, ComponentMonitor monitor, LifecycleStrategy lifecycleStrategy, Method injectionMethod, boolean useNames) throws NotConcreteRegistrationException {
    192                super(componentKey, componentImplementation, parameters, monitor, lifecycleStrategy, null, useNames);
    193                this.injectionMethod = injectionMethod;
    194            }
    195
    196            @Override
    197            protected Method getInjectorMethod() {
    198                return injectionMethod;
    199            }
    200            public String getDescriptor() {
    201                return "ReflectionMethodInjector[" + injectionMethod + "]-";
    202            }
    203
    204        }
    205
    206    }