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 ComponentContainer parent, child; // no need for multiple children 044 MutablePicoContainer pico; 045 PropertyDefinitions propertyDefinitions; 046 047 /** 048 * Create root container 049 */ 050 public ComponentContainer() { 051 this.parent = null; 052 this.child = null; 053 this.pico = createPicoContainer(); 054 propertyDefinitions = new PropertyDefinitions(); 055 addSingleton(propertyDefinitions); 056 addSingleton(this); 057 } 058 059 /** 060 * Create child container 061 */ 062 protected ComponentContainer(ComponentContainer parent) { 063 this.parent = parent; 064 this.pico = parent.pico.makeChildContainer(); 065 this.parent.child = this; 066 this.propertyDefinitions = parent.propertyDefinitions; 067 addSingleton(this); 068 } 069 070 public void execute() { 071 boolean threw = true; 072 try { 073 startComponents(); 074 threw = false; 075 } finally { 076 stopComponents(threw); 077 } 078 } 079 080 /** 081 * This method MUST NOT be renamed start() because the container is registered itself in picocontainer. Starting 082 * a component twice is not authorized. 083 */ 084 public ComponentContainer startComponents() { 085 try { 086 doBeforeStart(); 087 pico.start(); 088 doAfterStart(); 089 return this; 090 } catch (Exception e) { 091 throw PicoUtils.propagate(e); 092 } 093 } 094 095 /** 096 * This method aims to be overridden 097 */ 098 099 protected void doBeforeStart() { 100 101 } 102 103 /** 104 * This method aims to be overridden 105 */ 106 protected void doAfterStart() { 107 108 } 109 110 /** 111 * This method MUST NOT be renamed stop() because the container is registered itself in picocontainer. Starting 112 * a component twice is not authorized. 113 */ 114 public ComponentContainer stopComponents() { 115 return stopComponents(false); 116 } 117 118 public ComponentContainer stopComponents(boolean swallowException) { 119 try { 120 pico.stop(); 121 122 123 } catch (RuntimeException e) { 124 if (!swallowException) { 125 throw PicoUtils.propagate(e); 126 } 127 } finally { 128 removeChild(); 129 if (parent != null) { 130 parent.removeChild(); 131 } 132 } 133 return this; 134 } 135 136 /** 137 * @since 3.5 138 */ 139 public ComponentContainer add(Object... objects) { 140 for (Object object : objects) { 141 if (object instanceof ComponentAdapter) { 142 addPicoAdapter((ComponentAdapter) object); 143 } else if (object instanceof Iterable) { 144 add(Iterables.toArray((Iterable) object, Object.class)); 145 } else { 146 addSingleton(object); 147 } 148 } 149 return this; 150 } 151 152 public ComponentContainer addSingleton(Object component) { 153 return addComponent(component, true); 154 } 155 156 /** 157 * @param singleton return always the same instance if true, else a new instance 158 * is returned each time the component is requested 159 */ 160 public ComponentContainer addComponent(Object component, boolean singleton) { 161 pico.as(singleton ? Characteristics.CACHE : Characteristics.NO_CACHE).addComponent(getComponentKey(component), component); 162 declareExtension(null, component); 163 return this; 164 } 165 166 public ComponentContainer addExtension(@Nullable PluginMetadata plugin, Object extension) { 167 pico.as(Characteristics.CACHE).addComponent(getComponentKey(extension), extension); 168 declareExtension(plugin, extension); 169 return this; 170 } 171 172 public void declareExtension(@Nullable PluginMetadata plugin, Object extension) { 173 propertyDefinitions.addComponent(extension, plugin != null ? plugin.getName() : ""); 174 } 175 176 public ComponentContainer addPicoAdapter(ComponentAdapter adapter) { 177 pico.addAdapter(adapter); 178 return this; 179 } 180 181 public <T> T getComponentByType(Class<T> tClass) { 182 return pico.getComponent(tClass); 183 } 184 185 public Object getComponentByKey(Object key) { 186 return pico.getComponent(key); 187 } 188 189 public <T> List<T> getComponentsByType(Class<T> tClass) { 190 return pico.getComponents(tClass); 191 } 192 193 public ComponentContainer removeChild() { 194 if (child != null) { 195 pico.removeChildContainer(child.pico); 196 child = null; 197 } 198 return this; 199 } 200 201 public ComponentContainer createChild() { 202 return new ComponentContainer(this); 203 } 204 205 static MutablePicoContainer createPicoContainer() { 206 ReflectionLifecycleStrategy lifecycleStrategy = new ReflectionLifecycleStrategy(new NullComponentMonitor(), "start", "stop", "dispose"); 207 return new DefaultPicoContainer(new OptInCaching(), lifecycleStrategy, null); 208 } 209 210 static Object getComponentKey(Object component) { 211 if (component instanceof Class) { 212 return component; 213 } 214 return new StringBuilder().append(component.getClass().getCanonicalName()).append("-").append(component.toString()).toString(); 215 } 216 217 public ComponentContainer getParent() { 218 return parent; 219 } 220 221 public ComponentContainer getChild() { 222 return child; 223 } 224 225 public MutablePicoContainer getPicoContainer() { 226 return pico; 227 } 228}