001    /*
002     * Sonar, open source software quality management tool.
003     * Copyright (C) 2008-2012 SonarSource
004     * mailto:contact AT sonarsource DOT com
005     *
006     * Sonar 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     * Sonar 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
017     * License along with Sonar; if not, write to the Free Software
018     * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02
019     */
020    package org.sonar.api.platform;
021    
022    import org.picocontainer.Characteristics;
023    import org.picocontainer.ComponentAdapter;
024    import org.picocontainer.DefaultPicoContainer;
025    import org.picocontainer.MutablePicoContainer;
026    import org.picocontainer.behaviors.OptInCaching;
027    import org.picocontainer.lifecycle.ReflectionLifecycleStrategy;
028    import org.picocontainer.monitors.NullComponentMonitor;
029    import org.sonar.api.BatchComponent;
030    import org.sonar.api.ServerComponent;
031    import org.sonar.api.config.PropertyDefinitions;
032    
033    import javax.annotation.Nullable;
034    
035    /**
036     * @since 2.12
037     */
038    public class ComponentContainer implements BatchComponent, ServerComponent {
039    
040      ComponentContainer parent, child; // no need for multiple children
041      MutablePicoContainer pico;
042      PropertyDefinitions propertyDefinitions;
043    
044      /**
045       * Create root container
046       */
047      public ComponentContainer() {
048        this.parent = null;
049        this.child = null;
050        this.pico = createPicoContainer();
051        propertyDefinitions = new PropertyDefinitions();
052        addSingleton(propertyDefinitions);
053        addSingleton(this);
054      }
055    
056      /**
057       * Create child container
058       */
059      private ComponentContainer(ComponentContainer parent) {
060        this.parent = parent;
061        this.pico = parent.pico.makeChildContainer();
062        this.parent.child = this;
063        this.propertyDefinitions = parent.propertyDefinitions;
064        addSingleton(this);
065      }
066    
067      /**
068       * This method MUST NOT be renamed start() because the container is registered itself in picocontainer. Starting
069       * a component twice is not authorized.
070       */
071      public final ComponentContainer startComponents() {
072        pico.start();
073        return this;
074      }
075    
076      /**
077       * This method MUST NOT be renamed stop() because the container is registered itself in picocontainer. Starting
078       * a component twice is not authorized.
079       */
080      public final ComponentContainer stopComponents() {
081        pico.stop();
082        return this;
083      }
084    
085      public final ComponentContainer addSingleton(Object component) {
086        return addComponent(component, true);
087      }
088    
089      /**
090       * @param singleton return always the same instance if true, else a new instance
091       *                  is returned each time the component is requested
092       */
093      public final ComponentContainer addComponent(Object component, boolean singleton) {
094        pico.as(singleton ? Characteristics.CACHE : Characteristics.NO_CACHE).addComponent(getComponentKey(component), component);
095        declareExtension(null, component);
096        return this;
097      }
098    
099      public final ComponentContainer addExtension(@Nullable PluginMetadata plugin, Object extension) {
100        pico.as(Characteristics.CACHE).addComponent(getComponentKey(extension), extension);
101        declareExtension(plugin, extension);
102        return this;
103      }
104    
105      public final void declareExtension(@Nullable PluginMetadata plugin, Object extension) {
106        propertyDefinitions.addComponent(extension, plugin!=null ? plugin.getName() : "");
107      }
108    
109      public final ComponentContainer addPicoAdapter(ComponentAdapter adapter) {
110        pico.addAdapter(adapter);
111        return this;
112      }
113    
114      public final <T> T getComponentByType(Class<T> tClass) {
115        return pico.getComponent(tClass);
116      }
117    
118      public final Object getComponentByKey(Object key) {
119        return pico.getComponent(key);
120      }
121    
122      public final <T> java.util.List<T> getComponentsByType(java.lang.Class<T> tClass) {
123        return pico.getComponents(tClass);
124      }
125    
126      public final ComponentContainer removeChild() {
127        if (child != null) {
128          pico.removeChildContainer(child.pico);
129          child = null;
130        }
131        return this;
132      }
133    
134      public final ComponentContainer createChild() {
135        return new ComponentContainer(this);
136      }
137    
138      static MutablePicoContainer createPicoContainer() {
139        ReflectionLifecycleStrategy lifecycleStrategy = new ReflectionLifecycleStrategy(new NullComponentMonitor(), "start", "stop", "dispose");
140        return new DefaultPicoContainer(new OptInCaching(), lifecycleStrategy, null);
141      }
142    
143      static Object getComponentKey(Object component) {
144        if (component instanceof Class) {
145          return component;
146        }
147        return new StringBuilder().append(component.getClass().getCanonicalName()).append("-").append(component.toString()).toString();
148      }
149    
150      public ComponentContainer getParent() {
151        return parent;
152      }
153    
154      public ComponentContainer getChild() {
155        return child;
156      }
157    
158      public MutablePicoContainer getPicoContainer() {
159        return pico;
160      }
161    }