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 */ 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; 035import java.util.Collection; 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 pico.dispose(); 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", "close"); 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}