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 */ 020package org.sonar.api.platform; 021 022import com.google.common.collect.Iterables; 023import org.picocontainer.Characteristics; 024import org.picocontainer.ComponentAdapter; 025import org.picocontainer.DefaultPicoContainer; 026import org.picocontainer.MutablePicoContainer; 027import org.picocontainer.behaviors.OptInCaching; 028import org.picocontainer.lifecycle.ReflectionLifecycleStrategy; 029import org.picocontainer.monitors.NullComponentMonitor; 030import org.sonar.api.BatchComponent; 031import org.sonar.api.ServerComponent; 032import org.sonar.api.config.PropertyDefinitions; 033 034import javax.annotation.Nullable; 035 036import java.util.List; 037 038/** 039 * @since 2.12 040 */ 041public 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 protected void doBeforeStart() { 103 // nothing 104 } 105 106 /** 107 * This method aims to be overridden 108 */ 109 protected void doAfterStart() { 110 // nothing 111 } 112 113 /** 114 * This method MUST NOT be renamed stop() because the container is registered itself in picocontainer. Starting 115 * a component twice is not authorized. 116 */ 117 public ComponentContainer stopComponents() { 118 return stopComponents(false); 119 } 120 121 public ComponentContainer stopComponents(boolean swallowException) { 122 try { 123 pico.stop(); 124 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 addSingleton(Object component) { 156 return addComponent(component, true); 157 } 158 159 /** 160 * @param singleton return always the same instance if true, else a new instance 161 * is returned each time the component is requested 162 */ 163 public ComponentContainer addComponent(Object component, boolean singleton) { 164 Object key = componentKeys.of(component); 165 pico.as(singleton ? Characteristics.CACHE : Characteristics.NO_CACHE).addComponent(key, component); 166 declareExtension(null, component); 167 return this; 168 } 169 170 public ComponentContainer addExtension(@Nullable PluginMetadata plugin, Object extension) { 171 Object key = componentKeys.of(extension); 172 pico.as(Characteristics.CACHE).addComponent(key, extension); 173 declareExtension(plugin, extension); 174 return this; 175 } 176 177 public void declareExtension(@Nullable PluginMetadata plugin, Object extension) { 178 propertyDefinitions.addComponent(extension, plugin != null ? plugin.getName() : ""); 179 } 180 181 public ComponentContainer addPicoAdapter(ComponentAdapter adapter) { 182 pico.addAdapter(adapter); 183 return this; 184 } 185 186 public <T> T getComponentByType(Class<T> tClass) { 187 return pico.getComponent(tClass); 188 } 189 190 public Object getComponentByKey(Object key) { 191 return pico.getComponent(key); 192 } 193 194 public <T> List<T> getComponentsByType(Class<T> tClass) { 195 return pico.getComponents(tClass); 196 } 197 198 public ComponentContainer removeChild() { 199 if (child != null) { 200 pico.removeChildContainer(child.pico); 201 child = null; 202 } 203 return this; 204 } 205 206 public ComponentContainer createChild() { 207 return new ComponentContainer(this); 208 } 209 210 static MutablePicoContainer createPicoContainer() { 211 ReflectionLifecycleStrategy lifecycleStrategy = new ReflectionLifecycleStrategy(new NullComponentMonitor(), "start", "stop", "dispose"); 212 return new DefaultPicoContainer(new OptInCaching(), lifecycleStrategy, null); 213 } 214 215 public ComponentContainer getParent() { 216 return parent; 217 } 218 219 public ComponentContainer getChild() { 220 return child; 221 } 222 223 public MutablePicoContainer getPicoContainer() { 224 return pico; 225 } 226}