001/*
002 * Sonar, open source software quality management tool.
003 * Copyright (C) 2008-2012 SonarSource
004 * mailto:contact AT sonarsource DOT com
005 *
006 * Sonar is free software; you can redistribute it and/or
007 * modify it under the terms of the GNU Lesser General Public
008 * License as published by the Free Software Foundation; either
009 * version 3 of the License, or (at your option) any later version.
010 *
011 * Sonar is distributed in the hope that it will be useful,
012 * but WITHOUT ANY WARRANTY; without even the implied warranty of
013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014 * Lesser General Public License for more details.
015 *
016 * You should have received a copy of the GNU Lesser General Public
017 * License along with Sonar; if not, write to the Free Software
018 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02
019 */
020package org.sonar.api.platform;
021
022import org.picocontainer.Characteristics;
023import org.picocontainer.ComponentAdapter;
024import org.picocontainer.DefaultPicoContainer;
025import org.picocontainer.MutablePicoContainer;
026import org.picocontainer.behaviors.OptInCaching;
027import org.picocontainer.lifecycle.ReflectionLifecycleStrategy;
028import org.picocontainer.monitors.NullComponentMonitor;
029import org.sonar.api.BatchComponent;
030import org.sonar.api.ServerComponent;
031import org.sonar.api.config.PropertyDefinitions;
032
033/**
034 * @since 2.12
035 */
036public class ComponentContainer implements BatchComponent, ServerComponent {
037
038  ComponentContainer parent, child; // no need for multiple children
039  MutablePicoContainer pico;
040  PropertyDefinitions propertyDefinitions;
041
042  /**
043   * Create root container
044   */
045  public ComponentContainer() {
046    this.parent = null;
047    this.child = null;
048    this.pico = createPicoContainer();
049    propertyDefinitions = new PropertyDefinitions();
050    addSingleton(propertyDefinitions);
051    addSingleton(this);
052  }
053
054  /**
055   * Create child container
056   */
057  private ComponentContainer(ComponentContainer parent) {
058    this.parent = parent;
059    this.pico = parent.pico.makeChildContainer();
060    this.parent.child = this;
061    this.propertyDefinitions = parent.propertyDefinitions;
062    addSingleton(this);
063  }
064
065  /**
066   * This method MUST NOT be renamed start() because the container is registered itself in picocontainer. Starting
067   * a component twice is not authorized.
068   */
069  public final ComponentContainer startComponents() {
070    pico.start();
071    return this;
072  }
073
074  /**
075   * This method MUST NOT be renamed stop() because the container is registered itself in picocontainer. Starting
076   * a component twice is not authorized.
077   */
078  public final ComponentContainer stopComponents() {
079    pico.stop();
080    return this;
081  }
082
083  public final ComponentContainer addSingleton(Object component) {
084    return addComponent(component, true);
085  }
086
087  /**
088   * @param singleton return always the same instance if true, else a new instance
089   *                  is returned each time the component is requested
090   */
091  public final ComponentContainer addComponent(Object component, boolean singleton) {
092    pico.as(singleton ? Characteristics.CACHE : Characteristics.NO_CACHE).addComponent(getComponentKey(component), component);
093    propertyDefinitions.addComponent(component);
094    return this;
095  }
096
097  public final ComponentContainer addExtension(PluginMetadata plugin, Object extension) {
098    pico.as(Characteristics.CACHE).addComponent(getComponentKey(extension), extension);
099    declareExtension(plugin, extension);
100    return this;
101  }
102
103  public final void declareExtension(PluginMetadata plugin, Object extension) {
104    propertyDefinitions.addComponent(extension, plugin.getName());
105  }
106
107  public final ComponentContainer addPicoAdapter(ComponentAdapter adapter) {
108    pico.addAdapter(adapter);
109    return this;
110  }
111
112  public final <T> T getComponentByType(Class<T> tClass) {
113    return pico.getComponent(tClass);
114  }
115
116  public final Object getComponentByKey(Object key) {
117    return pico.getComponent(key);
118  }
119
120  public final <T> java.util.List<T> getComponentsByType(java.lang.Class<T> tClass) {
121    return pico.getComponents(tClass);
122  }
123
124  public final ComponentContainer removeChild() {
125    if (child != null) {
126      pico.removeChildContainer(child.pico);
127      child = null;
128    }
129    return this;
130  }
131
132  public final ComponentContainer createChild() {
133    return new ComponentContainer(this);
134  }
135
136  static MutablePicoContainer createPicoContainer() {
137    ReflectionLifecycleStrategy lifecycleStrategy = new ReflectionLifecycleStrategy(new NullComponentMonitor(), "start", "stop", "dispose");
138    return new DefaultPicoContainer(new OptInCaching(), lifecycleStrategy, null);
139  }
140
141  static Object getComponentKey(Object component) {
142    if (component instanceof Class) {
143      return component;
144    }
145    return new StringBuilder().append(component.getClass().getCanonicalName()).append("-").append(component.toString()).toString();
146  }
147
148  public ComponentContainer getParent() {
149    return parent;
150  }
151
152  public ComponentContainer getChild() {
153    return child;
154  }
155
156  /**
157   * Warning - do not use. This method exists only for the backward-compatibility due to the suppression
158   * of {@link org.sonar.api.utils.IocContainer}
159   * @return
160   */
161  public MutablePicoContainer getPicoContainer() {
162    return pico;
163  }
164}