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 * Idea by Rachel Davies, Original code by Aslak Hellesoy and Paul Hammant * 009 *****************************************************************************/ 010 011 package org.picocontainer.injectors; 012 013 import org.picocontainer.*; 014 import org.picocontainer.containers.ImmutablePicoContainer; 015 import org.picocontainer.lifecycle.NullLifecycleStrategy; 016 import org.picocontainer.monitors.NullComponentMonitor; 017 018 import java.lang.reflect.*; 019 import java.lang.annotation.Annotation; 020 import java.security.AccessController; 021 import java.security.PrivilegedAction; 022 import java.util.ArrayList; 023 import java.util.Arrays; 024 import java.util.Collections; 025 import java.util.Comparator; 026 import java.util.HashSet; 027 import java.util.List; 028 import java.util.Set; 029 import java.util.Map; 030 import java.util.HashMap; 031 032 /** 033 * Injection will happen through a constructor for the component. 034 * 035 * @author Paul Hammant 036 * @author Aslak Hellesøy 037 * @author Jon Tirsén 038 * @author Zohar Melamed 039 * @author Jörg Schaible 040 * @author Mauro Talevi 041 */ 042 @SuppressWarnings("serial") 043 public class ConstructorInjector<T> extends SingleMemberInjector<T> { 044 045 private transient List<Constructor<T>> sortedMatchingConstructors; 046 private transient ThreadLocalCyclicDependencyGuard<T> instantiationGuard; 047 private boolean rememberChosenConstructor = true; 048 private transient CtorAndAdapters<T> chosenConstructor; 049 private boolean enableEmjection = false; 050 051 /** 052 * Constructor injector that uses no monitor and no lifecycle adapter. This is a more 053 * convenient constructor for use when instantiating a constructor injector directly. 054 * @param componentKey the search key for this implementation 055 * @param componentImplementation the concrete implementation 056 * @param parameters the parameters used for initialization 057 */ 058 public ConstructorInjector(final Object componentKey, final Class<?> componentImplementation, Parameter... parameters) { 059 this(componentKey, componentImplementation, parameters, new NullComponentMonitor(), false); 060 } 061 062 /** 063 * Creates a ConstructorInjector 064 * 065 * @param componentKey the search key for this implementation 066 * @param componentImplementation the concrete implementation 067 * @param parameters the parameters to use for the initialization 068 * @param monitor the component monitor used by this addAdapter 069 * @param useNames use argument names when looking up dependencies 070 * @throws org.picocontainer.injectors.AbstractInjector.NotConcreteRegistrationException 071 * if the implementation is not a concrete class. 072 * @throws NullPointerException if one of the parameters is <code>null</code> 073 */ 074 public ConstructorInjector(final Object componentKey, final Class componentImplementation, Parameter[] parameters, ComponentMonitor monitor, 075 boolean useNames) throws NotConcreteRegistrationException { 076 super(componentKey, componentImplementation, parameters, monitor, useNames); 077 } 078 079 /** 080 * Creates a ConstructorInjector 081 * 082 * @param componentKey the search key for this implementation 083 * @param componentImplementation the concrete implementation 084 * @param parameters the parameters to use for the initialization 085 * @param monitor the component monitor used by this addAdapter 086 * @param useNames use argument names when looking up dependencies 087 * @param rememberChosenCtor remember the chosen constructor (to speed up second/subsequent calls) 088 * @throws org.picocontainer.injectors.AbstractInjector.NotConcreteRegistrationException 089 * if the implementation is not a concrete class. 090 * @throws NullPointerException if one of the parameters is <code>null</code> 091 */ 092 public ConstructorInjector(final Object componentKey, final Class componentImplementation, Parameter[] parameters, ComponentMonitor monitor, 093 boolean useNames, boolean rememberChosenCtor) throws NotConcreteRegistrationException { 094 super(componentKey, componentImplementation, parameters, monitor, useNames); 095 this.rememberChosenConstructor = rememberChosenCtor; 096 } 097 098 private CtorAndAdapters<T> getGreediestSatisfiableConstructor(PicoContainer guardedContainer, @SuppressWarnings("unused") Class<T> componentImplementation) { 099 CtorAndAdapters<T> ctor = null; 100 try { 101 if (chosenConstructor == null) { 102 ctor = getGreediestSatisfiableConstructor(guardedContainer); 103 } 104 if (rememberChosenConstructor) { 105 if (chosenConstructor == null) { 106 chosenConstructor = ctor; 107 } else { 108 ctor = chosenConstructor; 109 } 110 } 111 } catch (AmbiguousComponentResolutionException e) { 112 e.setComponent(getComponentImplementation()); 113 throw e; 114 } 115 return ctor; 116 } 117 118 @SuppressWarnings("synthetic-access") 119 protected CtorAndAdapters<T> getGreediestSatisfiableConstructor(PicoContainer container) throws PicoCompositionException { 120 final Set<Constructor> conflicts = new HashSet<Constructor>(); 121 final Set<List<Type>> unsatisfiableDependencyTypes = new HashSet<List<Type>>(); 122 final Map<ResolverKey, Parameter.Resolver> resolvers = new HashMap<ResolverKey, Parameter.Resolver>(); 123 if (sortedMatchingConstructors == null) { 124 sortedMatchingConstructors = getSortedMatchingConstructors(); 125 } 126 Constructor<T> greediestConstructor = null; 127 Parameter[] greediestConstructorsParameters = null; 128 ComponentAdapter[] greediestConstructorsParametersComponentAdapters = null; 129 int lastSatisfiableConstructorSize = -1; 130 Type unsatisfiedDependencyType = null; 131 for (final Constructor<T> sortedMatchingConstructor : sortedMatchingConstructors) { 132 boolean failedDependency = false; 133 Type[] parameterTypes = sortedMatchingConstructor.getGenericParameterTypes(); 134 fixGenericParameterTypes(sortedMatchingConstructor, parameterTypes); 135 Annotation[] bindings = getBindings(sortedMatchingConstructor.getParameterAnnotations()); 136 final Parameter[] currentParameters = parameters != null ? parameters : createDefaultParameters(parameterTypes); 137 final ComponentAdapter<?>[] currentAdapters = new ComponentAdapter<?>[currentParameters.length]; 138 // remember: all constructors with less arguments than the given parameters are filtered out already 139 for (int j = 0; j < currentParameters.length; j++) { 140 // check whether this constructor is satisfiable 141 Type expectedType = box(parameterTypes[j]); 142 NameBinding expectedNameBinding = new ParameterNameBinding(getParanamer(), sortedMatchingConstructor, j); 143 ResolverKey resolverKey = new ResolverKey(expectedType, useNames() ? expectedNameBinding.getName() : null, useNames(), bindings[j], currentParameters[j]); 144 Parameter.Resolver resolver = resolvers.get(resolverKey); 145 if (resolver == null) { 146 resolver = currentParameters[j].resolve(container, this, null, expectedType, expectedNameBinding, useNames(), bindings[j]); 147 resolvers.put(resolverKey, resolver); 148 } 149 if (resolver.isResolved()) { 150 currentAdapters[j] = resolver.getComponentAdapter(); 151 continue; 152 } 153 unsatisfiableDependencyTypes.add(Arrays.asList(parameterTypes)); 154 unsatisfiedDependencyType = box(parameterTypes[j]); 155 failedDependency = true; 156 break; 157 } 158 159 if (greediestConstructor != null && parameterTypes.length != lastSatisfiableConstructorSize) { 160 if (conflicts.isEmpty()) { 161 // we found our match [aka. greedy and satisfied] 162 return new CtorAndAdapters<T>(greediestConstructor, greediestConstructorsParameters, greediestConstructorsParametersComponentAdapters); 163 } 164 // fits although not greedy 165 conflicts.add(sortedMatchingConstructor); 166 } else if (!failedDependency && lastSatisfiableConstructorSize == parameterTypes.length) { 167 // satisfied and same size as previous one? 168 conflicts.add(sortedMatchingConstructor); 169 conflicts.add(greediestConstructor); 170 } else if (!failedDependency) { 171 greediestConstructor = sortedMatchingConstructor; 172 greediestConstructorsParameters = currentParameters; 173 greediestConstructorsParametersComponentAdapters = currentAdapters; 174 lastSatisfiableConstructorSize = parameterTypes.length; 175 } 176 } 177 if (!conflicts.isEmpty()) { 178 throw new PicoCompositionException(conflicts.size() + " satisfiable constructors is too many for '"+getComponentImplementation()+"'. Constructor List:" + conflicts.toString().replace(getComponentImplementation().getName(),"<init>").replace("public <i","<i")); 179 } else if (greediestConstructor == null && !unsatisfiableDependencyTypes.isEmpty()) { 180 throw new UnsatisfiableDependenciesException(this, unsatisfiedDependencyType, unsatisfiableDependencyTypes, container); 181 } else if (greediestConstructor == null) { 182 // be nice to the user, show all constructors that were filtered out 183 final Set<Constructor> nonMatching = new HashSet<Constructor>(); 184 for (Constructor constructor : getConstructors()) { 185 nonMatching.add(constructor); 186 } 187 throw new PicoCompositionException("Either the specified parameters do not match any of the following constructors: " + nonMatching.toString() + "; OR the constructors were not accessible for '" + getComponentImplementation().getName() + "'"); 188 } 189 return new CtorAndAdapters<T>(greediestConstructor, greediestConstructorsParameters, greediestConstructorsParametersComponentAdapters); 190 } 191 192 public void enableEmjection(boolean enableEmjection) { 193 this.enableEmjection = enableEmjection; 194 } 195 196 private static final class ResolverKey { 197 private final Type expectedType; 198 private final String pName; 199 private final boolean useNames; 200 private final Annotation binding; 201 private final Parameter currentParameter; 202 203 private ResolverKey(Type expectedType, String pName, boolean useNames, Annotation binding, Parameter currentParameter) { 204 this.expectedType = expectedType; 205 this.pName = pName; 206 this.useNames = useNames; 207 this.binding = binding; 208 this.currentParameter = currentParameter; 209 } 210 211 // Generated by IDEA 212 @Override 213 public boolean equals(Object o) { 214 if (this == o) return true; 215 if (o == null || getClass() != o.getClass()) return false; 216 217 ResolverKey that = (ResolverKey) o; 218 219 if (useNames != that.useNames) return false; 220 if (binding != null ? !binding.equals(that.binding) : that.binding != null) return false; 221 if (!currentParameter.equals(that.currentParameter)) return false; 222 if (!expectedType.equals(that.expectedType)) return false; 223 if (pName != null ? !pName.equals(that.pName) : that.pName != null) return false; 224 225 return true; 226 } 227 228 @Override 229 public int hashCode() { 230 int result; 231 result = expectedType.hashCode(); 232 result = 31 * result + (pName != null ? pName.hashCode() : 0); 233 result = 31 * result + (useNames ? 1 : 0); 234 result = 31 * result + (binding != null ? binding.hashCode() : 0); 235 result = 31 * result + currentParameter.hashCode(); 236 return result; 237 } 238 } 239 240 private void fixGenericParameterTypes(Constructor<T> ctor, Type[] parameterTypes) { 241 for (int i = 0; i < parameterTypes.length; i++) { 242 Type parameterType = parameterTypes[i]; 243 if (parameterType instanceof TypeVariable) { 244 parameterTypes[i] = ctor.getParameterTypes()[i]; 245 } 246 } 247 } 248 249 protected class CtorAndAdapters<TYPE> { 250 private final Constructor<TYPE> ctor; 251 private final Parameter[] constructorParameters; 252 private final ComponentAdapter[] injecteeAdapters; 253 254 public CtorAndAdapters(Constructor<TYPE> ctor, Parameter[] parameters, ComponentAdapter[] injecteeAdapters) { 255 this.ctor = ctor; 256 this.constructorParameters = parameters; 257 this.injecteeAdapters = injecteeAdapters; 258 } 259 260 public Constructor<TYPE> getConstructor() { 261 return ctor; 262 } 263 264 public Object[] getParameterArguments(PicoContainer container) { 265 Type[] parameterTypes = ctor.getGenericParameterTypes(); 266 // as per fixParameterType() 267 for (int i = 0; i < parameterTypes.length; i++) { 268 Type parameterType = parameterTypes[i]; 269 if (parameterType instanceof TypeVariable) { 270 parameterTypes[i] = ctor.getParameterTypes()[i]; 271 } 272 } 273 boxParameters(parameterTypes); 274 Object[] result = new Object[constructorParameters.length]; 275 Annotation[] bindings = getBindings(ctor.getParameterAnnotations()); 276 for (int i = 0; i < constructorParameters.length; i++) { 277 278 result[i] = getParameter(container, ctor, i, parameterTypes[i], 279 bindings[i], constructorParameters[i], injecteeAdapters[i]); 280 } 281 return result; 282 } 283 284 public ComponentAdapter[] getInjecteeAdapters() { 285 return injecteeAdapters; 286 } 287 288 public Parameter[] getParameters() { 289 return constructorParameters; 290 } 291 } 292 293 @Override 294 public T getComponentInstance(final PicoContainer container, @SuppressWarnings("unused") Type into) throws PicoCompositionException { 295 if (instantiationGuard == null) { 296 instantiationGuard = new ThreadLocalCyclicDependencyGuard<T>() { 297 @Override 298 @SuppressWarnings("synthetic-access") 299 public T run() { 300 CtorAndAdapters<T> ctorAndAdapters = getGreediestSatisfiableConstructor(guardedContainer, getComponentImplementation()); 301 ComponentMonitor componentMonitor = currentMonitor(); 302 Constructor<T> ctor = ctorAndAdapters.getConstructor(); 303 try { 304 Object[] ctorParameters = ctorAndAdapters.getParameterArguments(guardedContainer); 305 ctor = componentMonitor.instantiating(container, ConstructorInjector.this, ctor); 306 if(ctorAndAdapters == null) { 307 throw new NullPointerException("Component Monitor " + componentMonitor 308 + " returned a null constructor from method 'instantiating' after passing in " + ctorAndAdapters); 309 } 310 long startTime = System.currentTimeMillis(); 311 T inst = newInstance(ctor, ctorParameters); 312 componentMonitor.instantiated(container, ConstructorInjector.this, 313 ctor, inst, ctorParameters, System.currentTimeMillis() - startTime); 314 return inst; 315 } catch (InvocationTargetException e) { 316 componentMonitor.instantiationFailed(container, ConstructorInjector.this, ctor, e); 317 if (e.getTargetException() instanceof RuntimeException) { 318 throw (RuntimeException) e.getTargetException(); 319 } else if (e.getTargetException() instanceof Error) { 320 throw (Error) e.getTargetException(); 321 } 322 throw new PicoCompositionException(e.getTargetException()); 323 } catch (InstantiationException e) { 324 return caughtInstantiationException(componentMonitor, ctor, e, container); 325 } catch (IllegalAccessException e) { 326 return caughtIllegalAccessException(componentMonitor, ctor, e, container); 327 328 } 329 } 330 }; 331 } 332 instantiationGuard.setGuardedContainer(container); 333 T inst = instantiationGuard.observe(getComponentImplementation()); 334 decorate(inst, container); 335 return inst; 336 } 337 338 private void decorate(T inst, PicoContainer container) { 339 if (enableEmjection) { 340 Emjection.setupEmjection(inst, container); 341 } 342 } 343 344 private List<Constructor<T>> getSortedMatchingConstructors() { 345 List<Constructor<T>> matchingConstructors = new ArrayList<Constructor<T>>(); 346 Constructor<T>[] allConstructors = getConstructors(); 347 // filter out all constructors that will definately not match 348 for (Constructor<T> constructor : allConstructors) { 349 if ((parameters == null || constructor.getParameterTypes().length == parameters.length) && (constructor.getModifiers() & Modifier.PUBLIC) != 0) { 350 matchingConstructors.add(constructor); 351 } 352 } 353 // optimize list of constructors moving the longest at the beginning 354 if (parameters == null) { 355 Collections.sort(matchingConstructors, new Comparator<Constructor>() { 356 public int compare(Constructor arg0, Constructor arg1) { 357 return arg1.getParameterTypes().length - arg0.getParameterTypes().length; 358 } 359 }); 360 } 361 return matchingConstructors; 362 } 363 364 private Constructor<T>[] getConstructors() { 365 return AccessController.doPrivileged(new PrivilegedAction<Constructor<T>[]>() { 366 public Constructor<T>[] run() { 367 return (Constructor<T>[]) getComponentImplementation().getDeclaredConstructors(); 368 } 369 }); 370 } 371 372 @Override 373 public void verify(final PicoContainer container) throws PicoCompositionException { 374 if (verifyingGuard == null) { 375 verifyingGuard = new ThreadLocalCyclicDependencyGuard() { 376 @Override 377 public Object run() { 378 final Constructor constructor = getGreediestSatisfiableConstructor(guardedContainer).getConstructor(); 379 final Class[] parameterTypes = constructor.getParameterTypes(); 380 final Parameter[] currentParameters = parameters != null ? parameters : createDefaultParameters(parameterTypes); 381 for (int i = 0; i < currentParameters.length; i++) { 382 currentParameters[i].verify(container, ConstructorInjector.this, box(parameterTypes[i]), 383 new ParameterNameBinding(getParanamer(), constructor, i), 384 useNames(), getBindings(constructor.getParameterAnnotations())[i]); 385 } 386 return null; 387 } 388 }; 389 } 390 verifyingGuard.setGuardedContainer(container); 391 verifyingGuard.observe(getComponentImplementation()); 392 } 393 394 @Override 395 public String getDescriptor() { 396 return "ConstructorInjector-"; 397 } 398 399 400 }