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