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