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