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 }