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