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 * Original code by * 009 *****************************************************************************/ 010 package org.picocontainer.injectors; 011 012 import java.lang.reflect.Constructor; 013 import java.lang.reflect.InvocationTargetException; 014 import java.lang.reflect.Member; 015 import java.lang.reflect.Modifier; 016 import java.lang.reflect.Type; 017 import java.util.Arrays; 018 import java.util.LinkedList; 019 import java.util.List; 020 import java.util.Set; 021 022 import org.picocontainer.ComponentAdapter; 023 import org.picocontainer.ComponentMonitor; 024 import org.picocontainer.Injector; 025 import org.picocontainer.LifecycleStrategy; 026 import org.picocontainer.ObjectReference; 027 import org.picocontainer.Parameter; 028 import org.picocontainer.PicoCompositionException; 029 import org.picocontainer.PicoContainer; 030 import org.picocontainer.PicoVisitor; 031 import org.picocontainer.adapters.AbstractAdapter; 032 import org.picocontainer.parameters.ComponentParameter; 033 034 /** 035 * This ComponentAdapter will instantiate a new object for each call to 036 * {@link org.picocontainer.ComponentAdapter#getComponentInstance(PicoContainer, Class)}. 037 * That means that when used with a PicoContainer, getComponent will 038 * return a new object each time. 039 * 040 * @author Aslak Hellesøy 041 * @author Paul Hammant 042 * @author Jörg Schaible 043 * @author Mauro Talevi 044 */ 045 @SuppressWarnings("serial") 046 public abstract class AbstractInjector<T> extends AbstractAdapter<T> implements LifecycleStrategy, Injector<T> { 047 /** The cycle guard for the verification. */ 048 protected transient ThreadLocalCyclicDependencyGuard verifyingGuard; 049 /** The parameters to use for initialization. */ 050 protected transient Parameter[] parameters; 051 052 /** The strategy used to control the lifecycle */ 053 protected LifecycleStrategy lifecycleStrategy; 054 private final boolean useNames; 055 056 /** 057 * Constructs a new ComponentAdapter for the given key and implementation. 058 * @param componentKey the search key for this implementation 059 * @param componentImplementation the concrete implementation 060 * @param parameters the parameters to use for the initialization 061 * @param monitor the component monitor used by this ComponentAdapter 062 * @param lifecycleStrategy the lifecycle strategy used by this ComponentAdapter 063 * @throws org.picocontainer.injectors.AbstractInjector.NotConcreteRegistrationException if the implementation is not a concrete class 064 * @throws NullPointerException if one of the parameters is <code>null</code> 065 */ 066 protected AbstractInjector(final Object componentKey, final Class<?> componentImplementation, final Parameter[] parameters, 067 final ComponentMonitor monitor, final LifecycleStrategy lifecycleStrategy, final boolean useNames) { 068 super(componentKey, componentImplementation, monitor); 069 this.useNames = useNames; 070 checkConcrete(); 071 if (parameters != null) { 072 for (int i = 0; i < parameters.length; i++) { 073 if(parameters[i] == null) { 074 throw new NullPointerException("Parameter " + i + " is null"); 075 } 076 } 077 } 078 this.parameters = parameters; 079 this.lifecycleStrategy = lifecycleStrategy; 080 } 081 082 public boolean useNames() { 083 return useNames; 084 } 085 086 private void checkConcrete() throws NotConcreteRegistrationException { 087 // Assert that the component class is concrete. 088 boolean isAbstract = (getComponentImplementation().getModifiers() & Modifier.ABSTRACT) == Modifier.ABSTRACT; 089 if (getComponentImplementation().isInterface() || isAbstract) { 090 throw new NotConcreteRegistrationException(getComponentImplementation()); 091 } 092 } 093 094 /** 095 * Create default parameters for the given types. 096 * 097 * @param parameters the parameter types 098 * @return the array with the default parameters. 099 */ 100 protected Parameter[] createDefaultParameters(final Class[] parameters) { 101 Parameter[] componentParameters = new Parameter[parameters.length]; 102 for (int i = 0; i < parameters.length; i++) { 103 componentParameters[i] = ComponentParameter.DEFAULT; 104 } 105 return componentParameters; 106 } 107 108 public void verify(PicoContainer container) throws PicoCompositionException { 109 } 110 111 public T getComponentInstance(PicoContainer container) throws PicoCompositionException { 112 return getComponentInstance(container, NOTHING.class); 113 } 114 115 public abstract T getComponentInstance(PicoContainer container, Type into) throws PicoCompositionException; 116 117 @Override 118 public void accept(final PicoVisitor visitor) { 119 super.accept(visitor); 120 if (parameters != null) { 121 for (Parameter parameter : parameters) { 122 parameter.accept(visitor); 123 } 124 } 125 } 126 public void start(final Object component) { 127 lifecycleStrategy.start(component); 128 } 129 130 public void stop(final Object component) { 131 lifecycleStrategy.stop(component); 132 } 133 134 public void dispose(final Object component) { 135 lifecycleStrategy.dispose(component); 136 } 137 138 public boolean hasLifecycle(final Class<?> type) { 139 return lifecycleStrategy.hasLifecycle(type); 140 } 141 142 public String getDescriptor() { 143 return "Asbtract Injector"; 144 } 145 146 /** 147 * Instantiate an object with given parameters and respect the accessible flag. 148 * 149 * @param constructor the constructor to use 150 * @param parameters the parameters for the constructor 151 * @return the new object. 152 * @throws InstantiationException 153 * @throws IllegalAccessException 154 * @throws InvocationTargetException 155 */ 156 protected T newInstance(final Constructor<T> constructor, final Object[] parameters) throws InstantiationException, IllegalAccessException, InvocationTargetException { 157 return constructor.newInstance(parameters); 158 } 159 /** 160 * inform monitor about component instantiation failure 161 * @param componentMonitor 162 * @param constructor 163 * @param e 164 * @param container 165 * @return 166 */ 167 protected T caughtInstantiationException(final ComponentMonitor componentMonitor, 168 final Constructor<T> constructor, 169 final InstantiationException e, final PicoContainer container) { 170 // can't get here because checkConcrete() will catch it earlier, but see PICO-191 171 componentMonitor.instantiationFailed(container, this, constructor, e); 172 throw new PicoCompositionException("Should never get here"); 173 } 174 175 /** 176 * inform monitor about access exception. 177 * @param componentMonitor 178 * @param constructor 179 * @param e 180 * @param container 181 * @return 182 */ 183 protected T caughtIllegalAccessException(final ComponentMonitor componentMonitor, 184 final Constructor<T> constructor, 185 final IllegalAccessException e, final PicoContainer container) { 186 // can't get here because either filtered or access mode set 187 componentMonitor.instantiationFailed(container, this, constructor, e); 188 throw new PicoCompositionException(e); 189 } 190 191 /** 192 * inform monitor about exception while instantiating component 193 * @param componentMonitor 194 * @param member 195 * @param componentInstance 196 * @param e 197 * @return 198 */ 199 protected T caughtInvocationTargetException(final ComponentMonitor componentMonitor, 200 final Member member, 201 final Object componentInstance, final InvocationTargetException e) { 202 componentMonitor.invocationFailed(member, componentInstance, e); 203 if (e.getTargetException() instanceof RuntimeException) { 204 throw (RuntimeException) e.getTargetException(); 205 } else if (e.getTargetException() instanceof Error) { 206 throw (Error) e.getTargetException(); 207 } 208 throw new PicoCompositionException(e.getTargetException()); 209 } 210 211 protected Object caughtIllegalAccessException(final ComponentMonitor componentMonitor, 212 final Member member, 213 final Object componentInstance, final IllegalAccessException e) { 214 componentMonitor.invocationFailed(member, componentInstance, e); 215 throw new PicoCompositionException(e); 216 } 217 218 protected Class<?> box(final Class<?> parameterType) { 219 if (parameterType.isPrimitive()) { 220 if (parameterType == Integer.TYPE) { 221 return Integer.class; 222 } else if (parameterType == Boolean.TYPE) { 223 return Boolean.class; 224 } 225 } 226 return parameterType; 227 } 228 229 /** 230 * Abstract utility class to detect recursion cycles. 231 * Derive from this class and implement {@link ThreadLocalCyclicDependencyGuard#run}. 232 * The method will be called by {@link ThreadLocalCyclicDependencyGuard#observe}. Select 233 * an appropriate guard for your scope. Any {@link ObjectReference} can be 234 * used as long as it is initialized with <code>Boolean.FALSE</code>. 235 * 236 * @author Jörg Schaible 237 */ 238 static abstract class ThreadLocalCyclicDependencyGuard<T> extends ThreadLocal<Boolean> { 239 240 protected PicoContainer guardedContainer; 241 242 @Override 243 protected Boolean initialValue() { 244 return Boolean.FALSE; 245 } 246 247 /** 248 * Derive from this class and implement this function with the functionality 249 * to observe for a dependency cycle. 250 * 251 * @return a value, if the functionality result in an expression, 252 * otherwise just return <code>null</code> 253 */ 254 public abstract T run(); 255 256 /** 257 * Call the observing function. The provided guard will hold the {@link Boolean} value. 258 * If the guard is already <code>Boolean.TRUE</code> a {@link CyclicDependencyException} 259 * will be thrown. 260 * 261 * @param stackFrame the current stack frame 262 * @return the result of the <code>run</code> method 263 */ 264 public final T observe(final Class<?> stackFrame) { 265 if (Boolean.TRUE.equals(get())) { 266 throw new CyclicDependencyException(stackFrame); 267 } 268 T result = null; 269 try { 270 set(Boolean.TRUE); 271 result = run(); 272 } catch (final CyclicDependencyException e) { 273 e.push(stackFrame); 274 throw e; 275 } finally { 276 set(Boolean.FALSE); 277 } 278 return result; 279 } 280 281 public void setGuardedContainer(final PicoContainer container) { 282 this.guardedContainer = container; 283 } 284 285 } 286 287 @SuppressWarnings("serial") 288 public static class CyclicDependencyException extends PicoCompositionException { 289 private final List<Class> stack; 290 291 /** 292 * @param element 293 */ 294 public CyclicDependencyException(final Class<?> element) { 295 super((Throwable)null); 296 this.stack = new LinkedList<Class>(); 297 push(element); 298 } 299 300 /** 301 * @param element 302 */ 303 public void push(final Class<?> element) { 304 stack.add(element); 305 } 306 307 public Class[] getDependencies() { 308 return stack.toArray(new Class[stack.size()]); 309 } 310 311 @Override 312 public String getMessage() { 313 return "Cyclic dependency: " + stack.toString(); 314 } 315 } 316 317 /** 318 * Exception that is thrown as part of the introspection. Raised if a PicoContainer cannot resolve a 319 * type dependency because the registered {@link org.picocontainer.ComponentAdapter}s are not 320 * distinct. 321 * 322 * @author Paul Hammant 323 * @author Aslak Hellesøy 324 * @author Jon Tirsén 325 */ 326 @SuppressWarnings("serial") 327 public static final class AmbiguousComponentResolutionException extends PicoCompositionException { 328 329 330 private Class<?> component; 331 private final Class<?> ambiguousDependency; 332 private final Object[] ambiguousComponentKeys; 333 334 335 /** 336 * Construct a new exception with the ambigous class type and the ambiguous component keys. 337 * 338 * @param ambiguousDependency the unresolved dependency type 339 * @param componentKeys the ambiguous keys. 340 */ 341 public AmbiguousComponentResolutionException(final Class<?> ambiguousDependency, final Object[] componentKeys) { 342 super(""); 343 this.ambiguousDependency = ambiguousDependency; 344 this.ambiguousComponentKeys = new Class[componentKeys.length]; 345 System.arraycopy(componentKeys, 0, ambiguousComponentKeys, 0, componentKeys.length); 346 } 347 348 /** 349 * @return Returns a string containing the unresolved class type and the ambiguous keys. 350 */ 351 @Override 352 public String getMessage() { 353 StringBuffer msg = new StringBuffer(); 354 msg.append(component != null ? component : "<no-component>"); 355 msg.append(" needs a '"); 356 msg.append(ambiguousDependency.getName()); 357 msg.append("' injected, but there are too many choices to inject. These:"); 358 msg.append(Arrays.asList(getAmbiguousComponentKeys())); 359 msg.append(", refer http://picocontainer.org/ambiguous-injectable-help.html"); 360 return msg.toString(); 361 } 362 363 /** 364 * @return Returns the ambiguous component keys as array. 365 */ 366 public Object[] getAmbiguousComponentKeys() { 367 return ambiguousComponentKeys; 368 } 369 370 public void setComponent(final Class<?> component) { 371 this.component = component; 372 } 373 } 374 375 /** 376 * Exception thrown when some of the component's dependencies are not satisfiable. 377 * 378 * @author Aslak Hellesøy 379 * @author Mauro Talevi 380 */ 381 public static class UnsatisfiableDependenciesException extends PicoCompositionException { 382 383 384 private final ComponentAdapter<?> instantiatingComponentAdapter; 385 private final Set unsatisfiableDependencies; 386 private final Class<?> unsatisfiedDependencyType; 387 388 /** 389 * The original container requesting the instantiation of the component. 390 */ 391 private final PicoContainer leafContainer; 392 393 public UnsatisfiableDependenciesException(final ComponentAdapter<?> instantiatingComponentAdapter, 394 final Class<?> unsatisfiedDependencyType, final Set unsatisfiableDependencies, 395 final PicoContainer leafContainer) { 396 super(instantiatingComponentAdapter.getComponentImplementation().getName() + " has unsatisfied dependency: " + unsatisfiedDependencyType 397 +" among unsatisfiable dependencies: "+unsatisfiableDependencies + " where " + leafContainer 398 + " was the leaf container being asked for dependencies."); 399 this.instantiatingComponentAdapter = instantiatingComponentAdapter; 400 this.unsatisfiableDependencies = unsatisfiableDependencies; 401 this.unsatisfiedDependencyType = unsatisfiedDependencyType; 402 this.leafContainer = leafContainer; 403 } 404 405 public ComponentAdapter<?> getUnsatisfiableComponentAdapter() { 406 return instantiatingComponentAdapter; 407 } 408 409 public Set getUnsatisfiableDependencies() { 410 return unsatisfiableDependencies; 411 } 412 413 public Class<?> getUnsatisfiedDependencyType() { 414 return unsatisfiedDependencyType; 415 } 416 417 public PicoContainer getLeafContainer() { 418 return leafContainer; 419 } 420 421 } 422 423 /** 424 * @author Aslak Hellesoy 425 */ 426 public static class NotConcreteRegistrationException extends PicoCompositionException { 427 428 private final Class<?> componentImplementation; 429 430 public NotConcreteRegistrationException(final Class<?> componentImplementation) { 431 super("Bad Access: '" + componentImplementation.getName() + "' is not instantiable"); 432 this.componentImplementation = componentImplementation; 433 } 434 435 public Class<?> getComponentImplementation() { 436 return componentImplementation; 437 } 438 } 439 }