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 }