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      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    }