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 org.picocontainer.ComponentMonitor;
013    import org.picocontainer.Parameter;
014    import org.picocontainer.PicoContainer;
015    import org.picocontainer.PicoCompositionException;
016    import org.picocontainer.ParameterName;
017    import org.picocontainer.LifecycleStrategy;
018    import org.picocontainer.behaviors.Cached;
019    
020    import java.lang.reflect.InvocationTargetException;
021    import java.lang.reflect.Method;
022    import java.lang.reflect.Member;
023    
024    /**
025     * Instantiates components using Method Injection.
026     * <em>
027     * Note that this class doesn't cache instances. If you want caching,
028     * use a {@link Cached} around this one.
029     * </em>
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    public class MethodInjector extends SingleMemberInjector {
039        private transient ThreadLocalCyclicDependencyGuard instantiationGuard;
040        private final String methodName;
041    
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         * @throws AbstractInjector.NotConcreteRegistrationException
052         *                              if the implementation is not a concrete class.
053         * @throws NullPointerException if one of the parameters is <code>null</code>
054         */
055        public MethodInjector(final Object componentKey, final Class componentImplementation, Parameter[] parameters, ComponentMonitor monitor,
056                              LifecycleStrategy lifecycleStrategy, String methodName) throws AbstractInjector.NotConcreteRegistrationException {
057            super(componentKey, componentImplementation, parameters, monitor, lifecycleStrategy);
058            this.methodName = methodName;
059        }
060    
061        protected Method getInjectorMethod() {
062            Method[] methods = super.getComponentImplementation().getMethods();
063            for (Method method : methods) {
064                if (method.getName().equals(methodName)) {
065                    return method;
066                }
067            }
068            return null;
069        }
070    
071        public Object getComponentInstance(final PicoContainer container) throws PicoCompositionException {
072            if (instantiationGuard == null) {
073                instantiationGuard = new ThreadLocalCyclicDependencyGuard() {
074                    public Object run() {
075                        Method method;
076                        Object inst = null;
077                        try {
078                            method = getInjectorMethod();
079                        } catch (AmbiguousComponentResolutionException e) {
080                            e.setComponent(getComponentImplementation());
081                            throw e;
082                        }
083                        ComponentMonitor componentMonitor = currentMonitor();
084                        try {
085                            componentMonitor.instantiating(container, MethodInjector.this, null);
086                            long startTime = System.currentTimeMillis();
087                            Object[] parameters = null;
088                            inst = getComponentImplementation().newInstance();
089                            if (method != null) {
090                                parameters = getMemberArguments(guardedContainer, method);
091                                method.invoke(inst, parameters);
092                            }
093                            componentMonitor.instantiated(container,
094                                                          MethodInjector.this,
095                                                          null, inst, parameters, System.currentTimeMillis() - startTime);
096                            return inst;
097                        } catch (InvocationTargetException e) {
098                            componentMonitor.instantiationFailed(container, MethodInjector.this, null, e);
099                            if (e.getTargetException() instanceof RuntimeException) {
100                                throw (RuntimeException) e.getTargetException();
101                            } else if (e.getTargetException() instanceof Error) {
102                                throw (Error) e.getTargetException();
103                            }
104                            throw new PicoCompositionException(e.getTargetException());
105                        } catch (InstantiationException e) {
106                            return caughtInstantiationException(componentMonitor, null, e, container);
107                        } catch (IllegalAccessException e) {
108                            return caughtIllegalAccessException(componentMonitor, method, inst, e);
109    
110                        }
111                    }
112                };
113            }
114            instantiationGuard.setGuardedContainer(container);
115            return instantiationGuard.observe(getComponentImplementation());
116        }
117    
118        protected Object[] getMemberArguments(PicoContainer container, final Method method) {
119            return super.getMemberArguments(container, method, method.getParameterTypes());
120        }
121    
122    
123        public void verify(final PicoContainer container) throws PicoCompositionException {
124            if (verifyingGuard == null) {
125                verifyingGuard = new ThreadLocalCyclicDependencyGuard() {
126                    public Object run() {
127                        final Method method = getInjectorMethod();
128                        final Class[] parameterTypes = method.getParameterTypes();
129                        final Parameter[] currentParameters = parameters != null ? parameters : createDefaultParameters(parameterTypes);
130                        for (int i = 0; i < currentParameters.length; i++) {
131                            currentParameters[i].verify(container, MethodInjector.this, parameterTypes[i],
132                                                        new MemberInjectorParameterName(method, i));
133                        }
134                        return null;
135                    }
136                };
137            }
138            verifyingGuard.setGuardedContainer(container);
139            verifyingGuard.observe(getComponentImplementation());
140        }
141    
142        public String toString() {
143            return "MethodInjector-" + super.toString();
144        }
145    
146    }