From bfb49e25b626c15b9fcd9ef144222442da1df391 Mon Sep 17 00:00:00 2001 From: Simon Brandhof Date: Wed, 27 Sep 2017 22:14:52 +0200 Subject: [PATCH] New extension point executed during initialization of a CE task --- .../step/ReportComputationSteps.java | 2 + .../ExecuteStatelessInitExtensionsStep.java | 56 ++++++++++++ .../task/step/StatelessInitExtension.java | 41 +++++++++ ...xecuteStatelessInitExtensionsStepTest.java | 89 +++++++++++++++++++ 4 files changed, 188 insertions(+) create mode 100644 server/sonar-server/src/main/java/org/sonar/server/computation/task/step/ExecuteStatelessInitExtensionsStep.java create mode 100644 server/sonar-server/src/main/java/org/sonar/server/computation/task/step/StatelessInitExtension.java create mode 100644 server/sonar-server/src/test/java/org/sonar/server/computation/task/step/ExecuteStatelessInitExtensionsStepTest.java diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/step/ReportComputationSteps.java b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/step/ReportComputationSteps.java index 8c150408609..d60f9441d46 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/step/ReportComputationSteps.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/step/ReportComputationSteps.java @@ -24,6 +24,7 @@ import java.util.List; import org.sonar.server.computation.task.container.TaskContainer; import org.sonar.server.computation.task.projectanalysis.filemove.FileMoveDetectionStep; import org.sonar.server.computation.task.step.ComputationStep; +import org.sonar.server.computation.task.step.ExecuteStatelessInitExtensionsStep; /** * Ordered list of steps classes and instances to be executed for batch processing @@ -37,6 +38,7 @@ public class ReportComputationSteps extends AbstractComputationSteps { // Builds Component tree LoadReportAnalysisMetadataHolderStep.class, + ExecuteStatelessInitExtensionsStep.class, VerifyBillingStep.class, BuildComponentTreeStep.class, ValidateProjectStep.class, diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/task/step/ExecuteStatelessInitExtensionsStep.java b/server/sonar-server/src/main/java/org/sonar/server/computation/task/step/ExecuteStatelessInitExtensionsStep.java new file mode 100644 index 00000000000..656ba18576a --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/task/step/ExecuteStatelessInitExtensionsStep.java @@ -0,0 +1,56 @@ +/* + * SonarQube + * Copyright (C) 2009-2017 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-server/src/main/java/org/sonar/server/computation/task/step/StatelessInitExtension.java b/server/sonar-server/src/main/java/org/sonar/server/computation/task/step/StatelessInitExtension.java new file mode 100644 index 00000000000..00929c0b440 --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/task/step/StatelessInitExtension.java @@ -0,0 +1,41 @@ +/* + * SonarQube + * Copyright (C) 2009-2017 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-server/src/test/java/org/sonar/server/computation/task/step/ExecuteStatelessInitExtensionsStepTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/task/step/ExecuteStatelessInitExtensionsStepTest.java new file mode 100644 index 00000000000..f1ea07c637a --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/task/step/ExecuteStatelessInitExtensionsStepTest.java @@ -0,0 +1,89 @@ +/* + * SonarQube + * Copyright (C) 2009-2017 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(); + } + } + +} -- 2.39.5