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     */
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.Collection;
037    import java.util.List;
038    
039    /**
040     * @since 2.12
041     */
042    public 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    }