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