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.defaults;
011    
012    import org.picocontainer.ComponentMonitor;
013    import org.picocontainer.Parameter;
014    import org.picocontainer.PicoContainer;
015    import org.picocontainer.PicoIntrospectionException;
016    import org.picocontainer.PicoVisitor;
017    
018    import java.lang.reflect.Constructor;
019    import java.lang.reflect.InvocationTargetException;
020    import java.lang.reflect.Modifier;
021    
022    /**
023     * This ComponentAdapter will instantiate a new object for each call to
024     * {@link org.picocontainer.ComponentAdapter#getComponentInstance(PicoContainer)}.
025     * That means that when used with a PicoContainer, getComponentInstance will 
026     * return a new object each time.
027     *
028     * @author Aslak Hellesøy
029     * @author Paul Hammant
030     * @author Jörg Schaible
031     * @author Mauro Talevi
032     * @version $Revision: 2788 $
033     * @since 1.0
034     */
035    public abstract class InstantiatingComponentAdapter extends AbstractComponentAdapter 
036                                    implements LifecycleStrategy {
037        /** The cycle guard for the verification. */ 
038        protected transient Guard verifyingGuard;
039        /** The parameters to use for initialization. */ 
040        protected transient Parameter[] parameters;
041        /** Flag indicating instanciation of non-public classes. */ 
042        protected boolean allowNonPublicClasses;
043        
044        /** The cycle guard for the verification. */
045        protected static abstract class Guard extends ThreadLocalCyclicDependencyGuard {
046            protected PicoContainer guardedContainer;
047            protected void setArguments(PicoContainer container) {
048                this.guardedContainer = container;
049            }
050        }
051        
052        /** The strategy used to control the lifecycle */
053        protected LifecycleStrategy lifecycleStrategy;
054        
055        /**
056         * Constructs a new ComponentAdapter for the given key and implementation. 
057         * @param componentKey the search key for this implementation
058         * @param componentImplementation the concrete implementation
059         * @param parameters the parameters to use for the initialization
060         * @param allowNonPublicClasses flag to allow instantiation of non-public classes
061         * @param monitor the component monitor used by this ComponentAdapter
062         * @param lifecycleStrategy the lifecycle strategy used by this ComponentAdapter
063         * @throws AssignabilityRegistrationException if the key is a type and the implementation cannot be assigned to
064         * @throws NotConcreteRegistrationException if the implementation is not a concrete class
065         * @throws NullPointerException if one of the parameters is <code>null</code>
066         */
067        protected InstantiatingComponentAdapter(Object componentKey, Class componentImplementation, Parameter[] parameters, boolean allowNonPublicClasses,
068                ComponentMonitor monitor, LifecycleStrategy lifecycleStrategy) {
069            super(componentKey, componentImplementation, monitor);
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.allowNonPublicClasses = allowNonPublicClasses;
080            this.lifecycleStrategy = lifecycleStrategy;
081        }
082    
083        /**
084         * Constructs a new ComponentAdapter for the given key and implementation. 
085         * @param componentKey the search key for this implementation
086         * @param componentImplementation the concrete implementation
087         * @param parameters the parameters to use for the initialization
088         * @param allowNonPublicClasses flag to allow instantiation of non-public classes
089         * @param monitor the component monitor used by this ComponentAdapter
090         * @throws AssignabilityRegistrationException if the key is a type and the implementation cannot be assigned to
091         * @throws NotConcreteRegistrationException if the implementation is not a concrete class
092         * @throws NullPointerException if one of the parameters is <code>null</code>
093         */
094        protected InstantiatingComponentAdapter(Object componentKey, Class componentImplementation, 
095                Parameter[] parameters, boolean allowNonPublicClasses,
096                ComponentMonitor monitor) {
097            this(componentKey, componentImplementation, parameters, allowNonPublicClasses, monitor, new DefaultLifecycleStrategy(monitor));
098        }
099    
100        /**
101         * Constructs a new ComponentAdapter for the given key and implementation. 
102         * @param componentKey the search key for this implementation
103         * @param componentImplementation the concrete implementation
104         * @param parameters the parameters to use for the initialization
105         * @param allowNonPublicClasses flag to allow instantiation of non-public classes.
106         * @throws AssignabilityRegistrationException if the key is a type and the implementation cannot be assigned to.
107         * @throws NotConcreteRegistrationException if the implementation is not a concrete class.
108         * @throws NullPointerException if one of the parameters is <code>null</code>
109         */
110        protected InstantiatingComponentAdapter(Object componentKey, Class componentImplementation, Parameter[] parameters, boolean allowNonPublicClasses) {
111            this(componentKey, componentImplementation, parameters, allowNonPublicClasses, new DelegatingComponentMonitor());
112        }
113        
114        private void checkConcrete() throws NotConcreteRegistrationException {
115            // Assert that the component class is concrete.
116            boolean isAbstract = (getComponentImplementation().getModifiers() & Modifier.ABSTRACT) == Modifier.ABSTRACT;
117            if (getComponentImplementation().isInterface() || isAbstract) {
118                throw new NotConcreteRegistrationException(getComponentImplementation());
119            }
120        }
121    
122        /**
123         * Create default parameters for the given types.
124         *
125         * @param parameters the parameter types
126         * @return the array with the default parameters.
127         */
128        protected Parameter[] createDefaultParameters(Class[] parameters) {
129            Parameter[] componentParameters = new Parameter[parameters.length];
130            for (int i = 0; i < parameters.length; i++) {
131                componentParameters[i] = ComponentParameter.DEFAULT;
132            }
133            return componentParameters;
134        }
135    
136        public void verify(final PicoContainer container) throws PicoIntrospectionException {
137            if (verifyingGuard == null) {
138                verifyingGuard = new Guard() {
139                    public Object run() {
140                        final Constructor constructor = getGreediestSatisfiableConstructor(guardedContainer);
141                        final Class[] parameterTypes = constructor.getParameterTypes();
142                        final Parameter[] currentParameters = parameters != null ? parameters : createDefaultParameters(parameterTypes);
143                        for (int i = 0; i < currentParameters.length; i++) {
144                            currentParameters[i].verify(container, InstantiatingComponentAdapter.this, parameterTypes[i]);
145                        }
146                        return null;
147                    }
148                };
149            }
150            verifyingGuard.setArguments(container);
151            verifyingGuard.observe(getComponentImplementation());
152        }
153    
154        public void accept(PicoVisitor visitor) {
155            super.accept(visitor);
156            if (parameters != null) {
157                for (int i = 0; i < parameters.length; i++) {
158                    parameters[i].accept(visitor);
159                }
160            }
161        }
162      
163        public void start(Object component) {
164            lifecycleStrategy.start(component);
165        }
166    
167        public void stop(Object component) {
168            lifecycleStrategy.stop(component);
169        }
170    
171        public void dispose(Object component) {
172            lifecycleStrategy.dispose(component);
173        }
174    
175        public boolean hasLifecycle(Class type) {
176            return lifecycleStrategy.hasLifecycle(type);
177        }
178    
179        /**
180         * Instantiate an object with given parameters and respect the accessible flag.
181         * 
182         * @param constructor the constructor to use
183         * @param parameters the parameters for the constructor 
184         * @return the new object.
185         * @throws InstantiationException
186         * @throws IllegalAccessException
187         * @throws InvocationTargetException
188         */
189        protected Object newInstance(Constructor constructor, Object[] parameters) throws InstantiationException, IllegalAccessException, InvocationTargetException {
190            if (allowNonPublicClasses) {
191                constructor.setAccessible(true);
192            }
193            return constructor.newInstance(parameters);
194        }
195    
196        /**
197         * Find and return the greediest satisfiable constructor.
198         * 
199         * @param container the PicoContainer to resolve dependencies.
200         * @return the found constructor.
201         * @throws PicoIntrospectionException
202         * @throws UnsatisfiableDependenciesException
203         * @throws AmbiguousComponentResolutionException
204         * @throws AssignabilityRegistrationException
205         * @throws NotConcreteRegistrationException
206         */
207        protected abstract Constructor getGreediestSatisfiableConstructor(PicoContainer container) throws PicoIntrospectionException, UnsatisfiableDependenciesException, AmbiguousComponentResolutionException, AssignabilityRegistrationException, NotConcreteRegistrationException;
208    }