You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

ComponentContainer.java 10KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333
  1. /*
  2. * SonarQube
  3. * Copyright (C) 2009-2018 SonarSource SA
  4. * mailto:info AT sonarsource DOT com
  5. *
  6. * This program is free software; you can redistribute it and/or
  7. * modify it under the terms of the GNU Lesser General Public
  8. * License as published by the Free Software Foundation; either
  9. * version 3 of the License, or (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  14. * Lesser General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU Lesser General Public License
  17. * along with this program; if not, write to the Free Software Foundation,
  18. * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  19. */
  20. package org.sonar.core.platform;
  21. import com.google.common.collect.Iterables;
  22. import com.google.common.collect.Lists;
  23. import java.lang.annotation.Annotation;
  24. import java.util.ArrayList;
  25. import java.util.Iterator;
  26. import java.util.List;
  27. import javax.annotation.Nullable;
  28. import org.picocontainer.Characteristics;
  29. import org.picocontainer.ComponentAdapter;
  30. import org.picocontainer.ComponentFactory;
  31. import org.picocontainer.ComponentMonitor;
  32. import org.picocontainer.DefaultPicoContainer;
  33. import org.picocontainer.LifecycleStrategy;
  34. import org.picocontainer.MutablePicoContainer;
  35. import org.picocontainer.PicoContainer;
  36. import org.picocontainer.behaviors.OptInCaching;
  37. import org.picocontainer.monitors.NullComponentMonitor;
  38. import org.sonar.api.batch.ScannerSide;
  39. import org.sonar.api.ce.ComputeEngineSide;
  40. import org.sonar.api.config.PropertyDefinitions;
  41. import org.sonar.api.server.ServerSide;
  42. import static com.google.common.collect.ImmutableList.copyOf;
  43. import static java.util.Objects.requireNonNull;
  44. import static java.util.Optional.ofNullable;
  45. @ScannerSide
  46. @ServerSide
  47. @ComputeEngineSide
  48. public class ComponentContainer implements ContainerPopulator.Container {
  49. public static final int COMPONENTS_IN_EMPTY_COMPONENT_CONTAINER = 2;
  50. private static final class ExtendedDefaultPicoContainer extends DefaultPicoContainer {
  51. private ExtendedDefaultPicoContainer(ComponentFactory componentFactory, LifecycleStrategy lifecycleStrategy, PicoContainer parent,
  52. ComponentMonitor componentMonitor) {
  53. super(componentFactory, lifecycleStrategy, parent, componentMonitor);
  54. }
  55. @Override
  56. public Object getComponent(final Object componentKeyOrType, final Class<? extends Annotation> annotation) {
  57. try {
  58. return super.getComponent(componentKeyOrType, annotation);
  59. } catch (Throwable t) {
  60. throw new IllegalStateException("Unable to load component " + componentKeyOrType, t);
  61. }
  62. }
  63. @Override
  64. public MutablePicoContainer makeChildContainer() {
  65. DefaultPicoContainer pc = new ExtendedDefaultPicoContainer(componentFactory, lifecycleStrategy, this, componentMonitor);
  66. addChildContainer(pc);
  67. return pc;
  68. }
  69. }
  70. private ComponentContainer parent;
  71. private final List<ComponentContainer> children = new ArrayList<>();
  72. private MutablePicoContainer pico;
  73. private PropertyDefinitions propertyDefinitions;
  74. private ComponentKeys componentKeys;
  75. /**
  76. * Create root container
  77. */
  78. public ComponentContainer() {
  79. this(createPicoContainer());
  80. }
  81. protected ComponentContainer(MutablePicoContainer picoContainer) {
  82. this(picoContainer, new PropertyDefinitions());
  83. }
  84. protected ComponentContainer(MutablePicoContainer picoContainer, PropertyDefinitions propertyDefinitions) {
  85. requireNonNull(propertyDefinitions, "PropertyDefinitions can not be null");
  86. this.parent = null;
  87. this.pico = picoContainer;
  88. this.componentKeys = new ComponentKeys();
  89. this.propertyDefinitions = propertyDefinitions;
  90. addSingleton(propertyDefinitions);
  91. addSingleton(this);
  92. }
  93. /**
  94. * Create child container
  95. */
  96. protected ComponentContainer(ComponentContainer parent) {
  97. this.parent = parent;
  98. this.pico = parent.pico.makeChildContainer();
  99. this.parent.children.add(this);
  100. this.propertyDefinitions = parent.propertyDefinitions;
  101. this.componentKeys = new ComponentKeys();
  102. addSingleton(this);
  103. }
  104. protected void setParent(ComponentContainer parent) {
  105. this.parent = parent;
  106. }
  107. public void execute() {
  108. try {
  109. startComponents();
  110. } finally {
  111. stopComponents();
  112. }
  113. }
  114. /**
  115. * This method MUST NOT be renamed start() because the container is registered itself in picocontainer. Starting
  116. * a component twice is not authorized.
  117. */
  118. public ComponentContainer startComponents() {
  119. try {
  120. doBeforeStart();
  121. pico.start();
  122. doAfterStart();
  123. return this;
  124. } catch (Exception e) {
  125. throw PicoUtils.propagate(e);
  126. }
  127. }
  128. /**
  129. * This method aims to be overridden
  130. */
  131. protected void doBeforeStart() {
  132. // nothing
  133. }
  134. /**
  135. * This method aims to be overridden
  136. */
  137. protected void doAfterStart() {
  138. // nothing
  139. }
  140. /**
  141. * This method MUST NOT be renamed stop() because the container is registered itself in picocontainer. Starting
  142. * a component twice is not authorized.
  143. */
  144. public ComponentContainer stopComponents() {
  145. try {
  146. stopChildren();
  147. if (pico.getLifecycleState().isStarted()) {
  148. pico.stop();
  149. }
  150. pico.dispose();
  151. } finally {
  152. if (parent != null) {
  153. parent.removeChild(this);
  154. }
  155. }
  156. return this;
  157. }
  158. private void stopChildren() {
  159. // loop over a copy of list of children in reverse order, both to stop last added child first and because children
  160. // remove themselves from the list of children of their parent (ie. changing this.children)
  161. Lists.reverse(new ArrayList<>(this.children))
  162. .forEach(ComponentContainer::stopComponents);
  163. }
  164. /**
  165. * @since 3.5
  166. */
  167. @Override
  168. public ComponentContainer add(Object... objects) {
  169. for (Object object : objects) {
  170. if (object instanceof ComponentAdapter) {
  171. addPicoAdapter((ComponentAdapter) object);
  172. } else if (object instanceof Iterable) {
  173. add(Iterables.toArray((Iterable) object, Object.class));
  174. } else {
  175. addSingleton(object);
  176. }
  177. }
  178. return this;
  179. }
  180. public void addIfMissing(Object object, Class<?> objectType) {
  181. if (getComponentByType(objectType) == null) {
  182. add(object);
  183. }
  184. }
  185. @Override
  186. public ComponentContainer addSingletons(Iterable<?> components) {
  187. for (Object component : components) {
  188. addSingleton(component);
  189. }
  190. return this;
  191. }
  192. public ComponentContainer addSingleton(Object component) {
  193. return addComponent(component, true);
  194. }
  195. /**
  196. * @param singleton return always the same instance if true, else a new instance
  197. * is returned each time the component is requested
  198. */
  199. public ComponentContainer addComponent(Object component, boolean singleton) {
  200. Object key = componentKeys.of(component);
  201. if (component instanceof ComponentAdapter) {
  202. pico.addAdapter((ComponentAdapter) component);
  203. } else {
  204. try {
  205. pico.as(singleton ? Characteristics.CACHE : Characteristics.NO_CACHE).addComponent(key, component);
  206. } catch (Throwable t) {
  207. throw new IllegalStateException("Unable to register component " + getName(component), t);
  208. }
  209. declareExtension("", component);
  210. }
  211. return this;
  212. }
  213. public ComponentContainer addExtension(@Nullable PluginInfo pluginInfo, Object extension) {
  214. Object key = componentKeys.of(extension);
  215. try {
  216. pico.as(Characteristics.CACHE).addComponent(key, extension);
  217. } catch (Throwable t) {
  218. throw new IllegalStateException("Unable to register extension " + getName(extension) + (pluginInfo != null ? (" from plugin '" + pluginInfo.getKey() + "'") : ""), t);
  219. }
  220. declareExtension(pluginInfo, extension);
  221. return this;
  222. }
  223. public ComponentContainer addExtension(@Nullable String defaultCategory, Object extension) {
  224. Object key = componentKeys.of(extension);
  225. try {
  226. pico.as(Characteristics.CACHE).addComponent(key, extension);
  227. } catch (Throwable t) {
  228. throw new IllegalStateException("Unable to register extension " + getName(extension), t);
  229. }
  230. declareExtension(defaultCategory, extension);
  231. return this;
  232. }
  233. private static String getName(Object extension) {
  234. if (extension instanceof Class) {
  235. return ((Class<?>) extension).getName();
  236. }
  237. return getName(extension.getClass());
  238. }
  239. public void declareExtension(@Nullable PluginInfo pluginInfo, Object extension) {
  240. declareExtension(pluginInfo != null ? pluginInfo.getName() : "", extension);
  241. }
  242. public void declareExtension(@Nullable String defaultCategory, Object extension) {
  243. propertyDefinitions.addComponent(extension, ofNullable(defaultCategory).orElse(""));
  244. }
  245. public ComponentContainer addPicoAdapter(ComponentAdapter<?> adapter) {
  246. pico.addAdapter(adapter);
  247. return this;
  248. }
  249. @Override
  250. public <T> T getComponentByType(Class<T> type) {
  251. return pico.getComponent(type);
  252. }
  253. public Object getComponentByKey(Object key) {
  254. return pico.getComponent(key);
  255. }
  256. @Override
  257. public <T> List<T> getComponentsByType(Class<T> tClass) {
  258. return pico.getComponents(tClass);
  259. }
  260. public ComponentContainer removeChild(ComponentContainer childToBeRemoved) {
  261. requireNonNull(childToBeRemoved);
  262. Iterator<ComponentContainer> childrenIterator = children.iterator();
  263. while (childrenIterator.hasNext()) {
  264. ComponentContainer child = childrenIterator.next();
  265. if (child == childToBeRemoved) {
  266. if (pico.removeChildContainer(child.pico)) {
  267. childrenIterator.remove();
  268. }
  269. break;
  270. }
  271. }
  272. return this;
  273. }
  274. public ComponentContainer createChild() {
  275. return new ComponentContainer(this);
  276. }
  277. public static MutablePicoContainer createPicoContainer() {
  278. NullComponentMonitor componentMonitor = new NullComponentMonitor();
  279. return new ExtendedDefaultPicoContainer(new OptInCaching(), new StopSafeReflectionLifecycleStrategy(componentMonitor), null, componentMonitor);
  280. }
  281. public ComponentContainer getParent() {
  282. return parent;
  283. }
  284. public List<ComponentContainer> getChildren() {
  285. return copyOf(children);
  286. }
  287. public MutablePicoContainer getPicoContainer() {
  288. return pico;
  289. }
  290. public int size() {
  291. return pico.getComponentAdapters().size();
  292. }
  293. }