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