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