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 }