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