aboutsummaryrefslogtreecommitdiffstats
path: root/sonar-core/src/main
diff options
context:
space:
mode:
authorDuarte Meneses <duarte.meneses@sonarsource.com>2022-02-01 15:16:25 -0600
committersonartech <sonartech@sonarsource.com>2022-02-22 20:02:46 +0000
commit60c1a4038e041a342dda9810e6fd761d66b01bdb (patch)
tree0e76b4252e4d7d257cf4ddcb6f081996bb1e03ab /sonar-core/src/main
parent9694d4113bf401b84e86e0223dbea8f5339388d8 (diff)
downloadsonarqube-60c1a4038e041a342dda9810e6fd761d66b01bdb.tar.gz
sonarqube-60c1a4038e041a342dda9810e6fd761d66b01bdb.zip
SONAR-15994 Migrate Sonarqube IOC framework from Pico to Spring
Diffstat (limited to 'sonar-core/src/main')
-rw-r--r--sonar-core/src/main/java/org/sonar/core/extension/CoreExtensionsInstaller.java3
-rw-r--r--sonar-core/src/main/java/org/sonar/core/i18n/DefaultI18n.java2
-rw-r--r--sonar-core/src/main/java/org/sonar/core/language/LanguagesProvider.java38
-rw-r--r--sonar-core/src/main/java/org/sonar/core/platform/ClassDerivedBeanDefinition.java64
-rw-r--r--sonar-core/src/main/java/org/sonar/core/platform/ComponentContainer.java339
-rw-r--r--sonar-core/src/main/java/org/sonar/core/platform/Container.java5
-rw-r--r--sonar-core/src/main/java/org/sonar/core/platform/LazyStrategy.java30
-rw-r--r--sonar-core/src/main/java/org/sonar/core/platform/LazyUnlessStartableStrategy.java (renamed from sonar-core/src/main/java/org/sonar/core/platform/PicoUtils.java)29
-rw-r--r--sonar-core/src/main/java/org/sonar/core/platform/ListContainer.java95
-rw-r--r--sonar-core/src/main/java/org/sonar/core/platform/Module.java8
-rw-r--r--sonar-core/src/main/java/org/sonar/core/platform/PriorityBeanFactory.java138
-rw-r--r--sonar-core/src/main/java/org/sonar/core/platform/SpringComponentContainer.java260
-rw-r--r--sonar-core/src/main/java/org/sonar/core/platform/SpringInitStrategy.java39
-rw-r--r--sonar-core/src/main/java/org/sonar/core/platform/StartableBeanPostProcessor.java55
-rw-r--r--sonar-core/src/main/java/org/sonar/core/platform/StartableCloseableSafeLifecyleStrategy.java105
-rw-r--r--sonar-core/src/main/java/org/sonar/core/platform/StartableContainer.java24
-rw-r--r--sonar-core/src/main/java/org/sonar/core/util/UuidFactoryImpl.java2
17 files changed, 759 insertions, 477 deletions
diff --git a/sonar-core/src/main/java/org/sonar/core/extension/CoreExtensionsInstaller.java b/sonar-core/src/main/java/org/sonar/core/extension/CoreExtensionsInstaller.java
index bae7811e160..cc436fbc0d4 100644
--- a/sonar-core/src/main/java/org/sonar/core/extension/CoreExtensionsInstaller.java
+++ b/sonar-core/src/main/java/org/sonar/core/extension/CoreExtensionsInstaller.java
@@ -79,8 +79,7 @@ public abstract class CoreExtensionsInstaller {
}
}
- private void addDeclaredExtensions(ExtensionContainer container, Predicate<Object> extensionFilter,
- Predicate<Object> additionalSideFilter, CoreExtension coreExtension) {
+ private void addDeclaredExtensions(ExtensionContainer container, Predicate<Object> extensionFilter, Predicate<Object> additionalSideFilter, CoreExtension coreExtension) {
ContextImpl context = new ContextImpl(container, extensionFilter, additionalSideFilter, coreExtension.getName());
coreExtension.load(context);
}
diff --git a/sonar-core/src/main/java/org/sonar/core/i18n/DefaultI18n.java b/sonar-core/src/main/java/org/sonar/core/i18n/DefaultI18n.java
index ad64a703f3f..cfa5ac20de9 100644
--- a/sonar-core/src/main/java/org/sonar/core/i18n/DefaultI18n.java
+++ b/sonar-core/src/main/java/org/sonar/core/i18n/DefaultI18n.java
@@ -40,7 +40,7 @@ import java.util.Set;
import javax.annotation.CheckForNull;
import javax.annotation.Nullable;
import org.apache.commons.io.IOUtils;
-import org.picocontainer.Startable;
+import org.sonar.api.Startable;
import org.sonar.api.utils.SonarException;
import org.sonar.api.utils.System2;
import org.sonar.api.utils.log.Logger;
diff --git a/sonar-core/src/main/java/org/sonar/core/language/LanguagesProvider.java b/sonar-core/src/main/java/org/sonar/core/language/LanguagesProvider.java
new file mode 100644
index 00000000000..811cd624c48
--- /dev/null
+++ b/sonar-core/src/main/java/org/sonar/core/language/LanguagesProvider.java
@@ -0,0 +1,38 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2022 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.core.language;
+
+import java.util.List;
+import java.util.Optional;
+
+import org.sonar.api.resources.Language;
+import org.sonar.api.resources.Languages;
+import org.springframework.context.annotation.Bean;
+
+public class LanguagesProvider {
+ @Bean("Languages")
+ public Languages provide(Optional<List<Language>> languages) {
+ if (languages.isPresent()) {
+ return new Languages(languages.get().toArray(new Language[0]));
+ } else {
+ return new Languages();
+ }
+ }
+}
diff --git a/sonar-core/src/main/java/org/sonar/core/platform/ClassDerivedBeanDefinition.java b/sonar-core/src/main/java/org/sonar/core/platform/ClassDerivedBeanDefinition.java
new file mode 100644
index 00000000000..7ea16397e60
--- /dev/null
+++ b/sonar-core/src/main/java/org/sonar/core/platform/ClassDerivedBeanDefinition.java
@@ -0,0 +1,64 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2022 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.core.platform;
+
+import java.lang.reflect.Constructor;
+import org.springframework.beans.BeanUtils;
+import org.springframework.beans.factory.support.RootBeanDefinition;
+import org.springframework.lang.Nullable;
+
+/**
+ * Taken from Spring's GenericApplicationContext.ClassDerivedBeanDefinition.
+ * The goal is to support multiple constructors when adding extensions for plugins when no annotations are present.
+ * Spring will pick the constructor with the highest number of arguments that it can inject.
+ */
+public class ClassDerivedBeanDefinition extends RootBeanDefinition {
+ public ClassDerivedBeanDefinition(Class<?> beanClass) {
+ super(beanClass);
+ }
+
+ public ClassDerivedBeanDefinition(ClassDerivedBeanDefinition original) {
+ super(original);
+ }
+
+ /**
+ * This method gets called from AbstractAutowireCapableBeanFactory#createBeanInstance when a bean is instantiated.
+ * It first tries to look at annotations or any other methods provided by bean post processors. If a constructor can't be determined, it will fallback to this method.
+ */
+ @Override
+ @Nullable
+ public Constructor<?>[] getPreferredConstructors() {
+ Class<?> clazz = getBeanClass();
+ Constructor<?> primaryCtor = BeanUtils.findPrimaryConstructor(clazz);
+ if (primaryCtor != null) {
+ return new Constructor<?>[] {primaryCtor};
+ }
+ Constructor<?>[] publicCtors = clazz.getConstructors();
+ if (publicCtors.length > 0) {
+ return publicCtors;
+ }
+ return null;
+ }
+
+ @Override
+ public RootBeanDefinition cloneBeanDefinition() {
+ return new ClassDerivedBeanDefinition(this);
+ }
+}
diff --git a/sonar-core/src/main/java/org/sonar/core/platform/ComponentContainer.java b/sonar-core/src/main/java/org/sonar/core/platform/ComponentContainer.java
deleted file mode 100644
index b8d7c56b310..00000000000
--- a/sonar-core/src/main/java/org/sonar/core/platform/ComponentContainer.java
+++ /dev/null
@@ -1,339 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2022 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-package org.sonar.core.platform;
-
-import com.google.common.collect.Iterables;
-import com.google.common.collect.Lists;
-import java.lang.annotation.Annotation;
-import java.util.ArrayList;
-import java.util.Iterator;
-import java.util.List;
-import javax.annotation.Nullable;
-import org.picocontainer.Characteristics;
-import org.picocontainer.ComponentAdapter;
-import org.picocontainer.ComponentFactory;
-import org.picocontainer.ComponentMonitor;
-import org.picocontainer.DefaultPicoContainer;
-import org.picocontainer.LifecycleStrategy;
-import org.picocontainer.MutablePicoContainer;
-import org.picocontainer.PicoContainer;
-import org.picocontainer.behaviors.OptInCaching;
-import org.picocontainer.monitors.NullComponentMonitor;
-import org.sonar.api.ce.ComputeEngineSide;
-import org.sonar.api.config.PropertyDefinitions;
-import org.sonar.api.scanner.ScannerSide;
-import org.sonar.api.server.ServerSide;
-import org.sonar.api.utils.System2;
-
-import static com.google.common.collect.ImmutableList.copyOf;
-import static java.util.Objects.requireNonNull;
-import static java.util.Optional.ofNullable;
-
-@ScannerSide
-@ServerSide
-@ComputeEngineSide
-public class ComponentContainer implements ExtensionContainer {
- public static final int COMPONENTS_IN_EMPTY_COMPONENT_CONTAINER = 2;
-
- private static final class ExtendedDefaultPicoContainer extends DefaultPicoContainer {
- private ExtendedDefaultPicoContainer(ComponentFactory componentFactory, LifecycleStrategy lifecycleStrategy,
- @Nullable PicoContainer parent, ComponentMonitor componentMonitor) {
- super(componentFactory, lifecycleStrategy, parent, componentMonitor);
- }
-
- @Override
- public Object getComponent(final Object componentKeyOrType, final Class<? extends Annotation> annotation) {
- try {
- return super.getComponent(componentKeyOrType, annotation);
- } catch (Throwable t) {
- throw new IllegalStateException("Unable to load component " + componentKeyOrType, t);
- }
- }
-
- @Override
- public MutablePicoContainer makeChildContainer() {
- DefaultPicoContainer pc = new ExtendedDefaultPicoContainer(componentFactory, lifecycleStrategy, this, componentMonitor);
- addChildContainer(pc);
- return pc;
- }
- }
-
- private ComponentContainer parent;
- private final List<ComponentContainer> children = new ArrayList<>();
- private MutablePicoContainer pico;
- private PropertyDefinitions propertyDefinitions;
- private ComponentKeys componentKeys;
-
- /**
- * Create root container
- */
- public ComponentContainer() {
- this(createPicoContainer());
- }
-
- protected ComponentContainer(MutablePicoContainer picoContainer) {
- this(picoContainer, new PropertyDefinitions(System2.INSTANCE));
- }
-
- protected ComponentContainer(MutablePicoContainer picoContainer, PropertyDefinitions propertyDefinitions) {
- requireNonNull(propertyDefinitions, "PropertyDefinitions can not be null");
- this.parent = null;
- this.pico = picoContainer;
- this.componentKeys = new ComponentKeys();
- this.propertyDefinitions = propertyDefinitions;
- addSingleton(propertyDefinitions);
- addSingleton(this);
- }
-
- /**
- * Create child container
- */
- protected ComponentContainer(ComponentContainer parent) {
- this.parent = parent;
- this.pico = parent.pico.makeChildContainer();
- this.parent.children.add(this);
- this.propertyDefinitions = parent.propertyDefinitions;
- this.componentKeys = new ComponentKeys();
- addSingleton(this);
- }
-
- protected void setParent(ComponentContainer parent) {
- this.parent = parent;
- }
-
- public void execute() {
- try {
- startComponents();
- } finally {
- stopComponents();
- }
- }
-
- /**
- * This method MUST NOT be renamed start() because the container is registered itself in picocontainer. Starting
- * a component twice is not authorized.
- */
- public ComponentContainer startComponents() {
- try {
- doBeforeStart();
- pico.start();
- doAfterStart();
- return this;
- } catch (Exception e) {
- throw PicoUtils.propagate(e);
- }
- }
-
- /**
- * This method aims to be overridden
- */
- protected void doBeforeStart() {
- // nothing
- }
-
- /**
- * This method aims to be overridden
- */
- protected void doAfterStart() {
- // nothing
- }
-
- /**
- * This method MUST NOT be renamed stop() because the container is registered itself in picocontainer. Starting
- * a component twice is not authorized.
- */
- public ComponentContainer stopComponents() {
- try {
- stopChildren();
- if (pico.getLifecycleState().isStarted()) {
- pico.stop();
- }
- pico.dispose();
- } finally {
- if (parent != null) {
- parent.removeChild(this);
- }
- }
- return this;
- }
-
- private void stopChildren() {
- // loop over a copy of list of children in reverse order, both to stop last added child first and because children
- // remove themselves from the list of children of their parent (ie. changing this.children)
- Lists.reverse(new ArrayList<>(this.children))
- .forEach(ComponentContainer::stopComponents);
- }
-
- /**
- * @since 3.5
- */
- @Override
- public ComponentContainer add(Object... objects) {
- for (Object object : objects) {
- if (object instanceof ComponentAdapter) {
- addPicoAdapter((ComponentAdapter) object);
- } else if (object instanceof Iterable) {
- add(Iterables.toArray((Iterable) object, Object.class));
- } else {
- addSingleton(object);
- }
- }
- return this;
- }
-
- public void addIfMissing(Object object, Class<?> objectType) {
- if (getComponentByType(objectType) == null) {
- add(object);
- }
- }
-
- @Override
- public ComponentContainer addSingletons(Iterable<?> components) {
- for (Object component : components) {
- addSingleton(component);
- }
- return this;
- }
-
- public ComponentContainer addSingleton(Object component) {
- return addComponent(component, true);
- }
-
- /**
- * @param singleton return always the same instance if true, else a new instance
- * is returned each time the component is requested
- */
- public ComponentContainer addComponent(Object component, boolean singleton) {
- Object key = componentKeys.of(component);
- if (component instanceof ComponentAdapter) {
- pico.addAdapter((ComponentAdapter) component);
- } else {
- try {
- pico.as(singleton ? Characteristics.CACHE : Characteristics.NO_CACHE).addComponent(key, component);
- } catch (Throwable t) {
- throw new IllegalStateException("Unable to register component " + getName(component), t);
- }
- declareExtension("", component);
- }
- return this;
- }
-
- @Override
- public ComponentContainer addExtension(@Nullable PluginInfo pluginInfo, Object extension) {
- Object key = componentKeys.of(extension);
- try {
- pico.as(Characteristics.CACHE).addComponent(key, extension);
- } catch (Throwable t) {
- throw new IllegalStateException("Unable to register extension " + getName(extension) + (pluginInfo != null ? (" from plugin '" + pluginInfo.getKey() + "'") : ""), t);
- }
- declareExtension(pluginInfo, extension);
- return this;
- }
-
- @Override
- public ComponentContainer addExtension(@Nullable String defaultCategory, Object extension) {
- Object key = componentKeys.of(extension);
- try {
- pico.as(Characteristics.CACHE).addComponent(key, extension);
- } catch (Throwable t) {
- throw new IllegalStateException("Unable to register extension " + getName(extension), t);
- }
- declareExtension(defaultCategory, extension);
- return this;
- }
-
- private static String getName(Object extension) {
- if (extension instanceof Class) {
- return ((Class<?>) extension).getName();
- }
- return getName(extension.getClass());
- }
-
- @Override
- public ComponentContainer declareExtension(@Nullable PluginInfo pluginInfo, Object extension) {
- declareExtension(pluginInfo != null ? pluginInfo.getName() : "", extension);
- return this;
- }
-
- @Override
- public ComponentContainer declareExtension(@Nullable String defaultCategory, Object extension) {
- propertyDefinitions.addComponent(extension, ofNullable(defaultCategory).orElse(""));
- return this;
- }
-
- public ComponentContainer addPicoAdapter(ComponentAdapter<?> adapter) {
- pico.addAdapter(adapter);
- return this;
- }
-
- @Override
- public <T> T getComponentByType(Class<T> type) {
- return pico.getComponent(type);
- }
-
- public Object getComponentByKey(Object key) {
- return pico.getComponent(key);
- }
-
- @Override
- public <T> List<T> getComponentsByType(Class<T> tClass) {
- return pico.getComponents(tClass);
- }
-
- public ComponentContainer removeChild(ComponentContainer childToBeRemoved) {
- requireNonNull(childToBeRemoved);
- Iterator<ComponentContainer> childrenIterator = children.iterator();
- while (childrenIterator.hasNext()) {
- ComponentContainer child = childrenIterator.next();
- if (child == childToBeRemoved) {
- if (pico.removeChildContainer(child.pico)) {
- childrenIterator.remove();
- }
- break;
- }
- }
- return this;
- }
-
- public ComponentContainer createChild() {
- return new ComponentContainer(this);
- }
-
- public static MutablePicoContainer createPicoContainer() {
- return new ExtendedDefaultPicoContainer(new OptInCaching(), new StartableCloseableSafeLifecyleStrategy(), null, new NullComponentMonitor());
- }
-
- public ComponentContainer getParent() {
- return parent;
- }
-
- public List<ComponentContainer> getChildren() {
- return copyOf(children);
- }
-
- public MutablePicoContainer getPicoContainer() {
- return pico;
- }
-
- public int size() {
- return pico.getComponentAdapters().size();
- }
-
-}
diff --git a/sonar-core/src/main/java/org/sonar/core/platform/Container.java b/sonar-core/src/main/java/org/sonar/core/platform/Container.java
index e7a9963623d..eb9b03789ff 100644
--- a/sonar-core/src/main/java/org/sonar/core/platform/Container.java
+++ b/sonar-core/src/main/java/org/sonar/core/platform/Container.java
@@ -20,14 +20,15 @@
package org.sonar.core.platform;
import java.util.List;
+import java.util.Optional;
public interface Container {
Container add(Object... objects);
- Container addSingletons(Iterable<?> components);
-
<T> T getComponentByType(Class<T> type);
+ <T> Optional<T> getOptionalComponentByType(Class<T> type);
+
<T> List<T> getComponentsByType(Class<T> type);
Container getParent();
diff --git a/sonar-core/src/main/java/org/sonar/core/platform/LazyStrategy.java b/sonar-core/src/main/java/org/sonar/core/platform/LazyStrategy.java
new file mode 100644
index 00000000000..85c3f4d8cba
--- /dev/null
+++ b/sonar-core/src/main/java/org/sonar/core/platform/LazyStrategy.java
@@ -0,0 +1,30 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2022 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.core.platform;
+
+import javax.annotation.Nullable;
+import org.springframework.beans.factory.config.BeanDefinition;
+
+public class LazyStrategy extends SpringInitStrategy {
+ @Override
+ protected boolean isLazyInit(BeanDefinition beanDefinition, @Nullable Class<?> clazz) {
+ return true;
+ }
+}
diff --git a/sonar-core/src/main/java/org/sonar/core/platform/PicoUtils.java b/sonar-core/src/main/java/org/sonar/core/platform/LazyUnlessStartableStrategy.java
index c73bb07ea6b..307881a89c8 100644
--- a/sonar-core/src/main/java/org/sonar/core/platform/PicoUtils.java
+++ b/sonar-core/src/main/java/org/sonar/core/platform/LazyUnlessStartableStrategy.java
@@ -19,29 +19,14 @@
*/
package org.sonar.core.platform;
-import com.google.common.base.Throwables;
-import org.picocontainer.PicoLifecycleException;
+import org.sonar.api.Startable;
+import org.springframework.beans.factory.config.BeanDefinition;
-class PicoUtils {
+import javax.annotation.Nullable;
- private PicoUtils() {
- // only static methods
- }
-
- static Throwable sanitize(Throwable t) {
- Throwable result = t;
- Throwable cause = t.getCause();
- if (t instanceof PicoLifecycleException && cause != null) {
- if ("wrapper".equals(cause.getMessage()) && cause.getCause() != null) {
- result = cause.getCause();
- } else {
- result = cause;
- }
- }
- return result;
- }
-
- static RuntimeException propagate(Throwable t) {
- throw Throwables.propagate(sanitize(t));
+public class LazyUnlessStartableStrategy extends SpringInitStrategy {
+ @Override
+ protected boolean isLazyInit(BeanDefinition beanDefinition, @Nullable Class<?> clazz) {
+ return clazz == null || !Startable.class.isAssignableFrom(clazz);
}
}
diff --git a/sonar-core/src/main/java/org/sonar/core/platform/ListContainer.java b/sonar-core/src/main/java/org/sonar/core/platform/ListContainer.java
new file mode 100644
index 00000000000..eaf8a16e70e
--- /dev/null
+++ b/sonar-core/src/main/java/org/sonar/core/platform/ListContainer.java
@@ -0,0 +1,95 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2022 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.core.platform;
+
+import com.google.common.collect.Iterables;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+import javax.annotation.Nullable;
+
+import static java.util.Collections.unmodifiableList;
+
+/**
+ * Intended to be used in tests
+ */
+public class ListContainer implements ExtensionContainer {
+ private final List<Object> objects = new ArrayList<>();
+
+ @Override
+ public Container add(Object... objects) {
+ for (Object o : objects) {
+ if (o instanceof Module) {
+ ((Module) o).configure(this);
+ } else if (o instanceof Iterable) {
+ add(Iterables.toArray((Iterable<?>) o, Object.class));
+ } else {
+ this.objects.add(o);
+ }
+ }
+ return this;
+ }
+
+ public List<Object> getAddedObjects() {
+ return unmodifiableList(new ArrayList<>(objects));
+ }
+
+ @Override
+ public <T> T getComponentByType(Class<T> type) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public <T> Optional<T> getOptionalComponentByType(Class<T> type) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public <T> List<T> getComponentsByType(Class<T> type) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public ExtensionContainer addExtension(@Nullable PluginInfo pluginInfo, Object extension) {
+ add(extension);
+ return this;
+ }
+
+ @Override
+ public ExtensionContainer addExtension(@Nullable String defaultCategory, Object extension) {
+ add(extension);
+ return this;
+ }
+
+ @Override
+ public ExtensionContainer declareExtension(@Nullable PluginInfo pluginInfo, Object extension) {
+ return this;
+ }
+
+ @Override
+ public ExtensionContainer declareExtension(@Nullable String defaultCategory, Object extension) {
+ return this;
+ }
+
+ @Override
+ public ExtensionContainer getParent() {
+ throw new UnsupportedOperationException();
+ }
+}
diff --git a/sonar-core/src/main/java/org/sonar/core/platform/Module.java b/sonar-core/src/main/java/org/sonar/core/platform/Module.java
index 2e94f28c50e..383aa6be3e2 100644
--- a/sonar-core/src/main/java/org/sonar/core/platform/Module.java
+++ b/sonar-core/src/main/java/org/sonar/core/platform/Module.java
@@ -24,13 +24,11 @@ import javax.annotation.Nullable;
import static com.google.common.base.Preconditions.checkNotNull;
public abstract class Module {
- private ComponentContainer container;
+ private Container container;
- public Module configure(ComponentContainer container) {
+ public Module configure(Container container) {
this.container = checkNotNull(container);
-
configureModule();
-
return this;
}
@@ -43,7 +41,7 @@ public abstract class Module {
for (Object object : objects) {
if (object != null) {
- container.addComponent(object, true);
+ container.add(object);
}
}
}
diff --git a/sonar-core/src/main/java/org/sonar/core/platform/PriorityBeanFactory.java b/sonar-core/src/main/java/org/sonar/core/platform/PriorityBeanFactory.java
new file mode 100644
index 00000000000..a6af0bbb5de
--- /dev/null
+++ b/sonar-core/src/main/java/org/sonar/core/platform/PriorityBeanFactory.java
@@ -0,0 +1,138 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2022 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.core.platform;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+import javax.annotation.CheckForNull;
+import javax.annotation.Nullable;
+import org.springframework.beans.BeanWrapper;
+import org.springframework.beans.factory.support.DefaultListableBeanFactory;
+import org.springframework.beans.factory.support.RootBeanDefinition;
+
+public class PriorityBeanFactory extends DefaultListableBeanFactory {
+ /**
+ * Determines highest priority of the bean candidates.
+ * Does not take into account the @Primary annotations.
+ * This gets called from {@link DefaultListableBeanFactory#determineAutowireCandidate} when the bean factory is finding the beans to autowire. That method
+ * checks for @Primary before calling this method.
+ *
+ * The strategy is to look at the @Priority annotations. If there are ties, we give priority to components that were added to child containers over their parents.
+ * If there are still ties, null is returned, which will ultimately cause Spring to throw a NoUniqueBeanDefinitionException.
+ */
+ @Override
+ @Nullable
+ protected String determineHighestPriorityCandidate(Map<String, Object> candidates, Class<?> requiredType) {
+ List<Bean> candidateBeans = candidates.entrySet().stream()
+ .filter(e -> e.getValue() != null)
+ .map(e -> new Bean(e.getKey(), e.getValue()))
+ .collect(Collectors.toList());
+
+ List<Bean> beansAfterPriority = highestPriority(candidateBeans, b -> getPriority(b.getInstance()));
+ if (beansAfterPriority.isEmpty()) {
+ return null;
+ } else if (beansAfterPriority.size() == 1) {
+ return beansAfterPriority.get(0).getName();
+ }
+
+ List<Bean> beansAfterHierarchy = highestPriority(beansAfterPriority, b -> getHierarchyPriority(b.getName()));
+ if (beansAfterHierarchy.size() == 1) {
+ return beansAfterHierarchy.get(0).getName();
+ }
+
+ return null;
+ }
+
+ private static List<Bean> highestPriority(List<Bean> candidates, PriorityFunction function) {
+ List<Bean> highestPriorityBeans = new ArrayList<>();
+ Integer highestPriority = null;
+
+ for (Bean candidate : candidates) {
+ Integer candidatePriority = function.classify(candidate);
+ if (candidatePriority == null) {
+ candidatePriority = Integer.MAX_VALUE;
+ }
+ if (highestPriority == null) {
+ highestPriority = candidatePriority;
+ highestPriorityBeans.add(candidate);
+ } else if (candidatePriority < highestPriority) {
+ highestPriorityBeans.clear();
+ highestPriority = candidatePriority;
+ highestPriorityBeans.add(candidate);
+ } else if (candidatePriority.equals(highestPriority)) {
+ highestPriorityBeans.add(candidate);
+ }
+ }
+ return highestPriorityBeans;
+ }
+
+ @CheckForNull
+ private Integer getHierarchyPriority(String beanName) {
+ DefaultListableBeanFactory factory = this;
+ int i = 1;
+ while (factory != null) {
+ if (factory.containsBeanDefinition(beanName)) {
+ return i;
+ }
+ factory = (DefaultListableBeanFactory) factory.getParentBeanFactory();
+ i++;
+ }
+ return null;
+ }
+
+ /**
+ * A common mistake when migrating from Pico Container to Spring is to forget to add @Inject or @Autowire annotations to classes that have multiple constructors.
+ * Spring will fail if there is no default no-arg constructor, but it will silently use the no-arg constructor if there is one, never calling the other constructors.
+ * We override this method to fail fast if a class has multiple constructors.
+ */
+ @Override
+ protected BeanWrapper instantiateBean(String beanName, RootBeanDefinition mbd) {
+ if (mbd.hasBeanClass() && mbd.getBeanClass().getConstructors().length > 1) {
+ throw new IllegalStateException("Constructor annotations missing in: " + mbd.getBeanClass());
+ }
+ return super.instantiateBean(beanName, mbd);
+ }
+
+ private static class Bean {
+ private final String name;
+ private final Object instance;
+
+ public Bean(String name, Object instance) {
+ this.name = name;
+ this.instance = instance;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public Object getInstance() {
+ return instance;
+ }
+ }
+
+ @FunctionalInterface
+ private interface PriorityFunction {
+ @CheckForNull
+ Integer classify(Bean candidate);
+ }
+}
diff --git a/sonar-core/src/main/java/org/sonar/core/platform/SpringComponentContainer.java b/sonar-core/src/main/java/org/sonar/core/platform/SpringComponentContainer.java
new file mode 100644
index 00000000000..44f0db99eed
--- /dev/null
+++ b/sonar-core/src/main/java/org/sonar/core/platform/SpringComponentContainer.java
@@ -0,0 +1,260 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2022 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.core.platform;
+
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+import java.util.function.Supplier;
+import javax.annotation.CheckForNull;
+import javax.annotation.Nullable;
+import org.sonar.api.config.PropertyDefinitions;
+import org.sonar.api.utils.System2;
+import org.springframework.beans.factory.NoSuchBeanDefinitionException;
+import org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory;
+import org.springframework.context.annotation.AnnotationConfigApplicationContext;
+
+import static java.util.Collections.emptyList;
+import static java.util.Optional.ofNullable;
+
+public class SpringComponentContainer implements StartableContainer {
+ protected final AnnotationConfigApplicationContext context;
+ @Nullable
+ protected final SpringComponentContainer parent;
+ protected final List<SpringComponentContainer> children = new ArrayList<>();
+
+ private final PropertyDefinitions propertyDefinitions;
+ private final ComponentKeys componentKeys = new ComponentKeys();
+
+ public SpringComponentContainer() {
+ this(null, new PropertyDefinitions(System2.INSTANCE), emptyList(), new LazyUnlessStartableStrategy());
+ }
+
+ protected SpringComponentContainer(List<?> externalExtensions) {
+ this(null, new PropertyDefinitions(System2.INSTANCE), externalExtensions, new LazyUnlessStartableStrategy());
+ }
+
+ protected SpringComponentContainer(SpringComponentContainer parent) {
+ this(parent, parent.propertyDefinitions, emptyList(), new LazyUnlessStartableStrategy());
+ }
+
+ protected SpringComponentContainer(SpringComponentContainer parent, SpringInitStrategy initStrategy) {
+ this(parent, parent.propertyDefinitions, emptyList(), initStrategy);
+ }
+
+ protected SpringComponentContainer(@Nullable SpringComponentContainer parent, PropertyDefinitions propertyDefs, List<?> externalExtensions, SpringInitStrategy initStrategy) {
+ this.parent = parent;
+ this.propertyDefinitions = propertyDefs;
+ this.context = new AnnotationConfigApplicationContext(new PriorityBeanFactory());
+ this.context.setAllowBeanDefinitionOverriding(false);
+ ((AbstractAutowireCapableBeanFactory) context.getBeanFactory()).setParameterNameDiscoverer(null);
+ if (parent != null) {
+ context.setParent(parent.context);
+ parent.children.add(this);
+ }
+ add(initStrategy);
+ add(this);
+ add(new StartableBeanPostProcessor());
+ add(externalExtensions);
+ add(propertyDefs);
+ }
+
+ /**
+ * Beans need to have a unique name, otherwise they'll override each other.
+ * The strategy is:
+ * - For classes, use the classloader + fully qualified class name as the name of the bean
+ * - For instances, use the Classloader + FQCN + toString()
+ * - If the object is a collection, iterate through the elements and apply the same strategy for each of them
+ */
+ @Override
+ public Container add(Object... objects) {
+ for (Object o : objects) {
+ if (o instanceof Class) {
+ Class<?> clazz = (Class<?>) o;
+ if (Module.class.isAssignableFrom(clazz)) {
+ throw new IllegalStateException("Modules should be added as instances");
+ }
+ context.registerBean(componentKeys.ofClass(clazz), clazz);
+ declareExtension("", o);
+ } else if (o instanceof Module) {
+ ((Module) o).configure(this);
+ } else if (o instanceof Iterable) {
+ add(Iterables.toArray((Iterable<?>) o, Object.class));
+ } else {
+ registerInstance(o);
+ declareExtension("", o);
+ }
+ }
+ return this;
+ }
+
+ private <T> void registerInstance(T instance) {
+ Supplier<T> supplier = () -> instance;
+ Class<T> clazz = (Class<T>) instance.getClass();
+ context.registerBean(componentKeys.ofInstance(instance), clazz, supplier);
+ }
+
+ /**
+ * Extensions are usually added by plugins and we assume they don't support any injection-related annotations.
+ * Spring contexts supporting annotations will fail if multiple constructors are present without any annotations indicating which one to use for injection.
+ * For that reason, we need to create the beans ourselves, using ClassDerivedBeanDefinition, which will declare that all constructors can be used for injection.
+ */
+ private Container addExtension(Object o) {
+ if (o instanceof Class) {
+ Class<?> clazz = (Class<?>) o;
+ ClassDerivedBeanDefinition bd = new ClassDerivedBeanDefinition(clazz);
+ context.registerBeanDefinition(componentKeys.ofClass(clazz), bd);
+ } else if (o instanceof Iterable) {
+ ((Iterable<?>) o).forEach(this::addExtension);
+ } else {
+ registerInstance(o);
+ }
+ return this;
+ }
+
+ @Override
+ public <T> T getComponentByType(Class<T> type) {
+ try {
+ return context.getBean(type);
+ } catch (Exception t) {
+ throw new IllegalStateException("Unable to load component " + type, t);
+ }
+ }
+
+ @Override public <T> Optional<T> getOptionalComponentByType(Class<T> type) {
+ try {
+ return Optional.of(context.getBean(type));
+ } catch (NoSuchBeanDefinitionException t) {
+ return Optional.empty();
+ }
+ }
+
+ @Override
+ public <T> List<T> getComponentsByType(Class<T> type) {
+ try {
+ return new ArrayList<>(context.getBeansOfType(type).values());
+ } catch (Exception t) {
+ throw new IllegalStateException("Unable to load components " + type, t);
+ }
+ }
+
+ public AnnotationConfigApplicationContext context() {
+ return context;
+ }
+
+ public void execute() {
+ RuntimeException r = null;
+ try {
+ startComponents();
+ } catch (RuntimeException e) {
+ r = e;
+ } finally {
+ try {
+ stopComponents();
+ } catch (RuntimeException e) {
+ if (r == null) {
+ r = e;
+ }
+ }
+ }
+ if (r != null) {
+ throw r;
+ }
+ }
+
+ @Override
+ public SpringComponentContainer startComponents() {
+ doBeforeStart();
+ context.refresh();
+ doAfterStart();
+ return this;
+ }
+
+ public SpringComponentContainer stopComponents() {
+ try {
+ stopChildren();
+ if (context.isActive()) {
+ context.close();
+ }
+ } finally {
+ if (parent != null) {
+ parent.children.remove(this);
+ }
+ }
+ return this;
+ }
+
+ private void stopChildren() {
+ // loop over a copy of list of children in reverse order
+ Lists.reverse(new ArrayList<>(this.children)).forEach(SpringComponentContainer::stopComponents);
+ }
+
+ public SpringComponentContainer createChild() {
+ return new SpringComponentContainer(this);
+ }
+
+ @Override
+ @CheckForNull
+ public SpringComponentContainer getParent() {
+ return parent;
+ }
+
+ @Override
+ public SpringComponentContainer addExtension(@Nullable PluginInfo pluginInfo, Object extension) {
+ addExtension(extension);
+ declareExtension(pluginInfo, extension);
+ return this;
+ }
+
+ @Override
+ public SpringComponentContainer addExtension(@Nullable String defaultCategory, Object extension) {
+ addExtension(extension);
+ declareExtension(defaultCategory, extension);
+ return this;
+ }
+
+ @Override
+ public SpringComponentContainer declareExtension(@Nullable PluginInfo pluginInfo, Object extension) {
+ declareExtension(pluginInfo != null ? pluginInfo.getName() : "", extension);
+ return this;
+ }
+
+ @Override
+ public SpringComponentContainer declareExtension(@Nullable String defaultCategory, Object extension) {
+ this.propertyDefinitions.addComponent(extension, ofNullable(defaultCategory).orElse(""));
+ return this;
+ }
+
+ /**
+ * This method aims to be overridden
+ */
+ protected void doBeforeStart() {
+ // nothing
+ }
+
+ /**
+ * This method aims to be overridden
+ */
+ protected void doAfterStart() {
+ // nothing
+ }
+}
diff --git a/sonar-core/src/main/java/org/sonar/core/platform/SpringInitStrategy.java b/sonar-core/src/main/java/org/sonar/core/platform/SpringInitStrategy.java
new file mode 100644
index 00000000000..107c38709ba
--- /dev/null
+++ b/sonar-core/src/main/java/org/sonar/core/platform/SpringInitStrategy.java
@@ -0,0 +1,39 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2022 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.core.platform;
+
+import javax.annotation.Nullable;
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.config.BeanDefinition;
+import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
+import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
+
+public abstract class SpringInitStrategy implements BeanFactoryPostProcessor {
+ @Override
+ public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
+ for (String beanName : beanFactory.getBeanDefinitionNames()) {
+ BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanName);
+ Class<?> rawClass = beanDefinition.getResolvableType().getRawClass();
+ beanDefinition.setLazyInit(isLazyInit(beanDefinition, rawClass));
+ }
+ }
+
+ protected abstract boolean isLazyInit(BeanDefinition beanDefinition, @Nullable Class<?> clazz);
+}
diff --git a/sonar-core/src/main/java/org/sonar/core/platform/StartableBeanPostProcessor.java b/sonar-core/src/main/java/org/sonar/core/platform/StartableBeanPostProcessor.java
new file mode 100644
index 00000000000..a4aa6ea94e7
--- /dev/null
+++ b/sonar-core/src/main/java/org/sonar/core/platform/StartableBeanPostProcessor.java
@@ -0,0 +1,55 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2022 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.core.platform;
+
+import org.sonar.api.Startable;
+import org.sonar.api.utils.log.Loggers;
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.config.DestructionAwareBeanPostProcessor;
+import org.springframework.lang.Nullable;
+
+public class StartableBeanPostProcessor implements DestructionAwareBeanPostProcessor {
+ @Override
+ @Nullable
+ public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
+ if (bean instanceof Startable) {
+ ((Startable) bean).start();
+ }
+ return bean;
+ }
+
+ @Override
+ public boolean requiresDestruction(Object bean) {
+ return bean instanceof Startable;
+ }
+
+ @Override
+ public void postProcessBeforeDestruction(Object bean, String beanName) throws BeansException {
+ try {
+ // note: Spring will call close() on AutoCloseable beans.
+ if (bean instanceof Startable) {
+ ((Startable) bean).stop();
+ }
+ } catch (Exception e) {
+ Loggers.get(StartableBeanPostProcessor.class)
+ .warn("Dispose of component {} failed", bean.getClass().getCanonicalName(), e);
+ }
+ }
+}
diff --git a/sonar-core/src/main/java/org/sonar/core/platform/StartableCloseableSafeLifecyleStrategy.java b/sonar-core/src/main/java/org/sonar/core/platform/StartableCloseableSafeLifecyleStrategy.java
deleted file mode 100644
index 9549af80a27..00000000000
--- a/sonar-core/src/main/java/org/sonar/core/platform/StartableCloseableSafeLifecyleStrategy.java
+++ /dev/null
@@ -1,105 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2022 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-package org.sonar.core.platform;
-
-import java.io.Closeable;
-import java.io.Serializable;
-import java.util.Arrays;
-import java.util.stream.Stream;
-import org.picocontainer.ComponentAdapter;
-import org.picocontainer.LifecycleStrategy;
-import org.picocontainer.Startable;
-import org.sonar.api.utils.log.Logger;
-import org.sonar.api.utils.log.Loggers;
-
-public class StartableCloseableSafeLifecyleStrategy implements LifecycleStrategy, Serializable {
- private static final Class<?>[] TYPES_WITH_LIFECYCLE = new Class[] {Startable.class, org.sonar.api.Startable.class, Closeable.class, AutoCloseable.class};
-
- private static final Logger LOG = Loggers.get(StartableCloseableSafeLifecyleStrategy.class);
-
- @Override
- public void start(Object component) {
- if (component instanceof Startable) {
- ((Startable) component).start();
- } else if (component instanceof org.sonar.api.Startable) {
- ((org.sonar.api.Startable) component).start();
- }
- }
-
- @Override
- public void stop(Object component) {
- try {
- if (component instanceof Startable) {
- ((Startable) component).stop();
- } else if (component instanceof org.sonar.api.Startable) {
- ((org.sonar.api.Startable) component).stop();
- }
- } catch (RuntimeException | Error e) {
- Loggers.get(StartableCloseableSafeLifecyleStrategy.class)
- .warn("Stopping of component {} failed", component.getClass().getCanonicalName(), e);
- }
- }
-
- @Override
- public void dispose(Object component) {
- try {
- if (component instanceof Closeable) {
- ((Closeable) component).close();
- } else if (component instanceof AutoCloseable) {
- ((AutoCloseable) component).close();
- }
- } catch (Exception e) {
- Loggers.get(StartableCloseableSafeLifecyleStrategy.class)
- .warn("Dispose of component {} failed", component.getClass().getCanonicalName(), e);
- }
- }
-
- @Override
- public boolean hasLifecycle(Class<?> type) {
- if (Arrays.stream(TYPES_WITH_LIFECYCLE).anyMatch(t1 -> t1.isAssignableFrom(type))) {
- return true;
- }
-
- if (Stream.of("start", "stop").anyMatch(t -> hasMethod(type, t))) {
- LOG.warn("Component of type {} defines methods start() and/or stop(). Neither will be invoked to start/stop the component." +
- " Please implement either {} or {}",
- type, Startable.class.getName(), org.sonar.api.Startable.class.getName());
- }
- if (hasMethod(type, "close")) {
- LOG.warn("Component of type {} defines method close(). It won't be invoked to dispose the component." +
- " Please implement either {} or {}",
- type, Closeable.class.getName(), AutoCloseable.class.getName());
- }
- return false;
- }
-
- private static boolean hasMethod(Class<?> type, String methodName) {
- try {
- return type.getMethod(methodName) != null;
- } catch (NoSuchMethodException e) {
- return false;
- }
- }
-
- @Override
- public boolean isLazy(ComponentAdapter<?> adapter) {
- return false;
- }
-}
diff --git a/sonar-core/src/main/java/org/sonar/core/platform/StartableContainer.java b/sonar-core/src/main/java/org/sonar/core/platform/StartableContainer.java
new file mode 100644
index 00000000000..2cf7b83ba77
--- /dev/null
+++ b/sonar-core/src/main/java/org/sonar/core/platform/StartableContainer.java
@@ -0,0 +1,24 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2022 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.core.platform;
+
+public interface StartableContainer extends ExtensionContainer {
+ StartableContainer startComponents();
+}
diff --git a/sonar-core/src/main/java/org/sonar/core/util/UuidFactoryImpl.java b/sonar-core/src/main/java/org/sonar/core/util/UuidFactoryImpl.java
index 45272778ea6..1257dd4b485 100644
--- a/sonar-core/src/main/java/org/sonar/core/util/UuidFactoryImpl.java
+++ b/sonar-core/src/main/java/org/sonar/core/util/UuidFactoryImpl.java
@@ -27,7 +27,7 @@ public enum UuidFactoryImpl implements UuidFactory {
/**
* Should be removed as long {@link Uuids} is not used anymore. {@code UuidFactoryImpl}
- * should be built by picocontainer through a public constructor.
+ * should be injected by the ioc container through a public constructor.
*/
INSTANCE;