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     */
020    package org.sonar.api.platform;
021    
022    import com.google.common.collect.Iterables;
023    import org.picocontainer.Characteristics;
024    import org.picocontainer.ComponentAdapter;
025    import org.picocontainer.DefaultPicoContainer;
026    import org.picocontainer.MutablePicoContainer;
027    import org.picocontainer.behaviors.OptInCaching;
028    import org.picocontainer.lifecycle.ReflectionLifecycleStrategy;
029    import org.picocontainer.monitors.NullComponentMonitor;
030    import org.sonar.api.BatchComponent;
031    import org.sonar.api.ServerComponent;
032    import org.sonar.api.config.PropertyDefinitions;
033    
034    import javax.annotation.Nullable;
035    
036    import java.util.List;
037    
038    /**
039     * @since 2.12
040     */
041    public 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    
103      protected void doBeforeStart() {
104    
105      }
106    
107      /**
108       * This method aims to be overridden
109       */
110      protected void doAfterStart() {
111    
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    
127        } catch (RuntimeException e) {
128          if (!swallowException) {
129            throw PicoUtils.propagate(e);
130          }
131        } finally {
132          removeChild();
133          if (parent != null) {
134            parent.removeChild();
135          }
136        }
137        return this;
138      }
139    
140      /**
141       * @since 3.5
142       */
143      public ComponentContainer add(Object... objects) {
144        for (Object object : objects) {
145          if (object instanceof ComponentAdapter) {
146            addPicoAdapter((ComponentAdapter) object);
147          } else if (object instanceof Iterable) {
148            add(Iterables.toArray((Iterable) object, Object.class));
149          } else {
150            addSingleton(object);
151          }
152        }
153        return this;
154      }
155    
156      public ComponentContainer addSingleton(Object component) {
157        return addComponent(component, true);
158      }
159    
160      /**
161       * @param singleton return always the same instance if true, else a new instance
162       *                  is returned each time the component is requested
163       */
164      public ComponentContainer addComponent(Object component, boolean singleton) {
165        Object key = componentKeys.of(component);
166        pico.as(singleton ? Characteristics.CACHE : Characteristics.NO_CACHE).addComponent(key, component);
167        declareExtension(null, component);
168        return this;
169      }
170    
171      public ComponentContainer addExtension(@Nullable PluginMetadata plugin, Object extension) {
172        Object key = componentKeys.of(extension);
173        pico.as(Characteristics.CACHE).addComponent(key, extension);
174        declareExtension(plugin, extension);
175        return this;
176      }
177    
178      public void declareExtension(@Nullable PluginMetadata plugin, Object extension) {
179        propertyDefinitions.addComponent(extension, plugin != null ? plugin.getName() : "");
180      }
181    
182      public ComponentContainer addPicoAdapter(ComponentAdapter adapter) {
183        pico.addAdapter(adapter);
184        return this;
185      }
186    
187      public <T> T getComponentByType(Class<T> tClass) {
188        return pico.getComponent(tClass);
189      }
190    
191      public Object getComponentByKey(Object key) {
192        return pico.getComponent(key);
193      }
194    
195      public <T> List<T> getComponentsByType(Class<T> tClass) {
196        return pico.getComponents(tClass);
197      }
198    
199      public ComponentContainer removeChild() {
200        if (child != null) {
201          pico.removeChildContainer(child.pico);
202          child = null;
203        }
204        return this;
205      }
206    
207      public ComponentContainer createChild() {
208        return new ComponentContainer(this);
209      }
210    
211      static MutablePicoContainer createPicoContainer() {
212        ReflectionLifecycleStrategy lifecycleStrategy = new ReflectionLifecycleStrategy(new NullComponentMonitor(), "start", "stop", "dispose");
213        return new DefaultPicoContainer(new OptInCaching(), lifecycleStrategy, null);
214      }
215    
216      public ComponentContainer getParent() {
217        return parent;
218      }
219    
220      public ComponentContainer getChild() {
221        return child;
222      }
223    
224      public MutablePicoContainer getPicoContainer() {
225        return pico;
226      }
227    }