001/*
002 * SonarQube, open source software quality management tool.
003 * Copyright (C) 2008-2014 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.Collection;
037import java.util.List;
038
039/**
040 * @since 2.12
041 */
042public class ComponentContainer implements BatchComponent, ServerComponent {
043
044  // no need for multiple children
045  ComponentContainer parent, child;
046  MutablePicoContainer pico;
047  PropertyDefinitions propertyDefinitions;
048  ComponentKeys componentKeys;
049
050  /**
051   * Create root container
052   */
053  public ComponentContainer() {
054    this.parent = null;
055    this.child = null;
056    this.pico = createPicoContainer();
057    this.componentKeys = new ComponentKeys();
058    propertyDefinitions = new PropertyDefinitions();
059    addSingleton(propertyDefinitions);
060    addSingleton(this);
061  }
062
063  /**
064   * Create child container
065   */
066  protected ComponentContainer(ComponentContainer parent) {
067    this.parent = parent;
068    this.pico = parent.pico.makeChildContainer();
069    this.parent.child = this;
070    this.propertyDefinitions = parent.propertyDefinitions;
071    this.componentKeys = new ComponentKeys();
072    addSingleton(this);
073  }
074
075  public void execute() {
076    boolean threw = true;
077    try {
078      startComponents();
079      threw = false;
080    } finally {
081      stopComponents(threw);
082    }
083  }
084
085  /**
086   * This method MUST NOT be renamed start() because the container is registered itself in picocontainer. Starting
087   * a component twice is not authorized.
088   */
089  public ComponentContainer startComponents() {
090    try {
091      doBeforeStart();
092      pico.start();
093      doAfterStart();
094      return this;
095    } catch (Exception e) {
096      throw PicoUtils.propagate(e);
097    }
098  }
099
100  /**
101   * This method aims to be overridden
102   */
103  protected void doBeforeStart() {
104    // nothing
105  }
106
107  /**
108   * This method aims to be overridden
109   */
110  protected void doAfterStart() {
111    // nothing
112  }
113
114  /**
115   * This method MUST NOT be renamed stop() because the container is registered itself in picocontainer. Starting
116   * a component twice is not authorized.
117   */
118  public ComponentContainer stopComponents() {
119    return stopComponents(false);
120  }
121
122  public ComponentContainer stopComponents(boolean swallowException) {
123    try {
124      pico.stop();
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 addSingletons(Collection components) {
156    for (Object component : components) {
157      addSingleton(component);
158    }
159    return this;
160  }
161
162  public ComponentContainer addSingleton(Object component) {
163    return addComponent(component, true);
164  }
165
166  /**
167   * @param singleton return always the same instance if true, else a new instance
168   *                  is returned each time the component is requested
169   */
170  public ComponentContainer addComponent(Object component, boolean singleton) {
171    Object key = componentKeys.of(component);
172    if (component instanceof ComponentAdapter) {
173      pico.addAdapter((ComponentAdapter) component);
174    } else {
175      pico.as(singleton ? Characteristics.CACHE : Characteristics.NO_CACHE).addComponent(key, component);
176      declareExtension(null, component);
177    }
178    return this;
179  }
180
181  public ComponentContainer addExtension(@Nullable PluginMetadata plugin, Object extension) {
182    Object key = componentKeys.of(extension);
183    try {
184      pico.as(Characteristics.CACHE).addComponent(key, extension);
185    } catch (Throwable t) {
186      throw new IllegalStateException("Unable to register extension " + getName(extension), t);
187    }
188    declareExtension(plugin, extension);
189    return this;
190  }
191
192  private String getName(Object extension) {
193    if (extension instanceof Class) {
194      return ((Class) extension).getName();
195    }
196    return getName(extension.getClass());
197  }
198
199  public void declareExtension(@Nullable PluginMetadata plugin, Object extension) {
200    propertyDefinitions.addComponent(extension, plugin != null ? plugin.getName() : "");
201  }
202
203  public ComponentContainer addPicoAdapter(ComponentAdapter adapter) {
204    pico.addAdapter(adapter);
205    return this;
206  }
207
208  public <T> T getComponentByType(Class<T> tClass) {
209    return pico.getComponent(tClass);
210  }
211
212  public Object getComponentByKey(Object key) {
213    return pico.getComponent(key);
214  }
215
216  public <T> List<T> getComponentsByType(Class<T> tClass) {
217    return pico.getComponents(tClass);
218  }
219
220  public ComponentContainer removeChild() {
221    if (child != null) {
222      pico.removeChildContainer(child.pico);
223      child = null;
224    }
225    return this;
226  }
227
228  public ComponentContainer createChild() {
229    return new ComponentContainer(this);
230  }
231
232  static MutablePicoContainer createPicoContainer() {
233    ReflectionLifecycleStrategy lifecycleStrategy = new ReflectionLifecycleStrategy(new NullComponentMonitor(), "start", "stop", "dispose");
234    return new DefaultPicoContainer(new OptInCaching(), lifecycleStrategy, null);
235  }
236
237  public ComponentContainer getParent() {
238    return parent;
239  }
240
241  public ComponentContainer getChild() {
242    return child;
243  }
244
245  public MutablePicoContainer getPicoContainer() {
246    return pico;
247  }
248}