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 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 }