aboutsummaryrefslogtreecommitdiffstats
path: root/server/sonar-ce-task
diff options
context:
space:
mode:
authorSébastien Lesaint <sebastien.lesaint@sonarsource.com>2018-06-22 13:33:23 +0200
committersonartech <sonartech@sonarsource.com>2018-06-29 09:10:14 +0200
commitce684ddae14d1927a3111f43b537a22cb3168ebf (patch)
tree4097d046fed34e6b7f7cdea6a4e21cf225822462 /server/sonar-ce-task
parent444d5b4d5dcd12123e5b993c92bf61bdd8097872 (diff)
downloadsonarqube-ce684ddae14d1927a3111f43b537a22cb3168ebf.tar.gz
sonarqube-ce684ddae14d1927a3111f43b537a22cb3168ebf.zip
create module sonar-ce-task
Diffstat (limited to 'server/sonar-ce-task')
-rw-r--r--server/sonar-ce-task/build.gradle66
-rw-r--r--server/sonar-ce-task/src/main/java/org/sonar/server/computation/CeTask.java166
-rw-r--r--server/sonar-ce-task/src/main/java/org/sonar/server/computation/CeTaskResult.java33
-rw-r--r--server/sonar-ce-task/src/main/java/org/sonar/server/computation/log/CeTaskLogging.java37
-rw-r--r--server/sonar-ce-task/src/main/java/org/sonar/server/computation/log/package-info.java23
-rw-r--r--server/sonar-ce-task/src/main/java/org/sonar/server/computation/package-info.java23
-rw-r--r--server/sonar-ce-task/src/main/java/org/sonar/server/computation/task/container/EagerStart.java33
-rw-r--r--server/sonar-ce-task/src/main/java/org/sonar/server/computation/task/container/TaskContainer.java50
-rw-r--r--server/sonar-ce-task/src/main/java/org/sonar/server/computation/task/container/TaskContainerImpl.java93
-rw-r--r--server/sonar-ce-task/src/main/java/org/sonar/server/computation/task/container/package-info.java23
-rw-r--r--server/sonar-ce-task/src/main/java/org/sonar/server/computation/task/step/ComputationStep.java31
-rw-r--r--server/sonar-ce-task/src/main/java/org/sonar/server/computation/task/step/ComputationStepExecutor.java83
-rw-r--r--server/sonar-ce-task/src/main/java/org/sonar/server/computation/task/step/ComputationSteps.java39
-rw-r--r--server/sonar-ce-task/src/main/java/org/sonar/server/computation/task/step/ExecuteStatelessInitExtensionsStep.java56
-rw-r--r--server/sonar-ce-task/src/main/java/org/sonar/server/computation/task/step/StatelessInitExtension.java41
-rw-r--r--server/sonar-ce-task/src/main/java/org/sonar/server/computation/task/step/TypedException.java33
-rw-r--r--server/sonar-ce-task/src/main/java/org/sonar/server/computation/task/step/package-info.java23
-rw-r--r--server/sonar-ce-task/src/main/java/org/sonar/server/computation/taskprocessor/MutableTaskResultHolder.java30
-rw-r--r--server/sonar-ce-task/src/main/java/org/sonar/server/computation/taskprocessor/MutableTaskResultHolderImpl.java44
-rw-r--r--server/sonar-ce-task/src/main/java/org/sonar/server/computation/taskprocessor/TaskResultHolder.java29
-rw-r--r--server/sonar-ce-task/src/main/java/org/sonar/server/computation/util/InitializedProperty.java43
-rw-r--r--server/sonar-ce-task/src/main/java/org/sonar/server/computation/util/package-info.java24
-rw-r--r--server/sonar-ce-task/src/test/java/org/sonar/server/computation/CeTaskTest.java148
-rw-r--r--server/sonar-ce-task/src/test/java/org/sonar/server/computation/log/CeTaskLoggingTest.java78
-rw-r--r--server/sonar-ce-task/src/test/java/org/sonar/server/computation/task/ChangeLogLevel.java40
-rw-r--r--server/sonar-ce-task/src/test/java/org/sonar/server/computation/task/container/TaskContainerImplTest.java114
-rw-r--r--server/sonar-ce-task/src/test/java/org/sonar/server/computation/task/step/ComputationStepExecutorTest.java166
-rw-r--r--server/sonar-ce-task/src/test/java/org/sonar/server/computation/task/step/ExecuteStatelessInitExtensionsStepTest.java89
-rw-r--r--server/sonar-ce-task/src/test/java/org/sonar/server/computation/task/step/StepsExplorer.java68
-rw-r--r--server/sonar-ce-task/src/test/java/org/sonar/server/computation/taskprocessor/MutableTaskResultHolderImplTest.java70
-rw-r--r--server/sonar-ce-task/src/test/resources/logback-test.xml26
31 files changed, 1822 insertions, 0 deletions
diff --git a/server/sonar-ce-task/build.gradle b/server/sonar-ce-task/build.gradle
new file mode 100644
index 00000000000..66ad1d81185
--- /dev/null
+++ b/server/sonar-ce-task/build.gradle
@@ -0,0 +1,66 @@
+description = 'Definition of a Compute Engine task and utility "framework" and classes to code one'
+
+sonarqube {
+ properties {
+ property 'sonar.projectName', "${projectTitle} :: Compute Engine :: Task"
+ }
+}
+
+sourceSets {
+ test {
+ resources {
+ srcDirs += ['src/test/projects']
+ }
+ }
+}
+
+
+import org.apache.tools.ant.filters.ReplaceTokens
+processResources {
+ filesMatching('build.properties') {
+ filter ReplaceTokens, tokens: [
+ 'buildNumber': release ? 'git rev-parse HEAD'.execute().text.trim() : 'N/A'
+ ]
+ }
+}
+
+dependencies {
+ // please keep the list grouped by configuration and ordered by name
+
+ compile 'org.picocontainer:picocontainer'
+ compile 'org.slf4j:jul-to-slf4j'
+ compile 'org.slf4j:slf4j-api'
+
+ compile project(':server:sonar-process')
+ compile project(':sonar-core')
+ compileOnly project(path: ':sonar-plugin-api')
+
+ compileOnly 'com.google.code.findbugs:jsr305'
+
+ testCompile 'ch.qos.logback:logback-access'
+ testCompile 'ch.qos.logback:logback-classic'
+ testCompile 'ch.qos.logback:logback-core'
+ testCompile 'com.google.code.findbugs:jsr305'
+ testCompile 'com.h2database:h2'
+ testCompile 'com.tngtech.java:junit-dataprovider'
+ testCompile 'junit:junit'
+ testCompile 'org.apache.logging.log4j:log4j-api'
+ testCompile 'org.apache.logging.log4j:log4j-core'
+ testCompile 'org.assertj:assertj-core'
+ testCompile 'org.assertj:assertj-guava'
+ testCompile 'org.mockito:mockito-core'
+ testCompile 'org.reflections:reflections'
+}
+
+task testJar(type: Jar) {
+ classifier = 'tests'
+ from sourceSets.test.output
+}
+
+configurations {
+ tests
+}
+
+artifacts {
+ tests testJar
+}
diff --git a/server/sonar-ce-task/src/main/java/org/sonar/server/computation/CeTask.java b/server/sonar-ce-task/src/main/java/org/sonar/server/computation/CeTask.java
new file mode 100644
index 00000000000..f0d4f06ddcf
--- /dev/null
+++ b/server/sonar-ce-task/src/main/java/org/sonar/server/computation/CeTask.java
@@ -0,0 +1,166 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.server.computation;
+
+import com.google.common.base.MoreObjects;
+import javax.annotation.CheckForNull;
+import javax.annotation.Nullable;
+import javax.annotation.concurrent.Immutable;
+
+import static com.google.common.base.Strings.emptyToNull;
+import static java.util.Objects.requireNonNull;
+
+@Immutable
+public class CeTask {
+
+ private final String organizationUuid;
+ private final String type;
+ private final String uuid;
+ private final String componentUuid;
+ private final String componentKey;
+ private final String componentName;
+ private final String submitterUuid;
+
+ private CeTask(Builder builder) {
+ this.organizationUuid = requireNonNull(emptyToNull(builder.organizationUuid), "organizationUuid can't be null nor empty");
+ this.uuid = requireNonNull(emptyToNull(builder.uuid), "uuid can't be null nor empty");
+ this.type = requireNonNull(emptyToNull(builder.type), "type can't be null nor empty");
+ this.componentUuid = emptyToNull(builder.componentUuid);
+ this.componentKey = emptyToNull(builder.componentKey);
+ this.componentName = emptyToNull(builder.componentName);
+ this.submitterUuid = emptyToNull(builder.submitterUuid);
+ }
+
+ public String getOrganizationUuid() {
+ return organizationUuid;
+ }
+
+ public String getUuid() {
+ return uuid;
+ }
+
+ public String getType() {
+ return type;
+ }
+
+ @CheckForNull
+ public String getComponentUuid() {
+ return componentUuid;
+ }
+
+ @CheckForNull
+ public String getComponentKey() {
+ return componentKey;
+ }
+
+ @CheckForNull
+ public String getComponentName() {
+ return componentName;
+ }
+
+ @CheckForNull
+ public String getSubmitterUuid() {
+ return submitterUuid;
+ }
+
+ @Override
+ public String toString() {
+ return MoreObjects.toStringHelper(this)
+ .add("organizationUuid", organizationUuid)
+ .add("type", type)
+ .add("uuid", uuid)
+ .add("componentUuid", componentUuid)
+ .add("componentKey", componentKey)
+ .add("componentName", componentName)
+ .add("submitterUuid", submitterUuid)
+ .toString();
+ }
+
+ @Override
+ public boolean equals(@Nullable Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ CeTask ceTask = (CeTask) o;
+ return uuid.equals(ceTask.uuid);
+ }
+
+ @Override
+ public int hashCode() {
+ return uuid.hashCode();
+ }
+
+ public static final class Builder {
+ private String organizationUuid;
+ private String uuid;
+ private String type;
+ private String componentUuid;
+ private String componentKey;
+ private String componentName;
+ private String submitterUuid;
+
+ public Builder setOrganizationUuid(String organizationUuid) {
+ this.organizationUuid = organizationUuid;
+ return this;
+ }
+
+ // FIXME remove this method when organization support is added to the Compute Engine queue
+ public boolean hasOrganizationUuid() {
+ return organizationUuid != null;
+ }
+
+ public Builder setUuid(String uuid) {
+ this.uuid = uuid;
+ return this;
+ }
+
+ public Builder setType(String type) {
+ this.type = type;
+ return this;
+ }
+
+ public Builder setComponentUuid(String componentUuid) {
+ this.componentUuid = componentUuid;
+ return this;
+ }
+
+ public Builder setComponentKey(@Nullable String s) {
+ this.componentKey = s;
+ return this;
+ }
+
+ public Builder setComponentName(@Nullable String s) {
+ this.componentName = s;
+ return this;
+ }
+
+ public Builder setSubmitterUuid(@Nullable String s) {
+ this.submitterUuid = s;
+ return this;
+ }
+
+ public CeTask build() {
+ return new CeTask(this);
+ }
+ }
+}
diff --git a/server/sonar-ce-task/src/main/java/org/sonar/server/computation/CeTaskResult.java b/server/sonar-ce-task/src/main/java/org/sonar/server/computation/CeTaskResult.java
new file mode 100644
index 00000000000..73dcb927513
--- /dev/null
+++ b/server/sonar-ce-task/src/main/java/org/sonar/server/computation/CeTaskResult.java
@@ -0,0 +1,33 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.server.computation;
+
+import java.util.Optional;
+
+/**
+ * Represents the result of the processing of a {@link CeTask}.
+ */
+@FunctionalInterface
+public interface CeTaskResult {
+ /**
+ * The UUID of the analysis created, if any, for the Component in {@link CeTask}
+ */
+ Optional<String> getAnalysisUuid();
+}
diff --git a/server/sonar-ce-task/src/main/java/org/sonar/server/computation/log/CeTaskLogging.java b/server/sonar-ce-task/src/main/java/org/sonar/server/computation/log/CeTaskLogging.java
new file mode 100644
index 00000000000..8f1dbba3ced
--- /dev/null
+++ b/server/sonar-ce-task/src/main/java/org/sonar/server/computation/log/CeTaskLogging.java
@@ -0,0 +1,37 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.server.computation.log;
+
+import org.slf4j.MDC;
+import org.sonar.server.computation.CeTask;
+
+public class CeTaskLogging {
+
+ public static final String MDC_CE_TASK_UUID = "ceTaskUuid";
+
+ public void initForTask(CeTask task) {
+ MDC.put(MDC_CE_TASK_UUID, task.getUuid());
+ }
+
+ public void clearForTask() {
+ MDC.remove(MDC_CE_TASK_UUID);
+ }
+
+}
diff --git a/server/sonar-ce-task/src/main/java/org/sonar/server/computation/log/package-info.java b/server/sonar-ce-task/src/main/java/org/sonar/server/computation/log/package-info.java
new file mode 100644
index 00000000000..1088bac9be7
--- /dev/null
+++ b/server/sonar-ce-task/src/main/java/org/sonar/server/computation/log/package-info.java
@@ -0,0 +1,23 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.
+ */
+@ParametersAreNonnullByDefault
+package org.sonar.server.computation.log;
+
+import javax.annotation.ParametersAreNonnullByDefault;
diff --git a/server/sonar-ce-task/src/main/java/org/sonar/server/computation/package-info.java b/server/sonar-ce-task/src/main/java/org/sonar/server/computation/package-info.java
new file mode 100644
index 00000000000..2b97ccbf4fc
--- /dev/null
+++ b/server/sonar-ce-task/src/main/java/org/sonar/server/computation/package-info.java
@@ -0,0 +1,23 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.
+ */
+@ParametersAreNonnullByDefault
+package org.sonar.server.computation;
+
+import javax.annotation.ParametersAreNonnullByDefault;
diff --git a/server/sonar-ce-task/src/main/java/org/sonar/server/computation/task/container/EagerStart.java b/server/sonar-ce-task/src/main/java/org/sonar/server/computation/task/container/EagerStart.java
new file mode 100644
index 00000000000..10ab10abf01
--- /dev/null
+++ b/server/sonar-ce-task/src/main/java/org/sonar/server/computation/task/container/EagerStart.java
@@ -0,0 +1,33 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.server.computation.task.container;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Components with this annotation will be eagerly started when loaded into the {@link TaskContainerImpl}.
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.TYPE)
+public @interface EagerStart {
+}
diff --git a/server/sonar-ce-task/src/main/java/org/sonar/server/computation/task/container/TaskContainer.java b/server/sonar-ce-task/src/main/java/org/sonar/server/computation/task/container/TaskContainer.java
new file mode 100644
index 00000000000..ba9a14b28c0
--- /dev/null
+++ b/server/sonar-ce-task/src/main/java/org/sonar/server/computation/task/container/TaskContainer.java
@@ -0,0 +1,50 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.server.computation.task.container;
+
+import org.picocontainer.PicoContainer;
+import org.sonar.server.computation.CeTask;
+import org.sonar.core.platform.ComponentContainer;
+import org.sonar.core.platform.ContainerPopulator;
+
+/**
+ * The Compute Engine task container. Created for a specific parent {@link ComponentContainer} and a specific {@link CeTask}.
+ */
+public interface TaskContainer extends ContainerPopulator.Container, AutoCloseable {
+
+ ComponentContainer getParent();
+
+ /**
+ * Starts task container, starting any startable component in it.
+ */
+ void bootup();
+
+ /**
+ * Cleans up resources after process has been called and has returned.
+ */
+ @Override
+ void close();
+
+ /**
+ * Access to the underlying pico container.
+ */
+ PicoContainer getPicoContainer();
+
+}
diff --git a/server/sonar-ce-task/src/main/java/org/sonar/server/computation/task/container/TaskContainerImpl.java b/server/sonar-ce-task/src/main/java/org/sonar/server/computation/task/container/TaskContainerImpl.java
new file mode 100644
index 00000000000..3902a1b9709
--- /dev/null
+++ b/server/sonar-ce-task/src/main/java/org/sonar/server/computation/task/container/TaskContainerImpl.java
@@ -0,0 +1,93 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.server.computation.task.container;
+
+import java.util.List;
+import org.picocontainer.ComponentAdapter;
+import org.picocontainer.ComponentMonitor;
+import org.picocontainer.DefaultPicoContainer;
+import org.picocontainer.MutablePicoContainer;
+import org.picocontainer.behaviors.OptInCaching;
+import org.picocontainer.lifecycle.ReflectionLifecycleStrategy;
+import org.picocontainer.monitors.NullComponentMonitor;
+import org.sonar.api.config.PropertyDefinitions;
+import org.sonar.api.utils.log.Loggers;
+import org.sonar.core.platform.ComponentContainer;
+import org.sonar.core.platform.ContainerPopulator;
+import org.sonar.core.platform.Module;
+import org.sonar.core.platform.StopSafeReflectionLifecycleStrategy;
+
+import static java.util.Objects.requireNonNull;
+
+public class TaskContainerImpl extends ComponentContainer implements TaskContainer {
+
+ public TaskContainerImpl(ComponentContainer parent, ContainerPopulator<TaskContainer> populator) {
+ super(createContainer(requireNonNull(parent)), parent.getComponentByType(PropertyDefinitions.class));
+
+ populateContainer(requireNonNull(populator));
+ }
+
+ private void populateContainer(ContainerPopulator<TaskContainer> populator) {
+ populator.populateContainer(this);
+ populateFromModules();
+ }
+
+ private void populateFromModules() {
+ List<Module> modules = getComponentsByType(Module.class);
+ for (Module module : modules) {
+ module.configure(this);
+ }
+ }
+
+ /**
+ * Creates a PicContainer which extends the specified ComponentContainer <strong>but is not referenced in return</strong>
+ * and lazily starts its components.
+ */
+ private static MutablePicoContainer createContainer(ComponentContainer parent) {
+ ComponentMonitor componentMonitor = new NullComponentMonitor();
+ ReflectionLifecycleStrategy lifecycleStrategy = new StopSafeReflectionLifecycleStrategy(componentMonitor) {
+ @Override
+ public boolean isLazy(ComponentAdapter<?> adapter) {
+ return adapter.getComponentImplementation().getAnnotation(EagerStart.class) == null;
+ }
+ };
+
+ return new DefaultPicoContainer(new OptInCaching(), lifecycleStrategy, parent.getPicoContainer(), componentMonitor);
+ }
+
+ @Override
+ public void bootup() {
+ startComponents();
+ }
+
+ @Override
+ public String toString() {
+ return "TaskContainerImpl";
+ }
+
+ @Override
+ public void close() {
+ try {
+ stopComponents();
+ } catch (Throwable t) {
+ Loggers.get(TaskContainerImpl.class).error("Cleanup of container failed", t);
+ }
+ }
+}
diff --git a/server/sonar-ce-task/src/main/java/org/sonar/server/computation/task/container/package-info.java b/server/sonar-ce-task/src/main/java/org/sonar/server/computation/task/container/package-info.java
new file mode 100644
index 00000000000..f01956fbb38
--- /dev/null
+++ b/server/sonar-ce-task/src/main/java/org/sonar/server/computation/task/container/package-info.java
@@ -0,0 +1,23 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.
+ */
+@ParametersAreNonnullByDefault
+package org.sonar.server.computation.task.container;
+
+import javax.annotation.ParametersAreNonnullByDefault;
diff --git a/server/sonar-ce-task/src/main/java/org/sonar/server/computation/task/step/ComputationStep.java b/server/sonar-ce-task/src/main/java/org/sonar/server/computation/task/step/ComputationStep.java
new file mode 100644
index 00000000000..f8fd66fe961
--- /dev/null
+++ b/server/sonar-ce-task/src/main/java/org/sonar/server/computation/task/step/ComputationStep.java
@@ -0,0 +1,31 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.server.computation.task.step;
+
+/**
+ * A way of splitting the processing of a task into smaller items which can be executed sequencially
+ * by {@link ComputationStepExecutor}.
+ */
+public interface ComputationStep {
+
+ void execute();
+
+ String getDescription();
+}
diff --git a/server/sonar-ce-task/src/main/java/org/sonar/server/computation/task/step/ComputationStepExecutor.java b/server/sonar-ce-task/src/main/java/org/sonar/server/computation/task/step/ComputationStepExecutor.java
new file mode 100644
index 00000000000..536121f29ca
--- /dev/null
+++ b/server/sonar-ce-task/src/main/java/org/sonar/server/computation/task/step/ComputationStepExecutor.java
@@ -0,0 +1,83 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.server.computation.task.step;
+
+import javax.annotation.CheckForNull;
+import javax.annotation.Nullable;
+import org.sonar.api.utils.log.Logger;
+import org.sonar.api.utils.log.Loggers;
+import org.sonar.core.util.logs.Profiler;
+
+public final class ComputationStepExecutor {
+ private static final Logger LOGGER = Loggers.get(ComputationStepExecutor.class);
+
+ private final ComputationSteps steps;
+ @CheckForNull
+ private final Listener listener;
+
+ /**
+ * Used when no {@link ComputationStepExecutor.Listener} is available in pico
+ * container.
+ */
+ public ComputationStepExecutor(ComputationSteps steps) {
+ this(steps, null);
+ }
+
+ public ComputationStepExecutor(ComputationSteps steps, @Nullable Listener listener) {
+ this.steps = steps;
+ this.listener = listener;
+ }
+
+ public void execute() {
+ Profiler stepProfiler = Profiler.create(LOGGER);
+ boolean allStepsExecuted = false;
+ try {
+ executeSteps(stepProfiler);
+ allStepsExecuted = true;
+ } finally {
+ if (listener != null) {
+ executeListener(allStepsExecuted);
+ }
+ }
+ }
+
+ private void executeSteps(Profiler stepProfiler) {
+ for (ComputationStep step : steps.instances()) {
+ stepProfiler.start();
+ step.execute();
+ stepProfiler.stopDebug(step.getDescription());
+ }
+ }
+
+ private void executeListener(boolean allStepsExecuted) {
+ try {
+ listener.finished(allStepsExecuted);
+ } catch (Throwable e) {
+ // any Throwable throws by the listener going up the stack might hide an Exception/Error thrown by the step and
+ // cause it be swallowed. We don't wan't that => we catch Throwable
+ LOGGER.error("Execution of listener failed", e);
+ }
+ }
+
+ @FunctionalInterface
+ public interface Listener {
+ void finished(boolean allStepsExecuted);
+ }
+}
diff --git a/server/sonar-ce-task/src/main/java/org/sonar/server/computation/task/step/ComputationSteps.java b/server/sonar-ce-task/src/main/java/org/sonar/server/computation/task/step/ComputationSteps.java
new file mode 100644
index 00000000000..c35ca8f6a38
--- /dev/null
+++ b/server/sonar-ce-task/src/main/java/org/sonar/server/computation/task/step/ComputationSteps.java
@@ -0,0 +1,39 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.server.computation.task.step;
+
+import java.util.List;
+
+/**
+ * Ordered list of steps classes and instances to be executed in a Compute Engine process.
+ */
+public interface ComputationSteps {
+ /**
+ * List of all {@link ComputationStep},
+ * ordered by execution sequence.
+ */
+ List<Class<? extends ComputationStep>> orderedStepClasses();
+
+ /**
+ * List of all {@link ComputationStep},
+ * ordered by execution sequence.
+ */
+ Iterable<ComputationStep> instances();
+}
diff --git a/server/sonar-ce-task/src/main/java/org/sonar/server/computation/task/step/ExecuteStatelessInitExtensionsStep.java b/server/sonar-ce-task/src/main/java/org/sonar/server/computation/task/step/ExecuteStatelessInitExtensionsStep.java
new file mode 100644
index 00000000000..b587083d88b
--- /dev/null
+++ b/server/sonar-ce-task/src/main/java/org/sonar/server/computation/task/step/ExecuteStatelessInitExtensionsStep.java
@@ -0,0 +1,56 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.server.computation.task.step;
+
+import org.sonar.api.ce.ComputeEngineSide;
+
+/**
+ * Execute {@link StatelessInitExtension} instances in no specific order.
+ * If an extension fails (throws an exception), consecutive extensions
+ * won't be called.
+ */
+@ComputeEngineSide
+public class ExecuteStatelessInitExtensionsStep implements ComputationStep {
+
+ private final StatelessInitExtension[] extensions;
+
+ public ExecuteStatelessInitExtensionsStep(StatelessInitExtension[] extensions) {
+ this.extensions = extensions;
+ }
+
+ /**
+ * Used when zero {@link StatelessInitExtension} are registered into container.
+ */
+ public ExecuteStatelessInitExtensionsStep() {
+ this(new StatelessInitExtension[0]);
+ }
+
+ @Override
+ public void execute() {
+ for (StatelessInitExtension extension : extensions) {
+ extension.onInit();
+ }
+ }
+
+ @Override
+ public String getDescription() {
+ return "Initialize";
+ }
+}
diff --git a/server/sonar-ce-task/src/main/java/org/sonar/server/computation/task/step/StatelessInitExtension.java b/server/sonar-ce-task/src/main/java/org/sonar/server/computation/task/step/StatelessInitExtension.java
new file mode 100644
index 00000000000..aedc81e8347
--- /dev/null
+++ b/server/sonar-ce-task/src/main/java/org/sonar/server/computation/task/step/StatelessInitExtension.java
@@ -0,0 +1,41 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.server.computation.task.step;
+
+import org.sonar.api.ExtensionPoint;
+import org.sonar.api.ce.ComputeEngineSide;
+
+/**
+ * Extension point that is called during processing of a task
+ * by {@link ExecuteStatelessInitExtensionsStep}.
+ * It is stateless, the same instance is reused for all tasks.
+ * As a consequence Compute Engine task components can't be injected
+ * as dependencies.
+ */
+@ComputeEngineSide
+@ExtensionPoint
+public interface StatelessInitExtension {
+
+ /**
+ * This method can make the task fail by throwing a {@link RuntimeException}
+ */
+ void onInit();
+
+}
diff --git a/server/sonar-ce-task/src/main/java/org/sonar/server/computation/task/step/TypedException.java b/server/sonar-ce-task/src/main/java/org/sonar/server/computation/task/step/TypedException.java
new file mode 100644
index 00000000000..b600f90f7e3
--- /dev/null
+++ b/server/sonar-ce-task/src/main/java/org/sonar/server/computation/task/step/TypedException.java
@@ -0,0 +1,33 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.server.computation.task.step;
+
+/**
+ * This interface is implemented by the exceptions
+ * that provide a type of error when failing
+ * a Compute Engine task.
+ * The error type is persisted and available in
+ * the tasks returned by the web services api/ce.
+ */
+public interface TypedException {
+
+ String getType();
+
+}
diff --git a/server/sonar-ce-task/src/main/java/org/sonar/server/computation/task/step/package-info.java b/server/sonar-ce-task/src/main/java/org/sonar/server/computation/task/step/package-info.java
new file mode 100644
index 00000000000..5868b7cccc4
--- /dev/null
+++ b/server/sonar-ce-task/src/main/java/org/sonar/server/computation/task/step/package-info.java
@@ -0,0 +1,23 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.
+ */
+@ParametersAreNonnullByDefault
+package org.sonar.server.computation.task.step;
+
+import javax.annotation.ParametersAreNonnullByDefault;
diff --git a/server/sonar-ce-task/src/main/java/org/sonar/server/computation/taskprocessor/MutableTaskResultHolder.java b/server/sonar-ce-task/src/main/java/org/sonar/server/computation/taskprocessor/MutableTaskResultHolder.java
new file mode 100644
index 00000000000..5e4409b1b73
--- /dev/null
+++ b/server/sonar-ce-task/src/main/java/org/sonar/server/computation/taskprocessor/MutableTaskResultHolder.java
@@ -0,0 +1,30 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.server.computation.taskprocessor;
+
+import org.sonar.server.computation.CeTaskResult;
+
+public interface MutableTaskResultHolder extends TaskResultHolder {
+ /**
+ * @throws NullPointerException if {@code taskResult} is {@code null}
+ * @throws IllegalStateException if a {@link CeTaskResult} has already been set in the holder
+ */
+ void setResult(CeTaskResult taskResult);
+}
diff --git a/server/sonar-ce-task/src/main/java/org/sonar/server/computation/taskprocessor/MutableTaskResultHolderImpl.java b/server/sonar-ce-task/src/main/java/org/sonar/server/computation/taskprocessor/MutableTaskResultHolderImpl.java
new file mode 100644
index 00000000000..6fe1e53913e
--- /dev/null
+++ b/server/sonar-ce-task/src/main/java/org/sonar/server/computation/taskprocessor/MutableTaskResultHolderImpl.java
@@ -0,0 +1,44 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.server.computation.taskprocessor;
+
+import javax.annotation.CheckForNull;
+import org.sonar.server.computation.CeTaskResult;
+
+import static com.google.common.base.Preconditions.checkState;
+import static java.util.Objects.requireNonNull;
+
+public class MutableTaskResultHolderImpl implements MutableTaskResultHolder {
+ @CheckForNull
+ private CeTaskResult result;
+
+ @Override
+ public CeTaskResult getResult() {
+ checkState(this.result != null, "No CeTaskResult has been set in the holder");
+ return this.result;
+ }
+
+ @Override
+ public void setResult(CeTaskResult taskResult) {
+ requireNonNull(taskResult, "taskResult can not be null");
+ checkState(this.result == null, "CeTaskResult has already been set in the holder");
+ this.result = taskResult;
+ }
+}
diff --git a/server/sonar-ce-task/src/main/java/org/sonar/server/computation/taskprocessor/TaskResultHolder.java b/server/sonar-ce-task/src/main/java/org/sonar/server/computation/taskprocessor/TaskResultHolder.java
new file mode 100644
index 00000000000..b4c3e978150
--- /dev/null
+++ b/server/sonar-ce-task/src/main/java/org/sonar/server/computation/taskprocessor/TaskResultHolder.java
@@ -0,0 +1,29 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.server.computation.taskprocessor;
+
+import org.sonar.server.computation.CeTaskResult;
+
+public interface TaskResultHolder {
+ /**
+ * @throws IllegalStateException if holder holds no CeTaskResult
+ */
+ CeTaskResult getResult();
+}
diff --git a/server/sonar-ce-task/src/main/java/org/sonar/server/computation/util/InitializedProperty.java b/server/sonar-ce-task/src/main/java/org/sonar/server/computation/util/InitializedProperty.java
new file mode 100644
index 00000000000..01170eb9fde
--- /dev/null
+++ b/server/sonar-ce-task/src/main/java/org/sonar/server/computation/util/InitializedProperty.java
@@ -0,0 +1,43 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.server.computation.util;
+
+import javax.annotation.CheckForNull;
+import javax.annotation.Nullable;
+
+public class InitializedProperty<E> {
+ private E property;
+ private boolean initialized = false;
+
+ public InitializedProperty<E> setProperty(@Nullable E property) {
+ this.property = property;
+ this.initialized = true;
+ return this;
+ }
+
+ @CheckForNull
+ public E getProperty() {
+ return property;
+ }
+
+ public boolean isInitialized() {
+ return initialized;
+ }
+}
diff --git a/server/sonar-ce-task/src/main/java/org/sonar/server/computation/util/package-info.java b/server/sonar-ce-task/src/main/java/org/sonar/server/computation/util/package-info.java
new file mode 100644
index 00000000000..415bc7b76f9
--- /dev/null
+++ b/server/sonar-ce-task/src/main/java/org/sonar/server/computation/util/package-info.java
@@ -0,0 +1,24 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.
+ */
+@ParametersAreNonnullByDefault
+package org.sonar.server.computation.util;
+
+import javax.annotation.ParametersAreNonnullByDefault;
+
diff --git a/server/sonar-ce-task/src/test/java/org/sonar/server/computation/CeTaskTest.java b/server/sonar-ce-task/src/test/java/org/sonar/server/computation/CeTaskTest.java
new file mode 100644
index 00000000000..b42261f5eb7
--- /dev/null
+++ b/server/sonar-ce-task/src/test/java/org/sonar/server/computation/CeTaskTest.java
@@ -0,0 +1,148 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.server.computation;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class CeTaskTest {
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+
+ private CeTask.Builder underTest = new CeTask.Builder();
+
+ @Test
+ public void build_fails_with_NPE_if_organizationUuid_is_null() {
+ expectedException.expect(NullPointerException.class);
+ expectedException.expectMessage("organizationUuid can't be null nor empty");
+
+ underTest.build();
+ }
+
+ @Test
+ public void build_fails_with_NPE_if_organizationUuid_is_empty() {
+ underTest.setOrganizationUuid("");
+
+ expectedException.expect(NullPointerException.class);
+ expectedException.expectMessage("organizationUuid can't be null nor empty");
+
+ underTest.build();
+ }
+
+ @Test
+ public void build_fails_with_NPE_if_uid_is_null() {
+ underTest.setOrganizationUuid("org1");
+
+ expectedException.expect(NullPointerException.class);
+ expectedException.expectMessage("uuid can't be null nor empty");
+
+ underTest.build();
+ }
+
+ @Test
+ public void build_fails_with_NPE_if_uuid_is_empty() {
+ underTest.setOrganizationUuid("org1").setUuid("");
+
+ expectedException.expect(NullPointerException.class);
+ expectedException.expectMessage("uuid can't be null nor empty");
+
+ underTest.build();
+ }
+
+ @Test
+ public void build_fails_with_NPE_if_type_is_null() {
+ underTest.setOrganizationUuid("org1").setUuid("uuid");
+
+ expectedException.expect(NullPointerException.class);
+ expectedException.expectMessage("type can't be null nor empty");
+
+ underTest.build();
+ }
+
+ @Test
+ public void build_fails_with_NPE_if_type_is_empty() {
+ underTest.setOrganizationUuid("org1").setUuid("uuid").setType("");
+
+ expectedException.expect(NullPointerException.class);
+ expectedException.expectMessage("type can't be null nor empty");
+
+ underTest.build();
+ }
+
+ @Test
+ public void verify_getters() {
+ underTest.setOrganizationUuid("org1");
+ underTest.setType("TYPE_1");
+ underTest.setUuid("UUID_1");
+ underTest.setSubmitterUuid("LOGIN_1");
+ underTest.setComponentKey("COMPONENT_KEY_1");
+ underTest.setComponentUuid("COMPONENT_UUID_1");
+ underTest.setComponentName("The component");
+
+ CeTask task = underTest.build();
+
+ assertThat(task.getOrganizationUuid()).isEqualTo("org1");
+ assertThat(task.getUuid()).isEqualTo("UUID_1");
+ assertThat(task.getType()).isEqualTo("TYPE_1");
+ assertThat(task.getSubmitterUuid()).isEqualTo("LOGIN_1");
+ assertThat(task.getComponentKey()).isEqualTo("COMPONENT_KEY_1");
+ assertThat(task.getComponentUuid()).isEqualTo("COMPONENT_UUID_1");
+ assertThat(task.getComponentName()).isEqualTo("The component");
+ }
+
+ @Test
+ public void empty_in_component_properties_is_considered_as_null() {
+ CeTask ceTask = underTest.setOrganizationUuid("org1").setUuid("uuid").setType("type")
+ .setComponentKey("")
+ .setComponentName("")
+ .setComponentUuid("")
+ .build();
+
+ assertThat(ceTask.getComponentKey()).isNull();
+ assertThat(ceTask.getComponentName()).isNull();
+ assertThat(ceTask.getComponentUuid()).isNull();
+ }
+
+ @Test
+ public void empty_in_submitterLogin_is_considered_as_null() {
+ CeTask ceTask = underTest.setOrganizationUuid("org1").setUuid("uuid").setType("type")
+ .setSubmitterUuid("")
+ .build();
+
+ assertThat(ceTask.getSubmitterUuid()).isNull();
+ }
+
+ @Test
+ public void equals_and_hashCode_on_uuid() {
+ underTest.setOrganizationUuid("org1").setType("TYPE_1").setUuid("UUID_1");
+ CeTask task1 = underTest.build();
+ CeTask task1bis = underTest.build();
+ CeTask task2 = new CeTask.Builder().setOrganizationUuid("org1").setType("TYPE_1").setUuid("UUID_2").build();
+
+ assertThat(task1.equals(task1)).isTrue();
+ assertThat(task1.equals(task1bis)).isTrue();
+ assertThat(task1.equals(task2)).isFalse();
+ assertThat(task1.hashCode()).isEqualTo(task1.hashCode());
+ assertThat(task1.hashCode()).isEqualTo(task1bis.hashCode());
+ }
+}
diff --git a/server/sonar-ce-task/src/test/java/org/sonar/server/computation/log/CeTaskLoggingTest.java b/server/sonar-ce-task/src/test/java/org/sonar/server/computation/log/CeTaskLoggingTest.java
new file mode 100644
index 00000000000..5193eb1efd8
--- /dev/null
+++ b/server/sonar-ce-task/src/test/java/org/sonar/server/computation/log/CeTaskLoggingTest.java
@@ -0,0 +1,78 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.server.computation.log;
+
+import ch.qos.logback.core.joran.spi.JoranException;
+import org.junit.After;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.mockito.Mockito;
+import org.slf4j.MDC;
+import org.sonar.process.logging.LogbackHelper;
+import org.sonar.server.computation.CeTask;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.when;
+import static org.sonar.server.computation.log.CeTaskLogging.MDC_CE_TASK_UUID;
+
+public class CeTaskLoggingTest {
+
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+
+ private LogbackHelper helper = new LogbackHelper();
+ private CeTaskLogging underTest = new CeTaskLogging();
+
+ @After
+ public void resetLogback() throws JoranException {
+ helper.resetFromXml("/logback-test.xml");
+ }
+
+ @After
+ public void cleanMDC() {
+ MDC.clear();
+ }
+
+ @Test
+ public void initForTask_stores_task_uuid_in_MDC() {
+ String uuid = "ce_task_uuid";
+
+ underTest.initForTask(createCeTask(uuid));
+
+ assertThat(MDC.get(MDC_CE_TASK_UUID)).isEqualTo(uuid);
+ }
+
+ private CeTask createCeTask(String uuid) {
+ CeTask ceTask = Mockito.mock(CeTask.class);
+ when(ceTask.getUuid()).thenReturn(uuid);
+ return ceTask;
+ }
+
+ @Test
+ public void clearForTask_removes_task_uuid_from_MDC() {
+ MDC.put(MDC_CE_TASK_UUID, "some_value");
+
+ underTest.clearForTask();
+
+ assertThat(MDC.get(MDC_CE_TASK_UUID)).isNull();
+ }
+
+}
diff --git a/server/sonar-ce-task/src/test/java/org/sonar/server/computation/task/ChangeLogLevel.java b/server/sonar-ce-task/src/test/java/org/sonar/server/computation/task/ChangeLogLevel.java
new file mode 100644
index 00000000000..3260b7263cd
--- /dev/null
+++ b/server/sonar-ce-task/src/test/java/org/sonar/server/computation/task/ChangeLogLevel.java
@@ -0,0 +1,40 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.server.computation.task;
+
+import org.sonar.api.utils.log.Logger;
+import org.sonar.api.utils.log.LoggerLevel;
+import org.sonar.api.utils.log.Loggers;
+
+public final class ChangeLogLevel implements AutoCloseable {
+ private final Logger logger;
+ private final LoggerLevel previous;
+
+ public ChangeLogLevel(Class<?> clazz, LoggerLevel newLevel) {
+ this.logger = Loggers.get(clazz);
+ this.previous = logger.getLevel();
+ logger.setLevel(newLevel);
+ }
+
+ @Override
+ public void close() {
+ logger.setLevel(previous);
+ }
+}
diff --git a/server/sonar-ce-task/src/test/java/org/sonar/server/computation/task/container/TaskContainerImplTest.java b/server/sonar-ce-task/src/test/java/org/sonar/server/computation/task/container/TaskContainerImplTest.java
new file mode 100644
index 00000000000..debaac8a916
--- /dev/null
+++ b/server/sonar-ce-task/src/test/java/org/sonar/server/computation/task/container/TaskContainerImplTest.java
@@ -0,0 +1,114 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.server.computation.task.container;
+
+import org.junit.Test;
+import org.picocontainer.Startable;
+import org.sonar.core.platform.ComponentContainer;
+import org.sonar.core.platform.ContainerPopulator;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+public class TaskContainerImplTest {
+ private ComponentContainer parent = new ComponentContainer();
+ private ContainerPopulator populator = mock(ContainerPopulator.class);
+
+ @Test(expected = NullPointerException.class)
+ public void constructor_fails_fast_on_null_container() {
+ new TaskContainerImpl(null, mock(ContainerPopulator.class));
+ }
+
+ @Test(expected = NullPointerException.class)
+ public void constructor_fails_fast_on_null_item() {
+ new TaskContainerImpl(new ComponentContainer(), null);
+ }
+
+ @Test
+ public void calls_method_populateContainer_of_passed_in_populator() {
+ TaskContainerImpl ceContainer = new TaskContainerImpl(parent, populator);
+
+ verify(populator).populateContainer(ceContainer);
+ }
+
+ @Test
+ public void ce_container_is_not_child_of_specified_container() {
+ TaskContainerImpl ceContainer = new TaskContainerImpl(parent, populator);
+
+ assertThat(parent.getChildren()).isEmpty();
+ verify(populator).populateContainer(ceContainer);
+ }
+
+ @Test
+ public void bootup_starts_components_lazily_unless_they_are_annotated_with_EagerStart() {
+ final DefaultStartable defaultStartable = new DefaultStartable();
+ final EagerStartable eagerStartable = new EagerStartable();
+ TaskContainerImpl ceContainer = new TaskContainerImpl(parent, container -> {
+ container.add(defaultStartable);
+ container.add(eagerStartable);
+ });
+ ceContainer.bootup();
+
+ assertThat(defaultStartable.startCalls).isEqualTo(0);
+ assertThat(defaultStartable.stopCalls).isEqualTo(0);
+ assertThat(eagerStartable.startCalls).isEqualTo(1);
+ assertThat(eagerStartable.stopCalls).isEqualTo(0);
+ }
+
+ @Test
+ public void close_stops_started_components() {
+ final DefaultStartable defaultStartable = new DefaultStartable();
+ final EagerStartable eagerStartable = new EagerStartable();
+ TaskContainerImpl ceContainer = new TaskContainerImpl(parent, container -> {
+ container.add(defaultStartable);
+ container.add(eagerStartable);
+ });
+ ceContainer.bootup();
+
+ ceContainer.close();
+
+ assertThat(defaultStartable.startCalls).isEqualTo(0);
+ assertThat(defaultStartable.stopCalls).isEqualTo(0);
+ assertThat(eagerStartable.startCalls).isEqualTo(1);
+ assertThat(eagerStartable.stopCalls).isEqualTo(1);
+ }
+
+ public static class DefaultStartable implements Startable {
+ protected int startCalls = 0;
+ protected int stopCalls = 0;
+
+ @Override
+ public void start() {
+ startCalls++;
+ }
+
+ @Override
+ public void stop() {
+ stopCalls++;
+ }
+ }
+
+ @EagerStart
+ public static class EagerStartable extends DefaultStartable {
+ }
+
+
+}
diff --git a/server/sonar-ce-task/src/test/java/org/sonar/server/computation/task/step/ComputationStepExecutorTest.java b/server/sonar-ce-task/src/test/java/org/sonar/server/computation/task/step/ComputationStepExecutorTest.java
new file mode 100644
index 00000000000..19050a279bf
--- /dev/null
+++ b/server/sonar-ce-task/src/test/java/org/sonar/server/computation/task/step/ComputationStepExecutorTest.java
@@ -0,0 +1,166 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.server.computation.task.step;
+
+import java.util.Arrays;
+import java.util.List;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.mockito.InOrder;
+import org.sonar.api.utils.log.LogTester;
+import org.sonar.api.utils.log.LoggerLevel;
+import org.sonar.server.computation.task.ChangeLogLevel;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.fail;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+
+public class ComputationStepExecutorTest {
+ @Rule
+ public LogTester logTester = new LogTester();
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+
+ private final ComputationStepExecutor.Listener listener = mock(ComputationStepExecutor.Listener.class);
+ private final ComputationStep computationStep1 = mockComputationStep("step1");
+ private final ComputationStep computationStep2 = mockComputationStep("step2");
+ private final ComputationStep computationStep3 = mockComputationStep("step3");
+
+ @Test
+ public void execute_call_execute_on_each_ComputationStep_in_order_returned_by_instances_method() {
+ new ComputationStepExecutor(mockComputationSteps(computationStep1, computationStep2, computationStep3))
+ .execute();
+
+ InOrder inOrder = inOrder(computationStep1, computationStep2, computationStep3);
+ inOrder.verify(computationStep1).execute();
+ inOrder.verify(computationStep1).getDescription();
+ inOrder.verify(computationStep2).execute();
+ inOrder.verify(computationStep2).getDescription();
+ inOrder.verify(computationStep3).execute();
+ inOrder.verify(computationStep3).getDescription();
+ inOrder.verifyNoMoreInteractions();
+ }
+
+ @Test
+ public void execute_let_exception_thrown_by_ComputationStep_go_up_as_is() {
+ String message = "Exception should go up";
+
+ ComputationStep computationStep = mockComputationStep("step1");
+ doThrow(new RuntimeException(message))
+ .when(computationStep)
+ .execute();
+
+ ComputationStepExecutor computationStepExecutor = new ComputationStepExecutor(mockComputationSteps(computationStep));
+
+ expectedException.expect(RuntimeException.class);
+ expectedException.expectMessage(message);
+
+ computationStepExecutor.execute();
+ }
+
+ @Test
+ public void execute_does_not_log_end_timing_for_each_ComputationStep_called_when_level_is_INFO() {
+ List<String> infoLogs = execute_logs_end_timing_for_each_ComputationStep_called_when_(LoggerLevel.INFO);
+ assertThat(infoLogs).isEmpty();
+ }
+
+ @Test
+ public void execute_logs_end_timing_for_each_ComputationStep_called_when_level_is_DEBUG() {
+ List<String> infoLogs = execute_logs_end_timing_for_each_ComputationStep_called_when_(LoggerLevel.DEBUG);
+ assertThat(infoLogs).hasSize(2);
+ assertThat(infoLogs.get(0)).contains("step1 | time=");
+ assertThat(infoLogs.get(1)).contains("step2 | time=");
+ }
+
+ @Test
+ public void execute_logs_end_timing_for_each_ComputationStep_called_when_level_is_TRACE() {
+ List<String> infoLogs = execute_logs_end_timing_for_each_ComputationStep_called_when_(LoggerLevel.TRACE);
+ assertThat(infoLogs).hasSize(2);
+ assertThat(infoLogs.get(0)).contains("step1 | time=");
+ assertThat(infoLogs.get(1)).contains("step2 | time=");
+ }
+
+ private List<String> execute_logs_end_timing_for_each_ComputationStep_called_when_(LoggerLevel level) {
+ try (ChangeLogLevel executor = new ChangeLogLevel(ComputationStepExecutor.class, level);
+ ChangeLogLevel step1 = new ChangeLogLevel(computationStep1.getClass(), level);
+ ChangeLogLevel step2 = new ChangeLogLevel(computationStep2.getClass(), level)) {
+ new ComputationStepExecutor(mockComputationSteps(computationStep1, computationStep2))
+ .execute();
+
+ return logTester.logs(LoggerLevel.DEBUG);
+ }
+ }
+
+ @Test
+ public void execute_calls_listener_finished_method_with_all_step_runs() {
+ new ComputationStepExecutor(mockComputationSteps(computationStep1, computationStep2), listener)
+ .execute();
+
+ verify(listener).finished(true);
+ verifyNoMoreInteractions(listener);
+ }
+
+ @Test
+ public void execute_calls_listener_finished_method_even_if_a_step_throws_an_exception() {
+ RuntimeException toBeThrown = new RuntimeException("simulating failing execute Step method");
+ doThrow(toBeThrown)
+ .when(computationStep1)
+ .execute();
+
+ try {
+ new ComputationStepExecutor(mockComputationSteps(computationStep1, computationStep2), listener)
+ .execute();
+ fail("exception toBeThrown should have been raised");
+ } catch (RuntimeException e) {
+ assertThat(e).isSameAs(toBeThrown);
+ verify(listener).finished(false);
+ verifyNoMoreInteractions(listener);
+ }
+ }
+
+ @Test
+ public void execute_does_not_fail_if_listener_throws_Throwable() {
+ ComputationStepExecutor.Listener listener = mock(ComputationStepExecutor.Listener.class);
+ doThrow(new Error("Facking error thrown by Listener"))
+ .when(listener)
+ .finished(anyBoolean());
+
+ new ComputationStepExecutor(mockComputationSteps(computationStep1), listener).execute();
+ }
+
+ private static ComputationSteps mockComputationSteps(ComputationStep... computationSteps) {
+ ComputationSteps steps = mock(ComputationSteps.class);
+ when(steps.instances()).thenReturn(Arrays.asList(computationSteps));
+ return steps;
+ }
+
+ private static ComputationStep mockComputationStep(String desc) {
+ ComputationStep mock = mock(ComputationStep.class);
+ when(mock.getDescription()).thenReturn(desc);
+ return mock;
+ }
+}
diff --git a/server/sonar-ce-task/src/test/java/org/sonar/server/computation/task/step/ExecuteStatelessInitExtensionsStepTest.java b/server/sonar-ce-task/src/test/java/org/sonar/server/computation/task/step/ExecuteStatelessInitExtensionsStepTest.java
new file mode 100644
index 00000000000..fdf4049bf35
--- /dev/null
+++ b/server/sonar-ce-task/src/test/java/org/sonar/server/computation/task/step/ExecuteStatelessInitExtensionsStepTest.java
@@ -0,0 +1,89 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.server.computation.task.step;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.mockito.InOrder;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.Assert.fail;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+
+public class ExecuteStatelessInitExtensionsStepTest {
+
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+
+ @Test
+ public void test_getDescription() {
+ ExecuteStatelessInitExtensionsStep underTest = new ExecuteStatelessInitExtensionsStep();
+
+ assertThat(underTest.getDescription()).isEqualTo("Initialize");
+ }
+
+ @Test
+ public void do_nothing_if_no_extensions() {
+ ExecuteStatelessInitExtensionsStep underTest = new ExecuteStatelessInitExtensionsStep();
+
+ // no failure
+ underTest.execute();
+ }
+
+ @Test
+ public void execute_extensions() {
+ StatelessInitExtension ext1 = mock(StatelessInitExtension.class);
+ StatelessInitExtension ext2 = mock(StatelessInitExtension.class);
+
+ ExecuteStatelessInitExtensionsStep underTest = new ExecuteStatelessInitExtensionsStep(
+ new StatelessInitExtension[] {ext1, ext2});
+ underTest.execute();
+
+ InOrder inOrder = inOrder(ext1, ext2);
+ inOrder.verify(ext1).onInit();
+ inOrder.verify(ext2).onInit();
+ }
+
+ @Test
+ public void fail_if_an_extension_throws_an_exception() {
+ StatelessInitExtension ext1 = mock(StatelessInitExtension.class);
+ StatelessInitExtension ext2 = mock(StatelessInitExtension.class);
+ doThrow(new IllegalStateException("BOOM")).when(ext2).onInit();
+ StatelessInitExtension ext3 = mock(StatelessInitExtension.class);
+
+ ExecuteStatelessInitExtensionsStep underTest = new ExecuteStatelessInitExtensionsStep(
+ new StatelessInitExtension[] {ext1, ext2, ext3});
+
+ try {
+ underTest.execute();
+ fail();
+ } catch (IllegalStateException e) {
+ assertThat(e).hasMessage("BOOM");
+ verify(ext1).onInit();
+ verify(ext3, never()).onInit();
+ }
+ }
+
+}
diff --git a/server/sonar-ce-task/src/test/java/org/sonar/server/computation/task/step/StepsExplorer.java b/server/sonar-ce-task/src/test/java/org/sonar/server/computation/task/step/StepsExplorer.java
new file mode 100644
index 00000000000..1f836dee41c
--- /dev/null
+++ b/server/sonar-ce-task/src/test/java/org/sonar/server/computation/task/step/StepsExplorer.java
@@ -0,0 +1,68 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.server.computation.task.step;
+
+import com.google.common.base.Function;
+import com.google.common.base.Predicate;
+import java.lang.reflect.Modifier;
+import java.util.Set;
+import javax.annotation.Nonnull;
+import org.reflections.Reflections;
+
+import static com.google.common.base.Predicates.notNull;
+import static com.google.common.collect.FluentIterable.from;
+
+public class StepsExplorer {
+ /**
+ * Compute set of canonical names of classes implementing ComputationStep in the specified package using reflection.
+ */
+ public static Set<String> retrieveStepPackageStepsCanonicalNames(String packageName) {
+ Reflections reflections = new Reflections(packageName);
+
+ return from(reflections.getSubTypesOf(ComputationStep.class))
+ .filter(NotAbstractClass.INSTANCE)
+ .transform(ClassToCanonicalName.INSTANCE)
+ // anonymous classes do not have canonical names
+ .filter(notNull())
+ .toSet();
+ }
+
+ private enum NotAbstractClass implements Predicate<Class<? extends ComputationStep>> {
+ INSTANCE;
+
+ @Override
+ public boolean apply(Class<? extends ComputationStep> input) {
+ return !Modifier.isAbstract(input.getModifiers());
+ }
+ }
+
+ public static Function<Class<?>, String> toCanonicalName() {
+ return ClassToCanonicalName.INSTANCE;
+ }
+
+ private enum ClassToCanonicalName implements Function<Class<?>, String> {
+ INSTANCE;
+
+ @Override
+ public String apply(@Nonnull Class<?> input) {
+ return input.getCanonicalName();
+ }
+ }
+}
diff --git a/server/sonar-ce-task/src/test/java/org/sonar/server/computation/taskprocessor/MutableTaskResultHolderImplTest.java b/server/sonar-ce-task/src/test/java/org/sonar/server/computation/taskprocessor/MutableTaskResultHolderImplTest.java
new file mode 100644
index 00000000000..f81de440ad6
--- /dev/null
+++ b/server/sonar-ce-task/src/test/java/org/sonar/server/computation/taskprocessor/MutableTaskResultHolderImplTest.java
@@ -0,0 +1,70 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.server.computation.taskprocessor;
+
+import org.assertj.core.api.Assertions;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.sonar.server.computation.CeTaskResult;
+
+import static org.mockito.Mockito.mock;
+
+public class MutableTaskResultHolderImplTest {
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+
+ private MutableTaskResultHolder underTest = new MutableTaskResultHolderImpl();
+
+ @Test
+ public void getResult_throws_ISE_if_no_CeTaskResult_is_set() {
+ expectedException.expect(IllegalStateException.class);
+ expectedException.expectMessage("No CeTaskResult has been set in the holder");
+
+ underTest.getResult();
+ }
+
+ @Test
+ public void getResult_returns_object_set_with_setResult() {
+ CeTaskResult taskResult = mock(CeTaskResult.class);
+
+ underTest.setResult(taskResult);
+
+ Assertions.assertThat(underTest.getResult()).isSameAs(taskResult);
+ }
+
+ @Test
+ public void setResult_throws_NPE_if_CeTaskResult_argument_is_null() {
+ expectedException.expect(NullPointerException.class);
+ expectedException.expectMessage("taskResult can not be null");
+
+ underTest.setResult(null);
+ }
+
+ @Test
+ public void setResult_throws_ISE_if_called_twice() {
+ underTest.setResult(mock(CeTaskResult.class));
+
+ expectedException.expect(IllegalStateException.class);
+ expectedException.expectMessage("CeTaskResult has already been set in the holder");
+
+ underTest.setResult(mock(CeTaskResult.class));
+ }
+}
diff --git a/server/sonar-ce-task/src/test/resources/logback-test.xml b/server/sonar-ce-task/src/test/resources/logback-test.xml
new file mode 100644
index 00000000000..3e34b0f9fc8
--- /dev/null
+++ b/server/sonar-ce-task/src/test/resources/logback-test.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<configuration debug="false">
+ <contextListener class="ch.qos.logback.classic.jul.LevelChangePropagator"/>
+
+ <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
+ <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
+ <pattern>
+ %d{yyyy.MM.dd HH:mm:ss} %-5level %msg%n
+ </pattern>
+ </encoder>
+ </appender>
+
+ <root>
+ <level value="INFO"/>
+ <appender-ref ref="CONSOLE"/>
+ </root>
+
+ <logger name="ch.qos.logback">
+ <level value="WARN"/>
+ </logger>
+
+ <logger name="okhttp3.mockwebserver">
+ <level value="WARN"/>
+ </logger>
+
+</configuration>