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
016 import org.picocontainer.ComponentMonitor;
017 import org.picocontainer.LifecycleStrategy;
018 import org.picocontainer.Parameter;
019 import org.picocontainer.PicoCompositionException;
020 import org.picocontainer.PicoContainer;
021
022 /**
023 * Injection will happen through a single method for the component.
024 *
025 * Most likely it is a method called 'inject', though that can be overridden.
026 *
027 * @author Paul Hammant
028 * @author Aslak Hellesøy
029 * @author Jon Tirsén
030 * @author Zohar Melamed
031 * @author Jörg Schaible
032 * @author Mauro Talevi
033 */
034 @SuppressWarnings("serial")
035 public class MethodInjector<T> extends SingleMemberInjector<T> {
036 private transient ThreadLocalCyclicDependencyGuard instantiationGuard;
037 private final String methodName;
038
039 /**
040 * Creates a MethodInjector
041 *
042 * @param componentKey the search key for this implementation
043 * @param componentImplementation the concrete implementation
044 * @param parameters the parameters to use for the initialization
045 * @param monitor the component monitor used by this addAdapter
046 * @param lifecycleStrategy the component lifecycle strategy used by this addAdapter
047 * @param methodName the method name
048 * @param useNames use argument names when looking up dependencies
049 * @throws AbstractInjector.NotConcreteRegistrationException
050 * if the implementation is not a concrete class.
051 * @throws NullPointerException if one of the parameters is <code>null</code>
052 */
053 public MethodInjector(final Object componentKey, final Class componentImplementation, Parameter[] parameters, ComponentMonitor monitor,
054 LifecycleStrategy lifecycleStrategy, String methodName, boolean useNames) throws AbstractInjector.NotConcreteRegistrationException {
055 super(componentKey, componentImplementation, parameters, monitor, lifecycleStrategy, useNames);
056 this.methodName = methodName;
057 }
058
059 protected Method getInjectorMethod() {
060 Method[] methods = new Method[0];
061 try {
062 methods = super.getComponentImplementation().getMethods();
063 } catch (AmbiguousComponentResolutionException e) {
064 e.setComponent(getComponentImplementation());
065 throw e;
066 }
067 for (Method method : methods) {
068 if (method.getName().equals(methodName)) {
069 return method;
070 }
071 }
072 return null;
073 }
074
075 public T getComponentInstance(final PicoContainer container, Type into) throws PicoCompositionException {
076 if (instantiationGuard == null) {
077 instantiationGuard = new ThreadLocalCyclicDependencyGuard() {
078 public Object run() {
079 Method method = getInjectorMethod();
080 T inst = null;
081 ComponentMonitor componentMonitor = currentMonitor();
082 try {
083 componentMonitor.instantiating(container, MethodInjector.this, null);
084 long startTime = System.currentTimeMillis();
085 Object[] parameters = null;
086 inst = getComponentImplementation().newInstance();
087 if (method != null) {
088 parameters = getMemberArguments(guardedContainer, method);
089 invokeMethod(method, parameters, inst, container);
090 }
091 componentMonitor.instantiated(container, MethodInjector.this,
092 null, inst, parameters, System.currentTimeMillis() - startTime);
093 return inst;
094 } catch (InstantiationException e) {
095 return caughtInstantiationException(componentMonitor, null, e, container);
096 } catch (IllegalAccessException e) {
097 return caughtIllegalAccessException(componentMonitor, method, inst, e);
098
099 }
100 }
101 };
102 }
103 instantiationGuard.setGuardedContainer(container);
104 return (T) instantiationGuard.observe(getComponentImplementation());
105 }
106
107 protected Object[] getMemberArguments(PicoContainer container, final Method method) {
108 return super.getMemberArguments(container, method, method.getParameterTypes(), getBindings(method.getParameterAnnotations()));
109 }
110
111 public void decorateComponentInstance(final PicoContainer container, Type into, final T instance) {
112 if (instantiationGuard == null) {
113 instantiationGuard = new ThreadLocalCyclicDependencyGuard() {
114 public Object run() {
115 Method method = getInjectorMethod();
116 Object inst = null;
117 Object[] parameters = null;
118 parameters = getMemberArguments(guardedContainer, method);
119 return invokeMethod(method, parameters, instance, container);
120 }
121 };
122 }
123 instantiationGuard.setGuardedContainer(container);
124 instantiationGuard.observe(getComponentImplementation());
125
126 }
127
128 private Object invokeMethod(Method method, Object[] parameters, T instance, PicoContainer container) {
129 try {
130 return method.invoke(instance, parameters);
131 } catch (IllegalAccessException e) {
132 return caughtIllegalAccessException(currentMonitor(), method, instance, e);
133 } catch (InvocationTargetException e) {
134 currentMonitor().instantiationFailed(container, MethodInjector.this, null, e);
135 if (e.getTargetException() instanceof RuntimeException) {
136 throw (RuntimeException) e.getTargetException();
137 } else if (e.getTargetException() instanceof Error) {
138 throw (Error) e.getTargetException();
139 }
140 }
141 return null;
142 }
143
144
145 @Override
146 public void verify(final PicoContainer container) throws PicoCompositionException {
147 if (verifyingGuard == null) {
148 verifyingGuard = new ThreadLocalCyclicDependencyGuard() {
149 public Object run() {
150 final Method method = getInjectorMethod();
151 final Class[] parameterTypes = method.getParameterTypes();
152 final Parameter[] currentParameters = parameters != null ? parameters : createDefaultParameters(parameterTypes);
153 for (int i = 0; i < currentParameters.length; i++) {
154 currentParameters[i].verify(container, MethodInjector.this, parameterTypes[i],
155 new ParameterNameBinding(getParanamer(), getComponentImplementation(), method, i), useNames(),
156 getBindings(method.getParameterAnnotations())[i]);
157 }
158 return null;
159 }
160 };
161 }
162 verifyingGuard.setGuardedContainer(container);
163 verifyingGuard.observe(getComponentImplementation());
164 }
165
166 public String getDescriptor() {
167 return "MethodInjector-";
168 }
169
170 public static class ByReflectionMethod extends MethodInjector {
171 private final Method injectionMethod;
172
173 public ByReflectionMethod(Object componentKey, Class componentImplementation, Parameter[] parameters, ComponentMonitor monitor, LifecycleStrategy lifecycleStrategy, Method injectionMethod, boolean useNames) throws NotConcreteRegistrationException {
174 super(componentKey, componentImplementation, parameters, monitor, lifecycleStrategy, null, useNames);
175 this.injectionMethod = injectionMethod;
176 }
177
178 @Override
179 protected Method getInjectorMethod() {
180 if (injectionMethod.getDeclaringClass().isAssignableFrom(super.getComponentImplementation())) {
181 return injectionMethod;
182 } else {
183 return null;
184 }
185 }
186 }
187
188 }