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