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&ouml;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&oslash;y
342         * @author Jon Tirs&eacute;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&oslash;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    }