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
033import javax.annotation.Nullable;
034
035/**
036 * @since 2.12
037 */
038public class ComponentContainer implements BatchComponent, ServerComponent {
039
040  ComponentContainer parent, child; // no need for multiple children
041  MutablePicoContainer pico;
042  PropertyDefinitions propertyDefinitions;
043
044  /**
045   * Create root container
046   */
047  public ComponentContainer() {
048    this.parent = null;
049    this.child = null;
050    this.pico = createPicoContainer();
051    propertyDefinitions = new PropertyDefinitions();
052    addSingleton(propertyDefinitions);
053    addSingleton(this);
054  }
055
056  /**
057   * Create child container
058   */
059  private ComponentContainer(ComponentContainer parent) {
060    this.parent = parent;
061    this.pico = parent.pico.makeChildContainer();
062    this.parent.child = this;
063    this.propertyDefinitions = parent.propertyDefinitions;
064    addSingleton(this);
065  }
066
067  /**
068   * This method MUST NOT be renamed start() because the container is registered itself in picocontainer. Starting
069   * a component twice is not authorized.
070   */
071  public final ComponentContainer startComponents() {
072    pico.start();
073    return this;
074  }
075
076  /**
077   * This method MUST NOT be renamed stop() because the container is registered itself in picocontainer. Starting
078   * a component twice is not authorized.
079   */
080  public final ComponentContainer stopComponents() {
081    pico.stop();
082    return this;
083  }
084
085  public final ComponentContainer addSingleton(Object component) {
086    return addComponent(component, true);
087  }
088
089  /**
090   * @param singleton return always the same instance if true, else a new instance
091   *                  is returned each time the component is requested
092   */
093  public final ComponentContainer addComponent(Object component, boolean singleton) {
094    pico.as(singleton ? Characteristics.CACHE : Characteristics.NO_CACHE).addComponent(getComponentKey(component), component);
095    declareExtension(null, component);
096    return this;
097  }
098
099  public final ComponentContainer addExtension(@Nullable PluginMetadata plugin, Object extension) {
100    pico.as(Characteristics.CACHE).addComponent(getComponentKey(extension), extension);
101    declareExtension(plugin, extension);
102    return this;
103  }
104
105  public final void declareExtension(@Nullable PluginMetadata plugin, Object extension) {
106    propertyDefinitions.addComponent(extension, plugin!=null ? plugin.getName() : "");
107  }
108
109  public final ComponentContainer addPicoAdapter(ComponentAdapter adapter) {
110    pico.addAdapter(adapter);
111    return this;
112  }
113
114  public final <T> T getComponentByType(Class<T> tClass) {
115    return pico.getComponent(tClass);
116  }
117
118  public final Object getComponentByKey(Object key) {
119    return pico.getComponent(key);
120  }
121
122  public final <T> java.util.List<T> getComponentsByType(java.lang.Class<T> tClass) {
123    return pico.getComponents(tClass);
124  }
125
126  public final ComponentContainer removeChild() {
127    if (child != null) {
128      pico.removeChildContainer(child.pico);
129      child = null;
130    }
131    return this;
132  }
133
134  public final ComponentContainer createChild() {
135    return new ComponentContainer(this);
136  }
137
138  static MutablePicoContainer createPicoContainer() {
139    ReflectionLifecycleStrategy lifecycleStrategy = new ReflectionLifecycleStrategy(new NullComponentMonitor(), "start", "stop", "dispose");
140    return new DefaultPicoContainer(new OptInCaching(), lifecycleStrategy, null);
141  }
142
143  static Object getComponentKey(Object component) {
144    if (component instanceof Class) {
145      return component;
146    }
147    return new StringBuilder().append(component.getClass().getCanonicalName()).append("-").append(component.toString()).toString();
148  }
149
150  public ComponentContainer getParent() {
151    return parent;
152  }
153
154  public ComponentContainer getChild() {
155    return child;
156  }
157
158  public MutablePicoContainer getPicoContainer() {
159    return pico;
160  }
161}