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