View Javadoc
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