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 010package org.picocontainer.injectors; 011 012import org.picocontainer.ComponentMonitor; 013import org.picocontainer.Parameter; 014import org.picocontainer.PicoCompositionException; 015import org.picocontainer.PicoContainer; 016import org.picocontainer.annotations.Nullable; 017 018import java.lang.annotation.Annotation; 019import java.lang.reflect.AccessibleObject; 020import java.lang.reflect.InvocationTargetException; 021import java.lang.reflect.Member; 022import java.lang.reflect.Method; 023import java.lang.reflect.Type; 024import 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") 039public 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}