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