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  ComponentContainer parent, child; // no need for multiple children
044  MutablePicoContainer pico;
045  PropertyDefinitions propertyDefinitions;
046
047  /**
048   * Create root container
049   */
050  public ComponentContainer() {
051    this.parent = null;
052    this.child = null;
053    this.pico = createPicoContainer();
054    propertyDefinitions = new PropertyDefinitions();
055    addSingleton(propertyDefinitions);
056    addSingleton(this);
057  }
058
059  /**
060   * Create child container
061   */
062  protected ComponentContainer(ComponentContainer parent) {
063    this.parent = parent;
064    this.pico = parent.pico.makeChildContainer();
065    this.parent.child = this;
066    this.propertyDefinitions = parent.propertyDefinitions;
067    addSingleton(this);
068  }
069
070  public void execute() {
071    boolean threw = true;
072    try {
073      startComponents();
074      threw = false;
075    } finally {
076      stopComponents(threw);
077    }
078  }
079
080  /**
081   * This method MUST NOT be renamed start() because the container is registered itself in picocontainer. Starting
082   * a component twice is not authorized.
083   */
084  public ComponentContainer startComponents() {
085    try {
086      doBeforeStart();
087      pico.start();
088      doAfterStart();
089      return this;
090    } catch (Exception e) {
091      throw PicoUtils.propagate(e);
092    }
093  }
094
095  /**
096   * This method aims to be overridden
097   */
098
099  protected void doBeforeStart() {
100
101  }
102
103  /**
104   * This method aims to be overridden
105   */
106  protected void doAfterStart() {
107
108  }
109
110  /**
111   * This method MUST NOT be renamed stop() because the container is registered itself in picocontainer. Starting
112   * a component twice is not authorized.
113   */
114  public ComponentContainer stopComponents() {
115    return stopComponents(false);
116  }
117
118  public ComponentContainer stopComponents(boolean swallowException) {
119    try {
120      pico.stop();
121
122
123    } catch (RuntimeException e) {
124      if (!swallowException) {
125        throw PicoUtils.propagate(e);
126      }
127    } finally {
128      removeChild();
129      if (parent != null) {
130        parent.removeChild();
131      }
132    }
133    return this;
134  }
135
136  /**
137   * @since 3.5
138   */
139  public ComponentContainer add(Object... objects) {
140    for (Object object : objects) {
141      if (object instanceof ComponentAdapter) {
142        addPicoAdapter((ComponentAdapter) object);
143      } else if (object instanceof Iterable) {
144        add(Iterables.toArray((Iterable) object, Object.class));
145      } else {
146        addSingleton(object);
147      }
148    }
149    return this;
150  }
151
152  public ComponentContainer addSingleton(Object component) {
153    return addComponent(component, true);
154  }
155
156  /**
157   * @param singleton return always the same instance if true, else a new instance
158   *                  is returned each time the component is requested
159   */
160  public ComponentContainer addComponent(Object component, boolean singleton) {
161    pico.as(singleton ? Characteristics.CACHE : Characteristics.NO_CACHE).addComponent(getComponentKey(component), component);
162    declareExtension(null, component);
163    return this;
164  }
165
166  public ComponentContainer addExtension(@Nullable PluginMetadata plugin, Object extension) {
167    pico.as(Characteristics.CACHE).addComponent(getComponentKey(extension), extension);
168    declareExtension(plugin, extension);
169    return this;
170  }
171
172  public void declareExtension(@Nullable PluginMetadata plugin, Object extension) {
173    propertyDefinitions.addComponent(extension, plugin != null ? plugin.getName() : "");
174  }
175
176  public ComponentContainer addPicoAdapter(ComponentAdapter adapter) {
177    pico.addAdapter(adapter);
178    return this;
179  }
180
181  public <T> T getComponentByType(Class<T> tClass) {
182    return pico.getComponent(tClass);
183  }
184
185  public Object getComponentByKey(Object key) {
186    return pico.getComponent(key);
187  }
188
189  public <T> List<T> getComponentsByType(Class<T> tClass) {
190    return pico.getComponents(tClass);
191  }
192
193  public ComponentContainer removeChild() {
194    if (child != null) {
195      pico.removeChildContainer(child.pico);
196      child = null;
197    }
198    return this;
199  }
200
201  public ComponentContainer createChild() {
202    return new ComponentContainer(this);
203  }
204
205  static MutablePicoContainer createPicoContainer() {
206    ReflectionLifecycleStrategy lifecycleStrategy = new ReflectionLifecycleStrategy(new NullComponentMonitor(), "start", "stop", "dispose");
207    return new DefaultPicoContainer(new OptInCaching(), lifecycleStrategy, null);
208  }
209
210  static Object getComponentKey(Object component) {
211    if (component instanceof Class) {
212      return component;
213    }
214    return new StringBuilder().append(component.getClass().getCanonicalName()).append("-").append(component.toString()).toString();
215  }
216
217  public ComponentContainer getParent() {
218    return parent;
219  }
220
221  public ComponentContainer getChild() {
222    return child;
223  }
224
225  public MutablePicoContainer getPicoContainer() {
226    return pico;
227  }
228}