001 package org.picocontainer.injectors; 002 003 import org.picocontainer.ComponentMonitor; 004 import org.picocontainer.LifecycleStrategy; 005 import org.picocontainer.Parameter; 006 import org.picocontainer.NameBinding; 007 import org.picocontainer.PicoCompositionException; 008 import org.picocontainer.PicoContainer; 009 import org.picocontainer.annotations.Bind; 010 011 import java.lang.reflect.AccessibleObject; 012 import java.lang.reflect.Constructor; 013 import java.lang.reflect.InvocationTargetException; 014 import java.lang.reflect.Member; 015 import java.lang.reflect.Method; 016 import java.lang.reflect.Type; 017 import java.lang.reflect.TypeVariable; 018 import java.lang.annotation.Annotation; 019 import java.security.AccessController; 020 import java.security.PrivilegedAction; 021 import java.util.ArrayList; 022 import java.util.Collections; 023 import java.util.HashSet; 024 import java.util.List; 025 import java.util.Set; 026 027 import com.thoughtworks.paranamer.Paranamer; 028 import com.thoughtworks.paranamer.CachingParanamer; 029 import com.thoughtworks.paranamer.AdaptiveParanamer; 030 import com.thoughtworks.paranamer.AnnotationParanamer; 031 032 /** 033 * Injection will happen iteratively after component instantiation 034 */ 035 public abstract class IterativeInjector<T> extends AbstractInjector<T> { 036 private transient ThreadLocalCyclicDependencyGuard instantiationGuard; 037 protected transient List<AccessibleObject> injectionMembers; 038 protected transient Type[] injectionTypes; 039 protected transient Annotation[] bindings; 040 041 private transient Paranamer paranamer; 042 043 /** 044 * Constructs a IterativeInjector 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 useNames use argument names when looking up dependencies 051 * @throws org.picocontainer.injectors.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 IterativeInjector(final Object componentKey, final Class componentImplementation, Parameter[] parameters, ComponentMonitor monitor, 056 boolean useNames) throws NotConcreteRegistrationException { 057 super(componentKey, componentImplementation, parameters, monitor, useNames); 058 } 059 060 protected Constructor getConstructor() { 061 Object retVal = AccessController.doPrivileged(new PrivilegedAction<Object>() { 062 public Object run() { 063 try { 064 return getComponentImplementation().getConstructor((Class[])null); 065 } catch (NoSuchMethodException e) { 066 return new PicoCompositionException(e); 067 } catch (SecurityException e) { 068 return new PicoCompositionException(e); 069 } 070 } 071 }); 072 if (retVal instanceof Constructor) { 073 return (Constructor) retVal; 074 } else { 075 throw (PicoCompositionException) retVal; 076 } 077 } 078 079 private Parameter[] getMatchingParameterListForSetters(PicoContainer container) throws PicoCompositionException { 080 if (injectionMembers == null) { 081 initializeInjectionMembersAndTypeLists(); 082 } 083 084 final List<Object> matchingParameterList = new ArrayList<Object>(Collections.nCopies(injectionMembers.size(), null)); 085 086 final Parameter[] currentParameters = parameters != null ? parameters : createDefaultParameters(injectionTypes); 087 final Set<Integer> nonMatchingParameterPositions = matchParameters(container, matchingParameterList, currentParameters); 088 089 final Set<Type> unsatisfiableDependencyTypes = new HashSet<Type>(); 090 for (int i = 0; i < matchingParameterList.size(); i++) { 091 if (matchingParameterList.get(i) == null) { 092 unsatisfiableDependencyTypes.add(injectionTypes[i]); 093 } 094 } 095 if (unsatisfiableDependencyTypes.size() > 0) { 096 unsatisfiedDependencies(container, unsatisfiableDependencyTypes); 097 } else if (nonMatchingParameterPositions.size() > 0) { 098 throw new PicoCompositionException("Following parameters do not match any of the injectionMembers for " + getComponentImplementation() + ": " + nonMatchingParameterPositions.toString()); 099 } 100 return matchingParameterList.toArray(new Parameter[matchingParameterList.size()]); 101 } 102 103 private Set<Integer> matchParameters(PicoContainer container, List<Object> matchingParameterList, Parameter[] currentParameters) { 104 Set<Integer> unmatchedParameters = new HashSet<Integer>(); 105 for (int i = 0; i < currentParameters.length; i++) { 106 if (!matchParameter(container, matchingParameterList, currentParameters[i])) { 107 unmatchedParameters.add(i); 108 } 109 } 110 return unmatchedParameters; 111 } 112 113 private boolean matchParameter(PicoContainer container, List<Object> matchingParameterList, Parameter parameter) { 114 for (int j = 0; j < injectionTypes.length; j++) { 115 Object o = matchingParameterList.get(j); 116 if (o == null 117 && parameter.resolve(container, this, null, injectionTypes[j], 118 makeParameterNameImpl(injectionMembers.get(j)), 119 useNames(), bindings[j]).isResolved()) { 120 matchingParameterList.set(j, parameter); 121 return true; 122 } 123 } 124 return false; 125 } 126 127 protected NameBinding makeParameterNameImpl(AccessibleObject member) { 128 if (paranamer == null) { 129 paranamer = new CachingParanamer(new AnnotationParanamer(new AdaptiveParanamer())); 130 } 131 return new ParameterNameBinding(paranamer, member, 0); 132 } 133 134 protected void unsatisfiedDependencies(PicoContainer container, Set<Type> unsatisfiableDependencyTypes) { 135 throw new UnsatisfiableDependenciesException(this, null, unsatisfiableDependencyTypes, container); 136 } 137 138 public T getComponentInstance(final PicoContainer container, Type into) throws PicoCompositionException { 139 final Constructor constructor = getConstructor(); 140 if (instantiationGuard == null) { 141 instantiationGuard = new ThreadLocalCyclicDependencyGuard() { 142 public Object run() { 143 final Parameter[] matchingParameters = getMatchingParameterListForSetters(guardedContainer); 144 Object componentInstance = makeInstance(container, constructor, currentMonitor()); 145 return decorateComponentInstance(matchingParameters, currentMonitor(), componentInstance, container, guardedContainer); 146 } 147 }; 148 } 149 instantiationGuard.setGuardedContainer(container); 150 return (T) instantiationGuard.observe(getComponentImplementation()); 151 } 152 153 private Object decorateComponentInstance(Parameter[] matchingParameters, ComponentMonitor componentMonitor, Object componentInstance, PicoContainer container, PicoContainer guardedContainer) { 154 AccessibleObject member = null; 155 Object injected[] = new Object[injectionMembers.size()]; 156 Object lastReturn = null; 157 try { 158 for (int i = 0; i < injectionMembers.size(); i++) { 159 member = injectionMembers.get(i); 160 if (matchingParameters[i] != null) { 161 Object toInject = matchingParameters[i].resolve(guardedContainer, this, null, injectionTypes[i], 162 makeParameterNameImpl(injectionMembers.get(i)), 163 useNames(), bindings[i]).resolveInstance(); 164 Object rv = componentMonitor.invoking(container, this, (Member) member, componentInstance, new Object[] {toInject}); 165 if (rv == ComponentMonitor.KEEP) { 166 long str = System.currentTimeMillis(); 167 lastReturn = injectIntoMember(member, componentInstance, toInject); 168 componentMonitor.invoked(container, this, (Member) member, componentInstance, System.currentTimeMillis() - str, new Object[] {toInject}, lastReturn); 169 } else { 170 lastReturn = rv; 171 } 172 injected[i] = toInject; 173 } 174 } 175 return memberInvocationReturn(lastReturn, member, componentInstance); 176 } catch (InvocationTargetException e) { 177 return caughtInvocationTargetException(componentMonitor, (Member) member, componentInstance, e); 178 } catch (IllegalAccessException e) { 179 return caughtIllegalAccessException(componentMonitor, (Member) member, componentInstance, e); 180 } 181 } 182 183 protected abstract Object memberInvocationReturn(Object lastReturn, AccessibleObject member, Object instance); 184 185 private Object makeInstance(PicoContainer container, Constructor constructor, ComponentMonitor componentMonitor) { 186 long startTime = System.currentTimeMillis(); 187 Constructor constructorToUse = componentMonitor.instantiating(container, 188 IterativeInjector.this, constructor); 189 Object componentInstance; 190 try { 191 componentInstance = newInstance(constructorToUse, null); 192 } catch (InvocationTargetException e) { 193 componentMonitor.instantiationFailed(container, IterativeInjector.this, constructorToUse, e); 194 if (e.getTargetException() instanceof RuntimeException) { 195 throw (RuntimeException)e.getTargetException(); 196 } else if (e.getTargetException() instanceof Error) { 197 throw (Error)e.getTargetException(); 198 } 199 throw new PicoCompositionException(e.getTargetException()); 200 } catch (InstantiationException e) { 201 return caughtInstantiationException(componentMonitor, constructor, e, container); 202 } catch (IllegalAccessException e) { 203 return caughtIllegalAccessException(componentMonitor, constructor, e, container); 204 } 205 componentMonitor.instantiated(container, 206 IterativeInjector.this, 207 constructorToUse, 208 componentInstance, 209 null, 210 System.currentTimeMillis() - startTime); 211 return componentInstance; 212 } 213 214 @Override 215 public Object decorateComponentInstance(final PicoContainer container, Type into, final T instance) { 216 if (instantiationGuard == null) { 217 instantiationGuard = new ThreadLocalCyclicDependencyGuard() { 218 public Object run() { 219 final Parameter[] matchingParameters = getMatchingParameterListForSetters(guardedContainer); 220 return decorateComponentInstance(matchingParameters, currentMonitor(), instance, container, guardedContainer); 221 } 222 }; 223 } 224 instantiationGuard.setGuardedContainer(container); 225 return instantiationGuard.observe(getComponentImplementation()); 226 } 227 228 protected abstract Object injectIntoMember(AccessibleObject member, Object componentInstance, Object toInject) throws IllegalAccessException, InvocationTargetException; 229 230 @Override 231 public void verify(final PicoContainer container) throws PicoCompositionException { 232 if (verifyingGuard == null) { 233 verifyingGuard = new ThreadLocalCyclicDependencyGuard() { 234 public Object run() { 235 final Parameter[] currentParameters = getMatchingParameterListForSetters(guardedContainer); 236 for (int i = 0; i < currentParameters.length; i++) { 237 currentParameters[i].verify(container, IterativeInjector.this, injectionTypes[i], 238 makeParameterNameImpl(injectionMembers.get(i)), useNames(), bindings[i]); 239 } 240 return null; 241 } 242 }; 243 } 244 verifyingGuard.setGuardedContainer(container); 245 verifyingGuard.observe(getComponentImplementation()); 246 } 247 248 protected void initializeInjectionMembersAndTypeLists() { 249 injectionMembers = new ArrayList<AccessibleObject>(); 250 List<Annotation> bingingIds = new ArrayList<Annotation>(); 251 final List<String> nameList = new ArrayList<String>(); 252 final List<Type> typeList = new ArrayList<Type>(); 253 final Method[] methods = getMethods(); 254 for (final Method method : methods) { 255 final Type[] parameterTypes = method.getGenericParameterTypes(); 256 fixGenericParameterTypes(method, parameterTypes); 257 258 // We're only interested if there is only one parameter and the method name is bean-style. 259 if (parameterTypes.length == 1) { 260 boolean isInjector = isInjectorMethod(method); 261 if (isInjector) { 262 injectionMembers.add(method); 263 nameList.add(getName(method)); 264 typeList.add(box(parameterTypes[0])); 265 bingingIds.add(getBindings(method, 0)); 266 } 267 } 268 } 269 injectionTypes = typeList.toArray(new Type[0]); 270 bindings = bingingIds.toArray(new Annotation[0]); 271 } 272 273 protected String getName(Method method) { 274 return null; 275 } 276 277 private void fixGenericParameterTypes(Method method, Type[] parameterTypes) { 278 for (int i = 0; i < parameterTypes.length; i++) { 279 Type parameterType = parameterTypes[i]; 280 if (parameterType instanceof TypeVariable) { 281 parameterTypes[i] = method.getParameterTypes()[i]; 282 } 283 } 284 } 285 286 287 private Annotation getBindings(Method method, int i) { 288 Annotation[][] parameterAnnotations = method.getParameterAnnotations(); 289 if (parameterAnnotations.length >= i +1 ) { 290 Annotation[] o = parameterAnnotations[i]; 291 for (Annotation annotation : o) { 292 if (annotation.annotationType().getAnnotation(Bind.class) != null) { 293 return annotation; 294 } 295 } 296 return null; 297 298 } 299 //TODO - what's this ? 300 if (parameterAnnotations != null) { 301 //return ((Bind) method.getAnnotation(Bind.class)).id(); 302 } 303 return null; 304 305 } 306 307 protected boolean isInjectorMethod(Method method) { 308 return false; 309 } 310 311 private Method[] getMethods() { 312 return (Method[]) AccessController.doPrivileged(new PrivilegedAction() { 313 public Object run() { 314 return getComponentImplementation().getMethods(); 315 } 316 }); 317 } 318 319 320 }