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.PicoCompositionException;
015 import org.picocontainer.PicoContainer;
016 import org.picocontainer.annotations.Nullable;
017
018 import java.lang.annotation.Annotation;
019 import java.lang.reflect.AccessibleObject;
020 import java.lang.reflect.InvocationTargetException;
021 import java.lang.reflect.Member;
022 import java.lang.reflect.Method;
023 import java.lang.reflect.Type;
024 import java.util.Set;
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 abstract 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 methodName the method name
051 * @param useNames use argument names when looking up dependencies
052 * @throws AbstractInjector.NotConcreteRegistrationException
053 * if the implementation is not a concrete class.
054 * @throws NullPointerException if one of the parameters is <code>null</code>
055 */
056 public MethodInjector(final Object componentKey, final Class componentImplementation, Parameter[] parameters, ComponentMonitor monitor,
057 String methodName, boolean useNames) throws AbstractInjector.NotConcreteRegistrationException {
058 super(componentKey, componentImplementation, parameters, monitor, useNames);
059 this.methodName = methodName;
060 }
061
062 protected abstract Method getInjectorMethod();
063
064 @Override
065 public T getComponentInstance(final PicoContainer container, @SuppressWarnings("unused") Type into) throws PicoCompositionException {
066 if (instantiationGuard == null) {
067 instantiationGuard = new ThreadLocalCyclicDependencyGuard() {
068 @Override
069 @SuppressWarnings("synthetic-access")
070 public Object run(Object instance) {
071 Method method = getInjectorMethod();
072 T inst = null;
073 ComponentMonitor componentMonitor = currentMonitor();
074 try { // TODO .. instantiating() ???
075 componentMonitor.instantiating(container, MethodInjector.this, null);
076 long startTime = System.currentTimeMillis();
077 Object[] methodParameters = null;
078 inst = getComponentImplementation().newInstance();
079 if (method != null) {
080 methodParameters = getMemberArguments(guardedContainer, method);
081 invokeMethod(method, methodParameters, inst, container);
082 }
083 componentMonitor.instantiated(container, MethodInjector.this,
084 null, inst, methodParameters, System.currentTimeMillis() - startTime);
085 return inst;
086 } catch (InstantiationException e) {
087 return caughtInstantiationException(componentMonitor, null, e, container);
088 } catch (IllegalAccessException e) {
089 return caughtIllegalAccessException(componentMonitor, method, inst, e);
090
091 }
092 }
093 };
094 }
095 instantiationGuard.setGuardedContainer(container);
096 return (T) instantiationGuard.observe(getComponentImplementation(), null);
097 }
098
099 protected Object[] getMemberArguments(PicoContainer container, final Method method) {
100 return super.getMemberArguments(container, method, method.getParameterTypes(), getBindings(method.getParameterAnnotations()));
101 }
102
103 @Override
104 public Object decorateComponentInstance(final PicoContainer container, @SuppressWarnings("unused") final Type into, final T instance) {
105 if (instantiationGuard == null) {
106 instantiationGuard = new ThreadLocalCyclicDependencyGuard() {
107 @Override
108 @SuppressWarnings("synthetic-access")
109 public Object run(Object inst) {
110 Method method = getInjectorMethod();
111 if (method != null && method.getDeclaringClass().isAssignableFrom(inst.getClass())) {
112 Object[] methodParameters = getMemberArguments(guardedContainer, method);
113 return invokeMethod(method, methodParameters, (T) inst, container);
114 }
115 return null;
116 }
117 };
118 }
119 instantiationGuard.setGuardedContainer(container);
120 Object o = instantiationGuard.observe(getComponentImplementation(), instance);
121 return o;
122 }
123
124 private Object invokeMethod(Method method, Object[] methodParameters, T instance, PicoContainer container) {
125 try {
126 Object rv = currentMonitor().invoking(container, MethodInjector.this, (Member) method, instance, methodParameters);
127 if (rv == ComponentMonitor.KEEP) {
128 long str = System.currentTimeMillis();
129 rv = method.invoke(instance, methodParameters);
130 currentMonitor().invoked(container, MethodInjector.this, method, instance, System.currentTimeMillis() - str, methodParameters, rv);
131 }
132 return rv;
133 } catch (IllegalAccessException e) {
134 return caughtIllegalAccessException(currentMonitor(), method, instance, e);
135 } catch (InvocationTargetException e) {
136 currentMonitor().invocationFailed(method, instance, e);
137 if (e.getTargetException() instanceof RuntimeException) {
138 throw (RuntimeException) e.getTargetException();
139 } else if (e.getTargetException() instanceof Error) {
140 throw (Error) e.getTargetException();
141 }
142 throw new PicoCompositionException(e);
143 }
144 }
145
146
147 @Override
148 public void verify(final PicoContainer container) throws PicoCompositionException {
149 if (verifyingGuard == null) {
150 verifyingGuard = new ThreadLocalCyclicDependencyGuard() {
151 @Override
152 public Object run(Object instance) {
153 final Method method = getInjectorMethod();
154 final Class[] parameterTypes = method.getParameterTypes();
155 final Parameter[] currentParameters = parameters != null ? parameters : createDefaultParameters(parameterTypes.length);
156 for (int i = 0; i < currentParameters.length; i++) {
157 currentParameters[i].verify(container, MethodInjector.this, parameterTypes[i],
158 new ParameterNameBinding(getParanamer(), method, i), useNames(),
159 getBindings(method.getParameterAnnotations())[i]);
160 }
161 return null;
162 }
163 };
164 }
165 verifyingGuard.setGuardedContainer(container);
166 verifyingGuard.observe(getComponentImplementation(), null);
167 }
168
169 @Override
170 protected boolean isNullParamAllowed(AccessibleObject member, int i) {
171 Annotation[] annotations = ((Method) member).getParameterAnnotations()[i];
172 for (Annotation annotation : annotations) {
173 if (annotation instanceof Nullable) {
174 return true;
175 }
176 }
177 return false;
178 }
179
180
181 public static class ByReflectionMethod extends MethodInjector {
182 private final Method injectionMethod;
183
184 public ByReflectionMethod(Object componentKey, Class componentImplementation, Parameter[] parameters, ComponentMonitor monitor, Method injectionMethod, boolean useNames) throws NotConcreteRegistrationException {
185 super(componentKey, componentImplementation, parameters, monitor, null, useNames);
186 this.injectionMethod = injectionMethod;
187 }
188
189 @Override
190 protected Method getInjectorMethod() {
191 return injectionMethod;
192 }
193
194 @Override
195 public String getDescriptor() {
196 return "MethodInjector.ByReflectionMethod[" + injectionMethod + "]-";
197 }
198
199 }
200
201 public static class ByMethodName extends MethodInjector {
202 private Set<String> injectionMethodNames;
203
204 public ByMethodName(Object componentKey, Class componentImplementation, Parameter[] parameters, ComponentMonitor monitor, Set<String> injectionMethodNames, boolean useNames) throws NotConcreteRegistrationException {
205 super(componentKey, componentImplementation, parameters, monitor, null, useNames);
206 ByMethodName.this.injectionMethodNames = injectionMethodNames;
207 }
208
209 @Override
210 protected Method getInjectorMethod() {
211 for (Method method : super.getComponentImplementation().getMethods()) {
212 if (injectionMethodNames.contains(method.getName())) {
213 return method;
214 }
215 }
216 return null;
217 }
218
219
220 @Override
221 public String getDescriptor() {
222 return "MethodInjector.ByMethodName" + injectionMethodNames + "-";
223 }
224
225 }
226
227 }