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; 011 012 import org.picocontainer.adapters.AbstractAdapter; 013 import org.picocontainer.adapters.InstanceAdapter; 014 import org.picocontainer.behaviors.AbstractBehaviorFactory; 015 import org.picocontainer.behaviors.AdaptingBehavior; 016 import org.picocontainer.behaviors.Cached; 017 import org.picocontainer.behaviors.Caching; 018 import org.picocontainer.behaviors.HiddenImplementation; 019 import org.picocontainer.containers.AbstractDelegatingMutablePicoContainer; 020 import org.picocontainer.containers.AbstractDelegatingPicoContainer; 021 import org.picocontainer.containers.EmptyPicoContainer; 022 import org.picocontainer.containers.ImmutablePicoContainer; 023 import org.picocontainer.converters.BuiltInConverters; 024 import org.picocontainer.converters.ConvertsNothing; 025 import org.picocontainer.injectors.AbstractInjector; 026 import org.picocontainer.injectors.AdaptingInjection; 027 import org.picocontainer.injectors.FactoryInjector; 028 import org.picocontainer.lifecycle.DefaultLifecycleState; 029 import org.picocontainer.lifecycle.LifecycleState; 030 import org.picocontainer.lifecycle.StartableLifecycleStrategy; 031 import org.picocontainer.monitors.NullComponentMonitor; 032 import org.picocontainer.parameters.DefaultConstructorParameter; 033 034 import java.io.Serializable; 035 import java.lang.annotation.Annotation; 036 import java.lang.ref.WeakReference; 037 import java.lang.reflect.Type; 038 import java.util.ArrayList; 039 import java.util.Collection; 040 import java.util.Collections; 041 import java.util.Enumeration; 042 import java.util.HashMap; 043 import java.util.HashSet; 044 import java.util.List; 045 import java.util.Map; 046 import java.util.Properties; 047 import java.util.Set; 048 049 /** 050 * <p/> 051 * The Standard {@link PicoContainer}/{@link MutablePicoContainer} implementation. 052 * Constructing a container c with a parent p container will cause c to look up components 053 * in p if they cannot be found inside c itself. 054 * </p> 055 * <p/> 056 * Using {@link Class} objects as keys to the various registerXXX() methods makes 057 * a subtle semantic difference: 058 * </p> 059 * <p/> 060 * If there are more than one registered components of the same type and one of them are 061 * registered with a {@link java.lang.Class} key of the corresponding type, this addComponent 062 * will take precedence over other components during type resolution. 063 * </p> 064 * <p/> 065 * Another place where keys that are classes make a subtle difference is in 066 * {@link HiddenImplementation}. 067 * </p> 068 * <p/> 069 * This implementation of {@link MutablePicoContainer} also supports 070 * {@link ComponentMonitorStrategy}. 071 * </p> 072 * 073 * @author Paul Hammant 074 * @author Aslak Hellesøy 075 * @author Jon Tirsén 076 * @author Thomas Heller 077 * @author Mauro Talevi 078 */ 079 @SuppressWarnings("serial") 080 public class DefaultPicoContainer implements MutablePicoContainer, Converting, ComponentMonitorStrategy, Serializable { 081 082 private String name; 083 084 /** 085 * Component factory instance. 086 */ 087 protected final ComponentFactory componentFactory; 088 089 /** 090 * Parent picocontainer 091 */ 092 private PicoContainer parent; 093 094 /** 095 * All picocontainer children. 096 */ 097 private final Set<PicoContainer> children = new HashSet<PicoContainer>(); 098 099 /** 100 * Current state of the container. 101 */ 102 private LifecycleState lifecycleState = new DefaultLifecycleState(); 103 104 /** 105 * Keeps track of child containers started status. 106 */ 107 private final Set<WeakReference<PicoContainer>> childrenStarted = new HashSet<WeakReference<PicoContainer>>(); 108 109 /** 110 * Lifecycle strategy instance. 111 */ 112 protected final LifecycleStrategy lifecycleStrategy; 113 114 /** 115 * Properties set at the container level, that will affect subsequent components added. 116 */ 117 private final Properties containerProperties = new Properties(); 118 119 /** 120 * Component monitor instance. Receives event callbacks. 121 */ 122 protected ComponentMonitor componentMonitor; 123 124 /** 125 * Map used for looking up component adapters by their key. 126 */ 127 private final Map<Object, ComponentAdapter<?>> componentKeyToAdapterCache = new HashMap<Object, ComponentAdapter<?> >(); 128 129 130 private final List<ComponentAdapter<?>> componentAdapters = new ArrayList<ComponentAdapter<?>>(); 131 132 133 protected final List<ComponentAdapter<?>> orderedComponentAdapters = new ArrayList<ComponentAdapter<?>>(); 134 135 136 private transient IntoThreadLocal intoThreadLocal = new IntoThreadLocal(); 137 private Converters converters; 138 139 140 /** 141 * Creates a new container with a custom ComponentFactory and a parent container. 142 * <p/> 143 * <em> 144 * Important note about caching: If you intend the components to be cached, you should pass 145 * in a factory that creates {@link Cached} instances, such as for example 146 * {@link Caching}. Caching can delegate to 147 * other ComponentAdapterFactories. 148 * </em> 149 * 150 * @param componentFactory the factory to use for creation of ComponentAdapters. 151 * @param parent the parent container (used for component dependency lookups). 152 */ 153 public DefaultPicoContainer(final ComponentFactory componentFactory, final PicoContainer parent) { 154 this(componentFactory, new StartableLifecycleStrategy(new NullComponentMonitor()), parent, new NullComponentMonitor()); 155 } 156 157 /** 158 * Creates a new container with a custom ComponentFactory, LifecycleStrategy for instance registration, 159 * and a parent container. 160 * <p/> 161 * <em> 162 * Important note about caching: If you intend the components to be cached, you should pass 163 * in a factory that creates {@link Cached} instances, such as for example 164 * {@link Caching}. Caching can delegate to 165 * other ComponentAdapterFactories. 166 * </em> 167 * 168 * @param componentFactory the factory to use for creation of ComponentAdapters. 169 * @param lifecycleStrategy 170 * the lifecycle strategy chosen for registered 171 * instance (not implementations!) 172 * @param parent the parent container (used for component dependency lookups). 173 */ 174 public DefaultPicoContainer(final ComponentFactory componentFactory, 175 final LifecycleStrategy lifecycleStrategy, 176 final PicoContainer parent) { 177 this(componentFactory, lifecycleStrategy, parent, new NullComponentMonitor() ); 178 } 179 180 public DefaultPicoContainer(final ComponentFactory componentFactory, 181 final LifecycleStrategy lifecycleStrategy, 182 final PicoContainer parent, final ComponentMonitor componentMonitor) { 183 if (componentFactory == null) { 184 throw new NullPointerException("componentFactory"); 185 } 186 if (lifecycleStrategy == null) { 187 throw new NullPointerException("lifecycleStrategy"); 188 } 189 this.componentFactory = componentFactory; 190 this.lifecycleStrategy = lifecycleStrategy; 191 this.parent = parent; 192 if (parent != null && !(parent instanceof EmptyPicoContainer)) { 193 this.parent = new ImmutablePicoContainer(parent); 194 } 195 this.componentMonitor = componentMonitor; 196 } 197 198 /** 199 * Creates a new container with the AdaptingInjection using a 200 * custom ComponentMonitor 201 * 202 * @param monitor the ComponentMonitor to use 203 * @param parent the parent container (used for component dependency lookups). 204 */ 205 public DefaultPicoContainer(final ComponentMonitor monitor, final PicoContainer parent) { 206 this(new AdaptingBehavior(), new StartableLifecycleStrategy(monitor), parent, monitor); 207 } 208 209 /** 210 * Creates a new container with the AdaptingInjection using a 211 * custom ComponentMonitor and lifecycle strategy 212 * 213 * @param monitor the ComponentMonitor to use 214 * @param lifecycleStrategy the lifecycle strategy to use. 215 * @param parent the parent container (used for component dependency lookups). 216 */ 217 public DefaultPicoContainer(final ComponentMonitor monitor, final LifecycleStrategy lifecycleStrategy, final PicoContainer parent) { 218 this(new AdaptingBehavior(), lifecycleStrategy, parent, monitor); 219 } 220 221 /** 222 * Creates a new container with the AdaptingInjection using a 223 * custom lifecycle strategy 224 * 225 * @param lifecycleStrategy the lifecycle strategy to use. 226 * @param parent the parent container (used for component dependency lookups). 227 */ 228 public DefaultPicoContainer(final LifecycleStrategy lifecycleStrategy, final PicoContainer parent) { 229 this(new NullComponentMonitor(), lifecycleStrategy, parent); 230 } 231 232 233 /** 234 * Creates a new container with a custom ComponentFactory and no parent container. 235 * 236 * @param componentFactory the ComponentFactory to use. 237 */ 238 public DefaultPicoContainer(final ComponentFactory componentFactory) { 239 this(componentFactory, null); 240 } 241 242 /** 243 * Creates a new container with the AdaptingInjection using a 244 * custom ComponentMonitor 245 * 246 * @param monitor the ComponentMonitor to use 247 */ 248 public DefaultPicoContainer(final ComponentMonitor monitor) { 249 this(monitor, new StartableLifecycleStrategy(monitor), null); 250 } 251 252 /** 253 * Creates a new container with a (caching) {@link AdaptingInjection} 254 * and a parent container. 255 * 256 * @param parent the parent container (used for component dependency lookups). 257 */ 258 public DefaultPicoContainer(final PicoContainer parent) { 259 this(new AdaptingBehavior(), parent); 260 } 261 262 /** Creates a new container with a {@link AdaptingBehavior} and no parent container. */ 263 public DefaultPicoContainer() { 264 this(new AdaptingBehavior(), null); 265 } 266 267 /** {@inheritDoc} **/ 268 public Collection<ComponentAdapter<?>> getComponentAdapters() { 269 return Collections.unmodifiableList(getModifiableComponentAdapterList()); 270 } 271 272 273 /** {@inheritDoc} **/ 274 public final ComponentAdapter<?> getComponentAdapter(final Object componentKey) { 275 ComponentAdapter<?> adapter = getComponentKeyToAdapterCache().get(componentKey); 276 if (adapter == null && parent != null) { 277 adapter = getParent().getComponentAdapter(componentKey); 278 if (adapter != null) { 279 adapter = new KnowsContainerAdapter(adapter, getParent()); 280 } 281 } 282 if (adapter == null) { 283 Object inst = componentMonitor.noComponentFound(this, componentKey); 284 if (inst != null) { 285 adapter = new LateInstance(componentKey, inst); 286 } 287 } 288 return adapter; 289 } 290 291 public static class LateInstance extends AbstractAdapter { 292 private final Object instance; 293 private LateInstance(Object componentKey, Object instance) { 294 super(componentKey, instance.getClass()); 295 this.instance = instance; 296 } 297 298 public Object getComponentInstance() { 299 return instance; 300 } 301 302 public Object getComponentInstance(PicoContainer container, Type into) throws PicoCompositionException { 303 return instance; 304 } 305 306 public void verify(PicoContainer container) throws PicoCompositionException { 307 } 308 309 public String getDescriptor() { 310 return "LateInstance"; 311 } 312 } 313 314 public static class KnowsContainerAdapter<T> implements ComponentAdapter<T> { 315 private final ComponentAdapter<T> ca; 316 private final PicoContainer ctr; 317 318 public KnowsContainerAdapter(ComponentAdapter<T> ca, PicoContainer ctr) { 319 this.ca = ca; 320 this.ctr = ctr; 321 } 322 323 public T getComponentInstance(Type into) throws PicoCompositionException { 324 return getComponentInstance(ctr, into); 325 } 326 327 public Object getComponentKey() { 328 return ca.getComponentKey(); 329 } 330 331 public Class getComponentImplementation() { 332 return ca.getComponentImplementation(); 333 } 334 335 public T getComponentInstance(PicoContainer container) throws PicoCompositionException { 336 return ca.getComponentInstance(container); 337 } 338 339 public T getComponentInstance(PicoContainer container, Type into) throws PicoCompositionException { 340 return ca.getComponentInstance(container, into); 341 } 342 343 public void verify(PicoContainer container) throws PicoCompositionException { 344 ca.verify(container); 345 } 346 347 public void accept(PicoVisitor visitor) { 348 ca.accept(visitor); 349 } 350 351 public ComponentAdapter getDelegate() { 352 return ca.getDelegate(); 353 } 354 355 public <U extends ComponentAdapter> U findAdapterOfType(Class<U> adapterType) { 356 return ca.findAdapterOfType(adapterType); 357 } 358 359 public String getDescriptor() { 360 return null; 361 } 362 } 363 364 /** {@inheritDoc} **/ 365 public <T> ComponentAdapter<T> getComponentAdapter(final Class<T> componentType, final NameBinding componentNameBinding) { 366 return getComponentAdapter(componentType, componentNameBinding, null); 367 } 368 369 /** {@inheritDoc} **/ 370 private <T> ComponentAdapter<T> getComponentAdapter(final Class<T> componentType, final NameBinding componentNameBinding, final Class<? extends Annotation> binding) { 371 // See http://jira.codehaus.org/secure/ViewIssue.jspa?key=PICO-115 372 ComponentAdapter<?> adapterByKey = getComponentAdapter(componentType); 373 if (adapterByKey != null) { 374 return typeComponentAdapter(adapterByKey); 375 } 376 377 List<ComponentAdapter<T>> found = binding == null ? getComponentAdapters(componentType) : getComponentAdapters(componentType, binding); 378 379 if (found.size() == 1) { 380 return found.get(0); 381 } else if (found.isEmpty()) { 382 if (parent != null) { 383 return getParent().getComponentAdapter(componentType, componentNameBinding); 384 } else { 385 return null; 386 } 387 } else { 388 if (componentNameBinding != null) { 389 String parameterName = componentNameBinding.getName(); 390 if (parameterName != null) { 391 ComponentAdapter<?> ca = getComponentAdapter(parameterName); 392 if (ca != null && componentType.isAssignableFrom(ca.getComponentImplementation())) { 393 return typeComponentAdapter(ca); 394 } 395 } 396 } 397 Class<?>[] foundClasses = new Class[found.size()]; 398 for (int i = 0; i < foundClasses.length; i++) { 399 foundClasses[i] = found.get(i).getComponentImplementation(); 400 } 401 402 throw new AbstractInjector.AmbiguousComponentResolutionException(componentType, foundClasses); 403 } 404 } 405 406 /** {@inheritDoc} **/ 407 public <T> ComponentAdapter<T> getComponentAdapter(final Class<T> componentType, final Class<? extends Annotation> binding) { 408 // 1 409 return getComponentAdapter(componentType, null, binding); 410 } 411 412 /** {@inheritDoc} **/ 413 public <T> List<ComponentAdapter<T>> getComponentAdapters(final Class<T> componentType) { 414 return getComponentAdapters(componentType, null); 415 } 416 417 /** {@inheritDoc} **/ 418 public <T> List<ComponentAdapter<T>> getComponentAdapters(final Class<T> componentType, final Class<? extends Annotation> binding) { 419 if (componentType == null) { 420 return Collections.emptyList(); 421 } 422 List<ComponentAdapter<T>> found = new ArrayList<ComponentAdapter<T>>(); 423 for (ComponentAdapter<?> componentAdapter : getComponentAdapters()) { 424 Object k = componentAdapter.getComponentKey(); 425 426 if (componentType.isAssignableFrom(componentAdapter.getComponentImplementation()) && 427 (!(k instanceof BindKey) || (k instanceof BindKey && (((BindKey<?>)k).getAnnotation() == null || binding == null || 428 ((BindKey<?>)k).getAnnotation() == binding)))) { 429 found.add((ComponentAdapter<T>)typeComponentAdapter(componentAdapter)); 430 } 431 } 432 return found; 433 } 434 435 protected MutablePicoContainer addAdapterInternal(ComponentAdapter<?> componentAdapter) { 436 Object componentKey = componentAdapter.getComponentKey(); 437 if (getComponentKeyToAdapterCache().containsKey(componentKey)) { 438 throw new PicoCompositionException("Duplicate Keys not allowed. Duplicate for '" + componentKey + "'"); 439 } 440 getModifiableComponentAdapterList().add(componentAdapter); 441 getComponentKeyToAdapterCache().put(componentKey, componentAdapter); 442 return this; 443 } 444 445 /** 446 * {@inheritDoc} 447 * This method can be used to override the ComponentAdapter created by the {@link ComponentFactory} 448 * passed to the constructor of this container. 449 */ 450 public MutablePicoContainer addAdapter(final ComponentAdapter<?> componentAdapter) { 451 return addAdapter(componentAdapter, this.containerProperties); 452 } 453 454 /** {@inheritDoc} **/ 455 public MutablePicoContainer addAdapter(final ComponentAdapter<?> componentAdapter, final Properties properties) { 456 Properties tmpProperties = (Properties)properties.clone(); 457 if (AbstractBehaviorFactory.removePropertiesIfPresent(tmpProperties, Characteristics.NONE) == false && componentFactory instanceof BehaviorFactory) { 458 MutablePicoContainer container = addAdapterInternal(((BehaviorFactory)componentFactory).addComponentAdapter( 459 componentMonitor, 460 lifecycleStrategy, 461 tmpProperties, 462 componentAdapter)); 463 throwIfPropertiesLeft(tmpProperties); 464 return container; 465 } else { 466 return addAdapterInternal(componentAdapter); 467 } 468 469 } 470 471 472 /** {@inheritDoc} **/ 473 public <T> ComponentAdapter<T> removeComponent(final Object componentKey) { 474 lifecycleState.removingComponent(); 475 476 ComponentAdapter<T> adapter = (ComponentAdapter<T>) getComponentKeyToAdapterCache().remove(componentKey); 477 getModifiableComponentAdapterList().remove(adapter); 478 getOrderedComponentAdapters().remove(adapter); 479 return adapter; 480 } 481 482 /** 483 * {@inheritDoc} 484 * The returned ComponentAdapter will be an {@link org.picocontainer.adapters.InstanceAdapter}. 485 */ 486 public MutablePicoContainer addComponent(final Object implOrInstance) { 487 return addComponent(implOrInstance, this.containerProperties); 488 } 489 490 private MutablePicoContainer addComponent(final Object implOrInstance, final Properties props) { 491 Class<?> clazz; 492 if (implOrInstance instanceof String) { 493 return addComponent(implOrInstance, implOrInstance); 494 } 495 if (implOrInstance instanceof Class) { 496 clazz = (Class<?>)implOrInstance; 497 } else { 498 clazz = implOrInstance.getClass(); 499 } 500 return addComponent(clazz, implOrInstance, props); 501 } 502 503 504 public MutablePicoContainer addConfig(final String name, final Object val) { 505 return addAdapterInternal(new InstanceAdapter<Object>(name, val, lifecycleStrategy, componentMonitor)); 506 } 507 508 509 /** 510 * {@inheritDoc} 511 * The returned ComponentAdapter will be instantiated by the {@link ComponentFactory} 512 * passed to the container's constructor. 513 */ 514 public MutablePicoContainer addComponent(final Object componentKey, 515 final Object componentImplementationOrInstance, 516 final Parameter... parameters) { 517 return this.addComponent(componentKey, componentImplementationOrInstance, this.containerProperties, parameters); 518 } 519 520 private MutablePicoContainer addComponent(final Object componentKey, 521 final Object componentImplementationOrInstance, 522 final Properties properties, 523 Parameter... parameters) { 524 if (parameters != null && parameters.length == 0) { 525 parameters = null; // backwards compatibility! solve this better later - Paul 526 } 527 528 //New replacement for Parameter.ZERO. 529 if (parameters != null && parameters.length == 1 && DefaultConstructorParameter.INSTANCE.equals(parameters[0])) { 530 parameters = new Parameter[0]; 531 } 532 533 if (componentImplementationOrInstance instanceof Class) { 534 Properties tmpProperties = (Properties) properties.clone(); 535 ComponentAdapter<?> adapter = componentFactory.createComponentAdapter(componentMonitor, 536 lifecycleStrategy, 537 tmpProperties, 538 componentKey, 539 (Class<?>)componentImplementationOrInstance, 540 parameters); 541 AbstractBehaviorFactory.removePropertiesIfPresent(tmpProperties, Characteristics.USE_NAMES); 542 throwIfPropertiesLeft(tmpProperties); 543 if (lifecycleState.isStarted()) { 544 addAdapterIfStartable(adapter); 545 potentiallyStartAdapter(adapter); 546 } 547 return addAdapterInternal(adapter); 548 } else { 549 ComponentAdapter<?> adapter = 550 new InstanceAdapter<Object>(componentKey, componentImplementationOrInstance, lifecycleStrategy, componentMonitor); 551 if (lifecycleState.isStarted()) { 552 addAdapterIfStartable(adapter); 553 potentiallyStartAdapter(adapter); 554 } 555 return addAdapter(adapter, properties); 556 } 557 } 558 559 private void throwIfPropertiesLeft(final Properties tmpProperties) { 560 if(tmpProperties.size() > 0) { 561 throw new PicoCompositionException("Unprocessed Characteristics:" + tmpProperties +", please refer to http://picocontainer.org/help/unprocessed-properties-help.html"); 562 } 563 } 564 565 private void addOrderedComponentAdapter(final ComponentAdapter<?> componentAdapter) { 566 if (!getOrderedComponentAdapters().contains(componentAdapter)) { 567 getOrderedComponentAdapters().add(componentAdapter); 568 } 569 } 570 571 public List<Object> getComponents() throws PicoException { 572 return getComponents(Object.class); 573 } 574 575 public <T> List<T> getComponents(final Class<T> componentType) { 576 if (componentType == null) { 577 return Collections.emptyList(); 578 } 579 580 Map<ComponentAdapter<T>, T> adapterToInstanceMap = new HashMap<ComponentAdapter<T>, T>(); 581 for (ComponentAdapter<?> componentAdapter : getModifiableComponentAdapterList()) { 582 if (componentType.isAssignableFrom(componentAdapter.getComponentImplementation())) { 583 ComponentAdapter<T> typedComponentAdapter = typeComponentAdapter(componentAdapter); 584 T componentInstance = getLocalInstance(typedComponentAdapter); 585 586 adapterToInstanceMap.put(typedComponentAdapter, componentInstance); 587 } 588 } 589 List<T> result = new ArrayList<T>(); 590 for (ComponentAdapter<?> componentAdapter : getOrderedComponentAdapters()) { 591 final T componentInstance = adapterToInstanceMap.get(componentAdapter); 592 if (componentInstance != null) { 593 // may be null in the case of the "implicit" addAdapter 594 // representing "this". 595 result.add(componentInstance); 596 } 597 } 598 return result; 599 } 600 601 private <T> T getLocalInstance(final ComponentAdapter<T> typedComponentAdapter) { 602 T componentInstance = typedComponentAdapter.getComponentInstance(this, ComponentAdapter.NOTHING.class); 603 604 // This is to ensure all are added. (Indirect dependencies will be added 605 // from InstantiatingComponentAdapter). 606 addOrderedComponentAdapter(typedComponentAdapter); 607 608 return componentInstance; 609 } 610 611 @SuppressWarnings({ "unchecked" }) 612 private static <T> ComponentAdapter<T> typeComponentAdapter(final ComponentAdapter<?> componentAdapter) { 613 return (ComponentAdapter<T>)componentAdapter; 614 } 615 616 public Object getComponent(final Object componentKeyOrType) { 617 return getComponent(componentKeyOrType, null); 618 } 619 620 public Object getComponent(final Object componentKeyOrType, Type into) { 621 synchronized (this) { 622 if (intoThreadLocal == null) { 623 intoThreadLocal = new IntoThreadLocal(); 624 } 625 } 626 intoThreadLocal.set(into); 627 return getComponent(componentKeyOrType, (Class<? extends Annotation>) null); 628 } 629 630 public Object getComponent(final Object componentKeyOrType, final Class<? extends Annotation> annotation) { 631 ComponentAdapter<?> componentAdapter; 632 Object component; 633 if (annotation != null) { 634 componentAdapter = getComponentAdapter((Class<?>)componentKeyOrType, annotation); 635 component = componentAdapter == null ? null : getInstance(componentAdapter, null); 636 } else if (componentKeyOrType instanceof Class) { 637 componentAdapter = getComponentAdapter((Class<?>)componentKeyOrType, (NameBinding) null); 638 component = componentAdapter == null ? null : getInstance(componentAdapter, (Class<?>)componentKeyOrType); 639 } else { 640 componentAdapter = getComponentAdapter(componentKeyOrType); 641 component = componentAdapter == null ? null : getInstance(componentAdapter, null); 642 } 643 return decorateComponent(component, componentAdapter); 644 } 645 646 /** 647 * This is invoked when getComponent(..) is called. It allows extendees to decorate a 648 * component before it is returned to the caller. 649 * @param component the component that will be returned for getComponent(..) 650 * @param componentAdapter the component adapter that made that component 651 * @return the component (the same as that passed in by default) 652 */ 653 protected Object decorateComponent(Object component, ComponentAdapter<?> componentAdapter) { 654 if (componentAdapter instanceof ComponentLifecycle<?> 655 && lifecycleStrategy.isLazy(componentAdapter) // is Lazy 656 && !((ComponentLifecycle<?>) componentAdapter).isStarted()) { 657 ((ComponentLifecycle<?>)componentAdapter).start(this); 658 } 659 return component; 660 } 661 662 public <T> T getComponent(final Class<T> componentType) { 663 Object o = getComponent((Object)componentType, null); 664 return componentType.cast(o); 665 } 666 667 public <T> T getComponent(final Class<T> componentType, final Class<? extends Annotation> binding) { 668 Object o = getComponent((Object)componentType, binding); 669 return componentType.cast(o); 670 } 671 672 673 private Object getInstance(final ComponentAdapter<?> componentAdapter, Class componentKey) { 674 // check whether this is our adapter 675 // we need to check this to ensure up-down dependencies cannot be followed 676 final boolean isLocal = getModifiableComponentAdapterList().contains(componentAdapter); 677 678 if (isLocal || componentAdapter instanceof LateInstance) { 679 Object instance; 680 try { 681 if (componentAdapter instanceof FactoryInjector) { 682 instance = ((FactoryInjector) componentAdapter).getComponentInstance(this, intoThreadLocal.get()); 683 } else { 684 synchronized (this) { 685 if (intoThreadLocal == null) { 686 intoThreadLocal = new IntoThreadLocal(); 687 } 688 } 689 instance = componentAdapter.getComponentInstance(this, intoThreadLocal.get()); 690 } 691 } catch (AbstractInjector.CyclicDependencyException e) { 692 if (parent != null) { 693 instance = getParent().getComponent(componentAdapter.getComponentKey()); 694 if (instance != null) { 695 return instance; 696 } 697 } 698 throw e; 699 } 700 addOrderedComponentAdapter(componentAdapter); 701 702 return instance; 703 } else if (parent != null) { 704 return getParent().getComponent(componentAdapter.getComponentKey()); 705 } 706 707 return null; 708 } 709 710 711 /** {@inheritDoc} **/ 712 public PicoContainer getParent() { 713 return parent; 714 } 715 716 /** {@inheritDoc} **/ 717 public <T> ComponentAdapter<T> removeComponentByInstance(final T componentInstance) { 718 for (ComponentAdapter<?> componentAdapter : getModifiableComponentAdapterList()) { 719 if (getLocalInstance(componentAdapter).equals(componentInstance)) { 720 return removeComponent(componentAdapter.getComponentKey()); 721 } 722 } 723 return null; 724 } 725 726 /** 727 * Start the components of this PicoContainer and all its logical child containers. 728 * The starting of the child container is only attempted if the parent 729 * container start successfully. The child container for which start is attempted 730 * is tracked so that upon stop, only those need to be stopped. 731 * The lifecycle operation is delegated to the component adapter, 732 * if it is an instance of {@link Behavior lifecycle manager}. 733 * The actual {@link LifecycleStrategy lifecycle strategy} supported 734 * depends on the concrete implementation of the adapter. 735 * 736 * @see Behavior 737 * @see LifecycleStrategy 738 * @see #makeChildContainer() 739 * @see #addChildContainer(PicoContainer) 740 * @see #removeChildContainer(PicoContainer) 741 */ 742 public void start() { 743 744 lifecycleState.starting(); 745 746 startAdapters(); 747 childrenStarted.clear(); 748 for (PicoContainer child : children) { 749 childrenStarted.add(new WeakReference<PicoContainer>(child)); 750 if (child instanceof Startable) { 751 ((Startable)child).start(); 752 } 753 } 754 } 755 756 /** 757 * Stop the components of this PicoContainer and all its logical child containers. 758 * The stopping of the child containers is only attempted for those that have been 759 * started, possibly not successfully. 760 * The lifecycle operation is delegated to the component adapter, 761 * if it is an instance of {@link Behavior lifecycle manager}. 762 * The actual {@link LifecycleStrategy lifecycle strategy} supported 763 * depends on the concrete implementation of the adapter. 764 * 765 * @see Behavior 766 * @see LifecycleStrategy 767 * @see #makeChildContainer() 768 * @see #addChildContainer(PicoContainer) 769 * @see #removeChildContainer(PicoContainer) 770 */ 771 public void stop() { 772 773 lifecycleState.stopping(); 774 775 for (PicoContainer child : children) { 776 if (childStarted(child)) { 777 if (child instanceof Startable) { 778 ((Startable)child).stop(); 779 } 780 } 781 } 782 stopAdapters(); 783 lifecycleState.stopped(); 784 } 785 786 /** 787 * Checks the status of the child container to see if it's been started 788 * to prevent IllegalStateException upon stop 789 * 790 * @param child the child PicoContainer 791 * 792 * @return A boolean, <code>true</code> if the container is started 793 */ 794 private boolean childStarted(final PicoContainer child) { 795 for (WeakReference<PicoContainer> eachChild : childrenStarted) { 796 PicoContainer ref = eachChild.get(); 797 if (ref == null) { 798 continue; 799 } 800 801 if (child.equals(ref)) { 802 return true; 803 } 804 } 805 return false; 806 } 807 808 /** 809 * Dispose the components of this PicoContainer and all its logical child containers. 810 * The lifecycle operation is delegated to the component adapter, 811 * if it is an instance of {@link Behavior lifecycle manager}. 812 * The actual {@link LifecycleStrategy lifecycle strategy} supported 813 * depends on the concrete implementation of the adapter. 814 * 815 * @see Behavior 816 * @see LifecycleStrategy 817 * @see #makeChildContainer() 818 * @see #addChildContainer(PicoContainer) 819 * @see #removeChildContainer(PicoContainer) 820 */ 821 public void dispose() { 822 if (lifecycleState.isStarted()) { 823 stop(); 824 } 825 826 lifecycleState.disposing(); 827 828 for (PicoContainer child : children) { 829 if (child instanceof MutablePicoContainer) { 830 ((Disposable)child).dispose(); 831 } 832 } 833 disposeAdapters(); 834 835 lifecycleState.disposed(); 836 } 837 838 public void setLifecycleState(LifecycleState lifecycleState) { 839 this.lifecycleState = lifecycleState; 840 } 841 842 public MutablePicoContainer makeChildContainer() { 843 DefaultPicoContainer pc = new DefaultPicoContainer(componentFactory, lifecycleStrategy, this, componentMonitor); 844 addChildContainer(pc); 845 return pc; 846 } 847 848 /** 849 * Checks for identical references in the child container. It doesn't 850 * traverse an entire hierarchy, namely it simply checks for child containers 851 * that are equal to the current container. 852 * @param child 853 */ 854 private void checkCircularChildDependencies(PicoContainer child) { 855 final String MESSAGE = "Cannot have circular dependency between parent %s and child: %s"; 856 if (child == this) { 857 throw new IllegalArgumentException(String.format(MESSAGE,this,child)); 858 } 859 860 //Todo: Circular Import Dependency on AbstractDelegatingPicoContainer 861 if (child instanceof AbstractDelegatingPicoContainer) { 862 AbstractDelegatingPicoContainer delegateChild = (AbstractDelegatingPicoContainer) child; 863 while(delegateChild != null) { 864 PicoContainer delegateInstance = delegateChild.getDelegate(); 865 if (this == delegateInstance) { 866 throw new IllegalArgumentException(String.format(MESSAGE,this,child)); 867 } 868 if (delegateInstance instanceof AbstractDelegatingPicoContainer) { 869 delegateChild = (AbstractDelegatingPicoContainer) delegateInstance; 870 } else { 871 delegateChild = null; 872 } 873 } 874 } 875 876 } 877 878 public MutablePicoContainer addChildContainer(final PicoContainer child) { 879 checkCircularChildDependencies(child); 880 if (children.add(child)) { 881 // TODO Should only be added if child container has also be started 882 if (lifecycleState.isStarted()) { 883 childrenStarted.add(new WeakReference<PicoContainer>(child)); 884 } 885 } 886 return this; 887 } 888 889 public boolean removeChildContainer(final PicoContainer child) { 890 final boolean result = children.remove(child); 891 WeakReference<PicoContainer> foundRef = null; 892 for (WeakReference<PicoContainer> eachChild : childrenStarted) { 893 PicoContainer ref = eachChild.get(); 894 if (ref.equals(child)) { 895 foundRef = eachChild; 896 break; 897 } 898 } 899 900 if (foundRef != null) { 901 childrenStarted.remove(foundRef); 902 } 903 904 return result; 905 } 906 907 public MutablePicoContainer change(final Properties... properties) { 908 for (Properties c : properties) { 909 Enumeration<String> e = (Enumeration<String>) c.propertyNames(); 910 while (e.hasMoreElements()) { 911 String s = e.nextElement(); 912 containerProperties.setProperty(s,c.getProperty(s)); 913 } 914 } 915 return this; 916 } 917 918 public MutablePicoContainer as(final Properties... properties) { 919 return new AsPropertiesPicoContainer(properties); 920 } 921 922 public void accept(final PicoVisitor visitor) { 923 924 //TODO Pico 3 : change accept signatures to allow abort at any point in the traversal. 925 boolean shouldContinue = visitor.visitContainer(this); 926 if (!shouldContinue) { 927 return; 928 } 929 930 931 componentFactory.accept(visitor); // will cascade through behaviors 932 final List<ComponentAdapter<?>> componentAdapters = new ArrayList<ComponentAdapter<?>>(getComponentAdapters()); 933 for (ComponentAdapter<?> componentAdapter : componentAdapters) { 934 componentAdapter.accept(visitor); 935 } 936 final List<PicoContainer> allChildren = new ArrayList<PicoContainer>(children); 937 for (PicoContainer child : allChildren) { 938 child.accept(visitor); 939 } 940 } 941 942 /** 943 * Changes monitor in the ComponentFactory, the component adapters 944 * and the child containers, if these support a ComponentMonitorStrategy. 945 * {@inheritDoc} 946 */ 947 public void changeMonitor(final ComponentMonitor monitor) { 948 this.componentMonitor = monitor; 949 if (lifecycleStrategy instanceof ComponentMonitorStrategy) { 950 ((ComponentMonitorStrategy)lifecycleStrategy).changeMonitor(monitor); 951 } 952 for (ComponentAdapter<?> adapter : getModifiableComponentAdapterList()) { 953 if (adapter instanceof ComponentMonitorStrategy) { 954 ((ComponentMonitorStrategy)adapter).changeMonitor(monitor); 955 } 956 } 957 for (PicoContainer child : children) { 958 if (child instanceof ComponentMonitorStrategy) { 959 ((ComponentMonitorStrategy)child).changeMonitor(monitor); 960 } 961 } 962 } 963 964 /** 965 * Returns the first current monitor found in the ComponentFactory, the component adapters 966 * and the child containers, if these support a ComponentMonitorStrategy. 967 * {@inheritDoc} 968 * 969 * @throws PicoCompositionException if no component monitor is found in container or its children 970 */ 971 public ComponentMonitor currentMonitor() { 972 return componentMonitor; 973 } 974 975 /** 976 * {@inheritDoc} 977 * Loops over all component adapters and invokes 978 * start(PicoContainer) method on the ones which are LifecycleManagers 979 */ 980 private void startAdapters() { 981 Collection<ComponentAdapter<?>> adapters = getComponentAdapters(); 982 for (ComponentAdapter<?> adapter : adapters) { 983 addAdapterIfStartable(adapter); 984 } 985 adapters = getOrderedComponentAdapters(); 986 // clone the adapters 987 List<ComponentAdapter<?>> adaptersClone = new ArrayList<ComponentAdapter<?>>(adapters); 988 for (final ComponentAdapter<?> adapter : adaptersClone) { 989 potentiallyStartAdapter(adapter); 990 } 991 } 992 993 protected void potentiallyStartAdapter(ComponentAdapter<?> adapter) { 994 if (adapter instanceof ComponentLifecycle) { 995 if (!lifecycleStrategy.isLazy(adapter)) { 996 ((ComponentLifecycle<?>)adapter).start(this); 997 } 998 } 999 } 1000 1001 private void addAdapterIfStartable(ComponentAdapter<?> adapter) { 1002 if (adapter instanceof ComponentLifecycle) { 1003 ComponentLifecycle<?> componentLifecycle = (ComponentLifecycle<?>)adapter; 1004 if (componentLifecycle.componentHasLifecycle()) { 1005 // create an instance, it will be added to the ordered CA list 1006 instantiateComponentAsIsStartable(adapter); 1007 addOrderedComponentAdapter(adapter); 1008 } 1009 } 1010 } 1011 1012 protected void instantiateComponentAsIsStartable(ComponentAdapter<?> adapter) { 1013 if (!lifecycleStrategy.isLazy(adapter)) { 1014 adapter.getComponentInstance(DefaultPicoContainer.this, ComponentAdapter.NOTHING.class); 1015 } 1016 } 1017 1018 /** 1019 * {@inheritDoc} 1020 * Loops over started component adapters (in inverse order) and invokes 1021 * stop(PicoContainer) method on the ones which are LifecycleManagers 1022 */ 1023 private void stopAdapters() { 1024 for (int i = getOrderedComponentAdapters().size() - 1; 0 <= i; i--) { 1025 ComponentAdapter<?> adapter = getOrderedComponentAdapters().get(i); 1026 if (adapter instanceof ComponentLifecycle) { 1027 ComponentLifecycle<?> componentLifecycle = (ComponentLifecycle<?>)adapter; 1028 if (componentLifecycle.componentHasLifecycle() && componentLifecycle.isStarted()) { 1029 componentLifecycle.stop(DefaultPicoContainer.this); 1030 } 1031 } 1032 } 1033 } 1034 1035 /** 1036 * {@inheritDoc} 1037 * Loops over all component adapters (in inverse order) and invokes 1038 * dispose(PicoContainer) method on the ones which are LifecycleManagers 1039 */ 1040 private void disposeAdapters() { 1041 for (int i = getOrderedComponentAdapters().size() - 1; 0 <= i; i--) { 1042 ComponentAdapter<?> adapter = getOrderedComponentAdapters().get(i); 1043 if (adapter instanceof ComponentLifecycle) { 1044 ComponentLifecycle<?> componentLifecycle = (ComponentLifecycle<?>)adapter; 1045 componentLifecycle.dispose(DefaultPicoContainer.this); 1046 } 1047 } 1048 } 1049 1050 1051 1052 /** 1053 * @return the orderedComponentAdapters 1054 */ 1055 protected List<ComponentAdapter<?>> getOrderedComponentAdapters() { 1056 return orderedComponentAdapters; 1057 } 1058 1059 /** 1060 * @return the componentKeyToAdapterCache 1061 */ 1062 protected Map<Object, ComponentAdapter<?>> getComponentKeyToAdapterCache() { 1063 return componentKeyToAdapterCache; 1064 } 1065 1066 /** 1067 * @return the componentAdapters 1068 */ 1069 protected List<ComponentAdapter<?>> getModifiableComponentAdapterList() { 1070 return componentAdapters; 1071 } 1072 1073 public void setName(String name) { 1074 this.name = name; 1075 } 1076 1077 @Override 1078 public String toString() { 1079 return String.format("%s:%d<%s", (name != null ? name : super.toString()), this.componentAdapters.size(), (parent != null ? parent.toString() : "|")); 1080 } 1081 1082 /** 1083 * If this container has a set of converters, then return it. 1084 * If it does not, and the parent (or their parent ..) does, use that 1085 * If they do not, return a NullObject implementation (ConversNothing) 1086 * @return the converters 1087 */ 1088 public synchronized Converters getConverters() { 1089 if (converters == null) { 1090 if (parent == null || (parent instanceof Converting && ((Converting) parent).getConverters() instanceof ConvertsNothing)) { 1091 converters = new BuiltInConverters(); 1092 } else { 1093 return ((Converting) parent).getConverters(); 1094 } 1095 } 1096 return converters; 1097 } 1098 1099 private class AsPropertiesPicoContainer extends AbstractDelegatingMutablePicoContainer { 1100 1101 private final Properties properties; 1102 1103 public AsPropertiesPicoContainer(final Properties... props) { 1104 super(DefaultPicoContainer.this); 1105 properties = (Properties) containerProperties.clone(); 1106 for (Properties c : props) { 1107 Enumeration<?> e = c.propertyNames(); 1108 while (e.hasMoreElements()) { 1109 String s = (String)e.nextElement(); 1110 properties.setProperty(s,c.getProperty(s)); 1111 } 1112 } 1113 } 1114 1115 @Override 1116 @SuppressWarnings("unused") 1117 public MutablePicoContainer as( Properties... props) { 1118 throw new PicoCompositionException("Syntax 'as(FOO).as(BAR)' not allowed, do 'as(FOO, BAR)' instead"); 1119 } 1120 1121 @Override 1122 public MutablePicoContainer makeChildContainer() { 1123 return getDelegate().makeChildContainer(); 1124 } 1125 1126 @Override 1127 public MutablePicoContainer addComponent(final Object componentKey, 1128 final Object componentImplementationOrInstance, 1129 final Parameter... parameters) throws PicoCompositionException { 1130 return DefaultPicoContainer.this.addComponent(componentKey, 1131 componentImplementationOrInstance, 1132 properties, 1133 parameters); 1134 } 1135 1136 @Override 1137 public MutablePicoContainer addComponent(final Object implOrInstance) throws PicoCompositionException { 1138 return DefaultPicoContainer.this.addComponent(implOrInstance, properties); 1139 } 1140 1141 @Override 1142 public MutablePicoContainer addAdapter(final ComponentAdapter<?> componentAdapter) throws PicoCompositionException { 1143 return DefaultPicoContainer.this.addAdapter(componentAdapter, properties); 1144 } 1145 } 1146 1147 private static class IntoThreadLocal extends ThreadLocal<Type> implements Serializable { 1148 protected Type initialValue() { 1149 return ComponentAdapter.NOTHING.class; 1150 } 1151 } 1152 }