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 */
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.List;
037
038 /**
039 * @since 2.12
040 */
041 public 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
103 protected void doBeforeStart() {
104
105 }
106
107 /**
108 * This method aims to be overridden
109 */
110 protected void doAfterStart() {
111
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
127 } catch (RuntimeException e) {
128 if (!swallowException) {
129 throw PicoUtils.propagate(e);
130 }
131 } finally {
132 removeChild();
133 if (parent != null) {
134 parent.removeChild();
135 }
136 }
137 return this;
138 }
139
140 /**
141 * @since 3.5
142 */
143 public ComponentContainer add(Object... objects) {
144 for (Object object : objects) {
145 if (object instanceof ComponentAdapter) {
146 addPicoAdapter((ComponentAdapter) object);
147 } else if (object instanceof Iterable) {
148 add(Iterables.toArray((Iterable) object, Object.class));
149 } else {
150 addSingleton(object);
151 }
152 }
153 return this;
154 }
155
156 public ComponentContainer addSingleton(Object component) {
157 return addComponent(component, true);
158 }
159
160 /**
161 * @param singleton return always the same instance if true, else a new instance
162 * is returned each time the component is requested
163 */
164 public ComponentContainer addComponent(Object component, boolean singleton) {
165 Object key = componentKeys.of(component);
166 pico.as(singleton ? Characteristics.CACHE : Characteristics.NO_CACHE).addComponent(key, component);
167 declareExtension(null, component);
168 return this;
169 }
170
171 public ComponentContainer addExtension(@Nullable PluginMetadata plugin, Object extension) {
172 Object key = componentKeys.of(extension);
173 pico.as(Characteristics.CACHE).addComponent(key, extension);
174 declareExtension(plugin, extension);
175 return this;
176 }
177
178 public void declareExtension(@Nullable PluginMetadata plugin, Object extension) {
179 propertyDefinitions.addComponent(extension, plugin != null ? plugin.getName() : "");
180 }
181
182 public ComponentContainer addPicoAdapter(ComponentAdapter adapter) {
183 pico.addAdapter(adapter);
184 return this;
185 }
186
187 public <T> T getComponentByType(Class<T> tClass) {
188 return pico.getComponent(tClass);
189 }
190
191 public Object getComponentByKey(Object key) {
192 return pico.getComponent(key);
193 }
194
195 public <T> List<T> getComponentsByType(Class<T> tClass) {
196 return pico.getComponents(tClass);
197 }
198
199 public ComponentContainer removeChild() {
200 if (child != null) {
201 pico.removeChildContainer(child.pico);
202 child = null;
203 }
204 return this;
205 }
206
207 public ComponentContainer createChild() {
208 return new ComponentContainer(this);
209 }
210
211 static MutablePicoContainer createPicoContainer() {
212 ReflectionLifecycleStrategy lifecycleStrategy = new ReflectionLifecycleStrategy(new NullComponentMonitor(), "start", "stop", "dispose");
213 return new DefaultPicoContainer(new OptInCaching(), lifecycleStrategy, null);
214 }
215
216 public ComponentContainer getParent() {
217 return parent;
218 }
219
220 public ComponentContainer getChild() {
221 return child;
222 }
223
224 public MutablePicoContainer getPicoContainer() {
225 return pico;
226 }
227 }