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