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øy
033 * @author Jon Tirsén
034 * @author Zohar Melamed
035 * @author Jö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 }