001/*
002 * SonarQube, open source software quality management tool.
003 * Copyright (C) 2008-2013 SonarSource
004 * mailto:contact AT sonarsource DOT com
005 *
006 * SonarQube 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 * SonarQube 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 License
017 * along with this program; if not, write to the Free Software Foundation,
018 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
019 */
020package org.sonar.api.platform;
021
022import com.google.common.collect.Iterables;
023import org.picocontainer.Characteristics;
024import org.picocontainer.ComponentAdapter;
025import org.picocontainer.DefaultPicoContainer;
026import org.picocontainer.MutablePicoContainer;
027import org.picocontainer.behaviors.OptInCaching;
028import org.picocontainer.lifecycle.ReflectionLifecycleStrategy;
029import org.picocontainer.monitors.NullComponentMonitor;
030import org.sonar.api.BatchComponent;
031import org.sonar.api.ServerComponent;
032import org.sonar.api.config.PropertyDefinitions;
033
034import javax.annotation.Nullable;
035
036import java.util.List;
037
038/**
039 * @since 2.12
040 */
041public class ComponentContainer implements BatchComponent, ServerComponent {
042
043  // no need for multiple children
044  ComponentContainer parent, child;
045  MutablePicoContainer pico;
046  PropertyDefinitions propertyDefinitions;
047  ComponentKeys componentKeys;
048
049  /**
050   * Create root container
051   */
052  public ComponentContainer() {
053    this.parent = null;
054    this.child = null;
055    this.pico = createPicoContainer();
056    this.componentKeys = new ComponentKeys();
057    propertyDefinitions = new PropertyDefinitions();
058    addSingleton(propertyDefinitions);
059    addSingleton(this);
060  }
061
062  /**
063   * Create child container
064   */
065  protected ComponentContainer(ComponentContainer parent) {
066    this.parent = parent;
067    this.pico = parent.pico.makeChildContainer();
068    this.parent.child = this;
069    this.propertyDefinitions = parent.propertyDefinitions;
070    this.componentKeys = new ComponentKeys();
071    addSingleton(this);
072  }
073
074  public void execute() {
075    boolean threw = true;
076    try {
077      startComponents();
078      threw = false;
079    } finally {
080      stopComponents(threw);
081    }
082  }
083
084  /**
085   * This method MUST NOT be renamed start() because the container is registered itself in picocontainer. Starting
086   * a component twice is not authorized.
087   */
088  public ComponentContainer startComponents() {
089    try {
090      doBeforeStart();
091      pico.start();
092      doAfterStart();
093      return this;
094    } catch (Exception e) {
095      throw PicoUtils.propagate(e);
096    }
097  }
098
099  /**
100   * This method aims to be overridden
101   */
102  protected void doBeforeStart() {
103    // nothing
104  }
105
106  /**
107   * This method aims to be overridden
108   */
109  protected void doAfterStart() {
110    // nothing
111  }
112
113  /**
114   * This method MUST NOT be renamed stop() because the container is registered itself in picocontainer. Starting
115   * a component twice is not authorized.
116   */
117  public ComponentContainer stopComponents() {
118    return stopComponents(false);
119  }
120
121  public ComponentContainer stopComponents(boolean swallowException) {
122    try {
123      pico.stop();
124
125
126    } catch (RuntimeException e) {
127      if (!swallowException) {
128        throw PicoUtils.propagate(e);
129      }
130    } finally {
131      removeChild();
132      if (parent != null) {
133        parent.removeChild();
134      }
135    }
136    return this;
137  }
138
139  /**
140   * @since 3.5
141   */
142  public ComponentContainer add(Object... objects) {
143    for (Object object : objects) {
144      if (object instanceof ComponentAdapter) {
145        addPicoAdapter((ComponentAdapter) object);
146      } else if (object instanceof Iterable) {
147        add(Iterables.toArray((Iterable) object, Object.class));
148      } else {
149        addSingleton(object);
150      }
151    }
152    return this;
153  }
154
155  public ComponentContainer addSingleton(Object component) {
156    return addComponent(component, true);
157  }
158
159  /**
160   * @param singleton return always the same instance if true, else a new instance
161   *                  is returned each time the component is requested
162   */
163  public ComponentContainer addComponent(Object component, boolean singleton) {
164    Object key = componentKeys.of(component);
165    pico.as(singleton ? Characteristics.CACHE : Characteristics.NO_CACHE).addComponent(key, component);
166    declareExtension(null, component);
167    return this;
168  }
169
170  public ComponentContainer addExtension(@Nullable PluginMetadata plugin, Object extension) {
171    Object key = componentKeys.of(extension);
172    pico.as(Characteristics.CACHE).addComponent(key, extension);
173    declareExtension(plugin, extension);
174    return this;
175  }
176
177  public void declareExtension(@Nullable PluginMetadata plugin, Object extension) {
178    propertyDefinitions.addComponent(extension, plugin != null ? plugin.getName() : "");
179  }
180
181  public ComponentContainer addPicoAdapter(ComponentAdapter adapter) {
182    pico.addAdapter(adapter);
183    return this;
184  }
185
186  public <T> T getComponentByType(Class<T> tClass) {
187    return pico.getComponent(tClass);
188  }
189
190  public Object getComponentByKey(Object key) {
191    return pico.getComponent(key);
192  }
193
194  public <T> List<T> getComponentsByType(Class<T> tClass) {
195    return pico.getComponents(tClass);
196  }
197
198  public ComponentContainer removeChild() {
199    if (child != null) {
200      pico.removeChildContainer(child.pico);
201      child = null;
202    }
203    return this;
204  }
205
206  public ComponentContainer createChild() {
207    return new ComponentContainer(this);
208  }
209
210  static MutablePicoContainer createPicoContainer() {
211    ReflectionLifecycleStrategy lifecycleStrategy = new ReflectionLifecycleStrategy(new NullComponentMonitor(), "start", "stop", "dispose");
212    return new DefaultPicoContainer(new OptInCaching(), lifecycleStrategy, null);
213  }
214
215  public ComponentContainer getParent() {
216    return parent;
217  }
218
219  public ComponentContainer getChild() {
220    return child;
221  }
222
223  public MutablePicoContainer getPicoContainer() {
224    return pico;
225  }
226}