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    
016    import org.picocontainer.ComponentMonitor;
017    import org.picocontainer.LifecycleStrategy;
018    import org.picocontainer.Parameter;
019    import org.picocontainer.PicoCompositionException;
020    import org.picocontainer.PicoContainer;
021    
022    /**
023     * Injection will happen through a single method for the component.
024     *
025     * Most likely it is a method called 'inject', though that can be overridden.
026     *
027     * @author Paul Hammant
028     * @author Aslak Hellesøy
029     * @author Jon Tirsén
030     * @author Zohar Melamed
031     * @author Jörg Schaible
032     * @author Mauro Talevi
033     */
034    @SuppressWarnings("serial")
035    public class MethodInjector<T> extends SingleMemberInjector<T> {
036        private transient ThreadLocalCyclicDependencyGuard instantiationGuard;
037        private final String methodName;
038    
039        /**
040         * Creates a MethodInjector
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 methodName              the method name
048         * @param useNames                use argument names when looking up dependencies
049         * @throws AbstractInjector.NotConcreteRegistrationException
050         *                              if the implementation is not a concrete class.
051         * @throws NullPointerException if one of the parameters is <code>null</code>
052         */
053        public MethodInjector(final Object componentKey, final Class componentImplementation, Parameter[] parameters, ComponentMonitor monitor,
054                              LifecycleStrategy lifecycleStrategy, String methodName, boolean useNames) throws AbstractInjector.NotConcreteRegistrationException {
055            super(componentKey, componentImplementation, parameters, monitor, lifecycleStrategy, useNames);
056            this.methodName = methodName;
057        }
058    
059        protected Method getInjectorMethod() {
060            Method[] methods = new Method[0];
061            try {
062                methods = super.getComponentImplementation().getMethods();
063            } catch (AmbiguousComponentResolutionException e) {
064                e.setComponent(getComponentImplementation());
065                throw e;
066            }
067            for (Method method : methods) {
068                if (method.getName().equals(methodName)) {
069                    return method;
070                }
071            }
072            return null;
073        }
074    
075        public T getComponentInstance(final PicoContainer container, Type into) throws PicoCompositionException {
076            if (instantiationGuard == null) {
077                instantiationGuard = new ThreadLocalCyclicDependencyGuard() {
078                    public Object run() {
079                        Method method = getInjectorMethod();
080                        T inst = null;
081                        ComponentMonitor componentMonitor = currentMonitor();
082                        try {
083                            componentMonitor.instantiating(container, MethodInjector.this, null);
084                            long startTime = System.currentTimeMillis();
085                            Object[] parameters = null;
086                            inst = getComponentImplementation().newInstance();
087                            if (method != null) {
088                                parameters = getMemberArguments(guardedContainer, method);
089                                invokeMethod(method, parameters, inst, container);
090                            }
091                            componentMonitor.instantiated(container, MethodInjector.this,
092                                                          null, inst, parameters, System.currentTimeMillis() - startTime);
093                            return inst;
094                        } catch (InstantiationException e) {
095                            return caughtInstantiationException(componentMonitor, null, e, container);
096                        } catch (IllegalAccessException e) {
097                            return caughtIllegalAccessException(componentMonitor, method, inst, e);
098    
099                        }
100                    }
101                };
102            }
103            instantiationGuard.setGuardedContainer(container);
104            return (T) instantiationGuard.observe(getComponentImplementation());
105        }
106    
107        protected Object[] getMemberArguments(PicoContainer container, final Method method) {
108            return super.getMemberArguments(container, method, method.getParameterTypes(), getBindings(method.getParameterAnnotations()));
109        }
110    
111        public void decorateComponentInstance(final PicoContainer container, Type into, final T instance) {
112            if (instantiationGuard == null) {
113                instantiationGuard = new ThreadLocalCyclicDependencyGuard() {
114                    public Object run() {
115                        Method method = getInjectorMethod();
116                        Object inst = null;
117                        Object[] parameters = null;
118                        parameters = getMemberArguments(guardedContainer, method);
119                        return invokeMethod(method, parameters, instance, container);
120                    }
121                };
122            }
123            instantiationGuard.setGuardedContainer(container);
124            instantiationGuard.observe(getComponentImplementation());
125    
126        }
127    
128        private Object invokeMethod(Method method, Object[] parameters, T instance, PicoContainer container) {
129            try {
130                return method.invoke(instance, parameters);
131            } catch (IllegalAccessException e) {
132                return caughtIllegalAccessException(currentMonitor(), method, instance, e);
133            } catch (InvocationTargetException e) {
134                currentMonitor().instantiationFailed(container, MethodInjector.this, null, e);
135                if (e.getTargetException() instanceof RuntimeException) {
136                    throw (RuntimeException) e.getTargetException();
137                } else if (e.getTargetException() instanceof Error) {
138                    throw (Error) e.getTargetException();
139                }
140            }
141            return null;
142        }
143    
144    
145        @Override
146        public void verify(final PicoContainer container) throws PicoCompositionException {
147            if (verifyingGuard == null) {
148                verifyingGuard = new ThreadLocalCyclicDependencyGuard() {
149                    public Object run() {
150                        final Method method = getInjectorMethod();
151                        final Class[] parameterTypes = method.getParameterTypes();
152                        final Parameter[] currentParameters = parameters != null ? parameters : createDefaultParameters(parameterTypes);
153                        for (int i = 0; i < currentParameters.length; i++) {
154                            currentParameters[i].verify(container, MethodInjector.this, parameterTypes[i],
155                                new ParameterNameBinding(getParanamer(), getComponentImplementation(), method, i), useNames(),
156                                                        getBindings(method.getParameterAnnotations())[i]);
157                        }
158                        return null;
159                    }
160                };
161            }
162            verifyingGuard.setGuardedContainer(container);
163            verifyingGuard.observe(getComponentImplementation());
164        }
165    
166        public String getDescriptor() {
167            return "MethodInjector-";
168        }
169    
170    }