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 }