1 /******************************************************************************
2 * Copyright (C) PicoContainer Organization. All rights reserved. *
3 * ------------------------------------------------------------------------- *
4 * The software in this package is published under the terms of the BSD *
5 * style license a copy of which has been included with this distribution in *
6 * the LICENSE.txt file. *
7 * *
8 * Idea by Rachel Davies, Original code by Aslak Hellesoy and Paul Hammant *
9 *****************************************************************************/
10
11 package picocontainer.defaults;
12
13 import picocontainer.hierarchical.DuplicateComponentTypeRegistrationException;
14 import picocontainer.hierarchical.AssignabilityRegistrationException;
15 import picocontainer.hierarchical.NotConcreteRegistrationException;
16 import picocontainer.hierarchical.WrongNumberOfConstructorsRegistrationException;
17 import picocontainer.hierarchical.AmbiguousComponentResolutionException;
18 import picocontainer.hierarchical.UnsatisfiedDependencyStartupException;
19 import picocontainer.PicoContainer;
20 import picocontainer.ComponentFactory;
21 import picocontainer.PicoRegistrationException;
22 import picocontainer.PicoInitializationException;
23 import picocontainer.PicoInvocationTargetInitailizationException;
24
25 import java.util.Set;
26 import java.util.HashSet;
27 import java.util.List;
28 import java.util.Arrays;
29 import java.util.Map;
30 import java.util.HashMap;
31 import java.util.ArrayList;
32 import java.util.Collections;
33 import java.util.Iterator;
34 import java.util.LinkedList;
35 import java.lang.reflect.Method;
36 import java.lang.reflect.Proxy;
37 import java.lang.reflect.InvocationHandler;
38 import java.lang.reflect.InvocationTargetException;
39 import java.lang.reflect.Constructor;
40 import java.lang.reflect.Modifier;
41
42 /***
43 * Abstract baseclass for various PicoContainer implementations.
44 *
45 * @author Aslak Hellesoy
46 * @version $Revision: 1.8 $
47 */
48 public class DefaultPicoContainer implements PicoContainer {
49
50 private final ComponentFactory componentFactory;
51 private List registeredComponents = new ArrayList();
52 private Map componentTypeToInstanceMap = new HashMap();
53
54 // Keeps track of the instantiation order
55 protected List orderedComponents = new ArrayList();
56
57 // Keeps track of unmanaged components - components instantiated outside this container
58 protected List unmanagedComponents = new ArrayList();
59
60 private Map parametersForComponent = new HashMap();
61 private boolean initialized;
62
63 public static class Default extends DefaultPicoContainer {
64 public Default() {
65 super(new DefaultComponentFactory());
66 }
67 }
68
69 public DefaultPicoContainer(ComponentFactory componentFactory) {
70 if (componentFactory == null) {
71 throw new NullPointerException("componentFactory cannot be null");
72 }
73 this.componentFactory = componentFactory;
74 }
75
76 public final Object[] getComponents() {
77 Class[] componentTypes = getComponentTypes();
78 Object[] components = new Object[componentTypes.length];
79 for (int i = 0; i < componentTypes.length; i++) {
80 Class componentType = componentTypes[i];
81 components[i] = getComponent(componentType);
82 }
83 return components;
84 }
85
86 /***
87 * Shorthand for {@link #getAggregateComponentProxy(boolean, boolean)}(true, true).
88 * @return a proxy.
89 */
90 public Object getMultipleInheritanceProxy() {
91 return getAggregateComponentProxy( true, true);
92 }
93
94 /***
95 * Returns a proxy that implements the union of all the components'
96 * interfaces.
97 * Calling a method on the returned Object will call the
98 * method on all components in the container that implement
99 * that interface.
100 *
101 * @param callInInstantiationOrder whether to call the methods in the order of instantiation (true) or reverse (false)
102 * @param callInInstantiationOrder whether to exclude components registered with {@link #registerComponent(Class, Object)}
103 * or {@link #registerComponent(Object)}
104 */
105 public Object getAggregateComponentProxy(boolean callInInstantiationOrder, boolean callUnmanagedComponents) {
106 return Proxy.newProxyInstance(
107 getClass().getClassLoader(),
108 getComponentInterfaces(),
109 new ComponentsInvocationHandler(callInInstantiationOrder, callUnmanagedComponents));
110 }
111
112 private class ComponentsInvocationHandler implements InvocationHandler {
113 private boolean callInInstantiationOrder;
114 private boolean callUnmanagedComponents;
115
116 public ComponentsInvocationHandler(boolean callInInstantiationOrder, boolean callUnmanagedComponents) {
117 this.callInInstantiationOrder = callInInstantiationOrder;
118 this.callUnmanagedComponents = callUnmanagedComponents;
119 }
120
121 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
122
123 List orderedComponentsCopy = new ArrayList(orderedComponents);
124
125 if( !callInInstantiationOrder ) {
126 // reverse the list
127 Collections.reverse(orderedComponentsCopy);
128 }
129 Object[] components = orderedComponentsCopy.toArray();
130 return invokeOnComponents(components, method, args);
131 }
132
133 private Object invokeOnComponents(Object[] components, Method method, Object[] args) throws IllegalAccessException, InvocationTargetException {
134 Object result = null;
135 int invokeCount = 0;
136 for (int i = 0; i < components.length; i++) {
137 Class declarer = method.getDeclaringClass();
138 boolean isValidType = declarer.isAssignableFrom(components[i].getClass());
139 boolean isUnmanaged = unmanagedComponents.contains(components[i]);
140 boolean exclude = !callUnmanagedComponents && isUnmanaged;
141 if (isValidType && !exclude) {
142 // It's ok to call the method on this one
143 Object resultCandidate = method.invoke(components[i], args);
144 invokeCount++;
145 if( invokeCount == 1 ) {
146 result = resultCandidate;
147 } else {
148 result = null;
149 }
150 }
151 }
152 return result;
153 }
154 }
155
156 /***
157 * Get all the interfaces implemented by the registered component instances.
158 * @return an array of interfaces implemented by the concrete component instances.
159 */
160 private final Class[] getComponentInterfaces() {
161 Set interfaces = new HashSet();
162 Object[] components = getComponents();
163 for (int i = 0; i < components.length; i++) {
164 Class componentClass = components[i].getClass();
165 // Strangely enough Class.getInterfaces() does not include the interfaces
166 // implemented by superclasses. So we must loop up the hierarchy.
167 while( componentClass != null ) {
168 Class[] implemeted = componentClass.getInterfaces();
169 List implementedList = Arrays.asList(implemeted);
170 interfaces.addAll(implementedList);
171 componentClass = componentClass.getSuperclass();
172 }
173 }
174
175 Class[] result = (Class[]) interfaces.toArray(new Class[interfaces.size()]);
176 return result;
177 }
178
179 public void registerComponent(Class componentType, Class componentImplementation) throws DuplicateComponentTypeRegistrationException, AssignabilityRegistrationException, NotConcreteRegistrationException, WrongNumberOfConstructorsRegistrationException {
180 checkConcrete(componentImplementation);
181 checkConstructor(componentImplementation);
182 checkTypeCompatibility(componentType, componentImplementation);
183 checkTypeDuplication(componentType);
184 registeredComponents.add(new ComponentSpecification(componentType, componentImplementation));
185 }
186
187 private void checkConstructor(Class componentImplementation) throws WrongNumberOfConstructorsRegistrationException {
188 // TODO move this check to checkConstructor and rename the exception to
189 // WrongNumberOfConstructorsRegistrationException extends PicoRegistrationException
190 Constructor[] constructors = componentImplementation.getConstructors();
191 if (constructors.length != 1) {
192 throw new WrongNumberOfConstructorsRegistrationException(constructors.length);
193 }
194 }
195
196 private void checkTypeDuplication(Class componentType) throws DuplicateComponentTypeRegistrationException {
197 for (Iterator iterator = registeredComponents.iterator(); iterator.hasNext();) {
198 Class aClass = ((ComponentSpecification) iterator.next()).getComponentType();
199 if (aClass == componentType) {
200 throw new DuplicateComponentTypeRegistrationException(aClass);
201 }
202 }
203 }
204
205 private void checkTypeCompatibility(Class componentType, Class componentImplementation) throws AssignabilityRegistrationException {
206 if (!componentType.isAssignableFrom(componentImplementation)) {
207 throw new AssignabilityRegistrationException(componentType, componentImplementation);
208 }
209 }
210
211 private void checkConcrete(Class componentImplementation) throws NotConcreteRegistrationException {
212 // Assert that the component class is concrete.
213 boolean isAbstract = (componentImplementation.getModifiers() & Modifier.ABSTRACT) == Modifier.ABSTRACT;
214 if (componentImplementation.isInterface() || isAbstract) {
215 throw new NotConcreteRegistrationException(componentImplementation);
216 }
217 }
218
219 public void registerComponent(Object component) throws PicoRegistrationException {
220 registerComponent(component.getClass(), component);
221 }
222
223 public void registerComponent(Class componentType, Object component) throws PicoRegistrationException {
224 checkTypeCompatibility(componentType, component.getClass());
225 checkTypeDuplication(componentType);
226 //checkImplementationDuplication(component.getClass());
227 componentTypeToInstanceMap.put(componentType, component);
228 orderedComponents.add(component);
229 unmanagedComponents.add(component);
230 }
231
232 public void addParameterToComponent(Class componentType, Class parameter, Object arg) {
233 if (!parametersForComponent.containsKey(componentType)) {
234 parametersForComponent.put(componentType, new ArrayList());
235 }
236 List args = (List) parametersForComponent.get(componentType);
237 args.add(new ParameterSpec(arg));
238 }
239
240 public void registerComponent(Class componentImplementation) throws DuplicateComponentTypeRegistrationException, AssignabilityRegistrationException, NotConcreteRegistrationException, WrongNumberOfConstructorsRegistrationException {
241 registerComponent(componentImplementation, componentImplementation);
242 }
243
244 private class ParameterSpec {
245 private Object arg;
246
247 ParameterSpec(Object parameter) {
248 this.arg = parameter;
249 }
250 }
251
252 public void instantiateComponents() throws PicoInitializationException {
253 if (initialized == false) {
254 initializeComponents();
255 checkUnsatisfiedDependencies();
256 initialized = true;
257 } else {
258 throw new IllegalStateException("PicoContainer Started Already");
259 }
260 }
261
262 // This is Lazy and NOT public :-)
263 private void initializeComponents() throws AmbiguousComponentResolutionException, PicoInvocationTargetInitailizationException {
264 boolean progress = true;
265 while (progress == true) {
266 progress = false;
267
268 for (Iterator iterator = registeredComponents.iterator(); iterator.hasNext();) {
269 ComponentSpecification componentSpec = (ComponentSpecification) iterator.next();
270 Class componentImplementation = componentSpec.getComponentImplementation();
271 Class componentType = componentSpec.getComponentType();
272
273 if (componentTypeToInstanceMap.get(componentType) == null) {
274 boolean reused = reuseImplementationIfAppropriate(componentType, componentImplementation);
275 if (reused) {
276 progress = true;
277 } else {
278 // hook'em up
279 progress = hookEmUp(componentImplementation, componentType, progress);
280 }
281 }
282 }
283 }
284 }
285
286 protected boolean hookEmUp(Class componentImplementation, Class componentType, boolean progress) throws AmbiguousComponentResolutionException, PicoInvocationTargetInitailizationException {
287 Constructor[] constructors = componentImplementation.getConstructors();
288 Constructor constructor = constructors[0];
289 Class[] parameters = constructor.getParameterTypes();
290
291 List paramSpecs = (List) parametersForComponent.get(componentImplementation);
292 paramSpecs = paramSpecs == null ? Collections.EMPTY_LIST : new LinkedList(paramSpecs); // clone because we are going to modify it
293
294 // For each param, look up the instantiated componentImplementation.
295 Object[] args = new Object[parameters.length];
296 for (int i = 0; i < parameters.length; i++) {
297 Class param = parameters[i];
298 args[i] = getComponentForParam(param); // lookup a service for this param
299 if (args[i] == null && !paramSpecs.isEmpty()) { // failing that, check if any params are available from addParameterToComponent()
300 args[i] = ((ParameterSpec) paramSpecs.remove(0)).arg;
301 }
302 }
303 if (hasAnyNullArguments(args) == false) {
304 Object componentInstance = null;
305 componentInstance = makeComponentInstance(componentType, constructor, args);
306 // Put the instantiated comp back in the map
307 componentTypeToInstanceMap.put(componentType, componentInstance);
308 orderedComponents.add(componentInstance);
309 progress = true;
310 }
311
312 return progress;
313 }
314
315 protected boolean reuseImplementationIfAppropriate(Class componentType, Class componentImplementation) {
316 Set compEntries = componentTypeToInstanceMap.entrySet();
317 for (Iterator iterator = compEntries.iterator();
318 iterator.hasNext();) {
319 Map.Entry entry = (Map.Entry) iterator.next();
320 Object exisitingCompClass = entry.getValue();
321 if (exisitingCompClass.getClass() == componentImplementation) {
322 componentTypeToInstanceMap.put(componentType, exisitingCompClass);
323 return true;
324 }
325 }
326 return false;
327 }
328
329 private void checkUnsatisfiedDependencies() throws UnsatisfiedDependencyStartupException {
330 for (Iterator iterator = registeredComponents.iterator(); iterator.hasNext();) {
331 ComponentSpecification componentSpecification = (ComponentSpecification) iterator.next();
332 Class componentType = componentSpecification.getComponentType();
333 if (componentTypeToInstanceMap.get(componentType) == null) {
334 throw new UnsatisfiedDependencyStartupException(componentType);
335 }
336 }
337 }
338
339 protected Object makeComponentInstance(Class type, Constructor constructor, Object[] args) throws PicoInvocationTargetInitailizationException {
340 return componentFactory.createComponent(type, constructor, args);
341 }
342
343 protected Object getComponentForParam(Class parameter) throws AmbiguousComponentResolutionException {
344 Object result = null;
345
346 // We're keeping track of all candidate parameters, so we can bomb with a detailed error message
347 // if there is ambiguity
348 List candidateClasses = new ArrayList();
349
350 for (Iterator iterator = componentTypeToInstanceMap.entrySet().iterator(); iterator.hasNext();) {
351 Map.Entry entry = (Map.Entry) iterator.next();
352 Class clazz = (Class) entry.getKey();
353 if (parameter.isAssignableFrom(clazz)) {
354 candidateClasses.add(clazz);
355 result = entry.getValue();
356 }
357 }
358
359 // We should only have one here.
360 if (candidateClasses.size() > 1) {
361 Class[] ambiguities = (Class[]) candidateClasses.toArray(new Class[candidateClasses.size()]);
362 throw new AmbiguousComponentResolutionException(ambiguities);
363 }
364
365 return result;
366 }
367
368 private boolean hasAnyNullArguments(Object[] args) {
369 for (int i = 0; i < args.length; i++) {
370 Object arg = args[i];
371 if (arg == null) {
372 return true;
373 }
374 }
375 return false;
376 }
377
378 public Object getComponent(Class componentType) {
379 return componentTypeToInstanceMap.get(componentType);
380 }
381
382 public Class[] getComponentTypes() {
383 // Get my own
384 Set types = componentTypeToInstanceMap.keySet();
385 return (Class[]) types.toArray(new Class[types.size()]);
386 }
387
388 public boolean hasComponent(Class componentType) {
389 return getComponent(componentType) != null;
390 }
391 }
This page was automatically generated by Maven