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